四川省红鱼洞水库建设管理网站,无锡软件开发培训机构,装修设计网站免费,门户网站如何做谷歌seo基于hello.cpp对C的运行进行一个初步认识#xff0c;并介绍国外C大佬Cherno常用的项目结构和调试Tips C是如何工作的 C工作流程1.实用工程#xff08;project#xff09;结构#xff08;1#xff09;Microsoft Visual Studio2022新建项目后#xff0c;自动生成的原始文件…基于hello.cpp对C的运行进行一个初步认识并介绍国外C大佬Cherno常用的项目结构和调试Tips C是如何工作的 C工作流程1.实用工程project结构1Microsoft Visual Studio2022新建项目后自动生成的原始文件和文件夹2写一个简单的main.cpp编译运行后自动创建文件加和生成中间文件obj和可执行文件exe3从默认工程/项目project结构到实用工程/项目结构 2.调试debug3.1整体代码3.2预处理3.3.编译3.4链接 4.C编译器4.1定义与作用4.2示例4.3编译器的优化代码例子 ——“常数折叠” 5.C链接器6.头文件6.1定义与作用6.2示例4.3在实现函数的 functions.cpp 文件中包含 #include functions.h 的目的 C工作流程
1.实用工程project结构
1Microsoft Visual Studio2022新建项目后自动生成的原始文件和文件夹 2写一个简单的main.cpp编译运行后自动创建文件加和生成中间文件obj和可执行文件exe 3从默认工程/项目project结构到实用工程/项目结构
实用如下宏修改输出目录和中间目录
$(SolutionDir)bin\$(Platform)\$(Configuration)\
$(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\-$(SolutionDir)代表当前解决方案不同的的解决方案会进行不同的具体的实例化$(Platform)以此类推2. 切换为“显示所有文件”的视图并且新建一个src文件夹用于收录.cpp文件
3.写一个简单的cpp文件并且查看编译运行后的目录结构
这样的目录结构是不是更清晰喜人呢
2.调试debug CtrlF5进入调试模式F11可单步执行调试–窗口–内存–内存1可以查看当前程序使用的内存的内存的状态
断点之上的代码已经运行断点及断点之下的代码将要运行这里我们键入参数a的地址并且回车 3.1整体代码
#includeiostreamint main()
{std::cout hello, cpp! std::endl;std::cin.get();return 0;
}
}
3.2预处理 预处理Preprocessing是 C 编译过程中的第一个阶段它在实际的编译之前对源代码进行一系列的文本替换和处理操作。 #include iostream在编译之前C 编译器会进行预处理经预处理后iostream文件中的内容就被copy到我们的源文件中了。 项目中每一个cpp文件都会被编译但头文件不会被编译因为其已经被预处理过了。
3.3.编译 编译是将高级程序设计语言如C编写的源代码转换成计算机可以执行的机器代码的过程。 cpp的代码正常情况下是从上到下一行一行编译的头文件不会被执行编译其在预处理时被包含进来和后面的代码一起编译。
#includeiostream int main()
{std::cout hello, cpp! std::endl;std::cin.get();return 0
}完成编译后生成了obj文件。在 Windows 平台上“.obj” 文件通常是使用 Microsoft Visual Studio 编译器生成的。 obj文件包含了编译器对源代码的翻译结果。 这个文件中包含了与源代码中的每个函数和变量相关的机器代码并且还可能包含一些调试信息、符号表、重定位信息等。目标文件的生成是编译的一部分它还需要经过链接过程与其他目标文件和库文件一起生成最终的可执行文件exe。
3.4链接 链接Linking是 C编译过程中的一个重要阶段它将编译器生成的目标文件和其他可能用到的目标文件或库文件合并成最终的可执行文件。 每一个cpp源文件都对应一个obj链接器将其连接后生成可执行文件。
增加一个cpp源文件如下
#includeiostreamvoid log(const char* message)
{std::cout message std::endl;
}对其编译后在debug文件夹下会看到其生成的obj文件 4.C编译器
4.1定义与作用 C编译器是一种将C源代码翻译成目标代码通常是机器代码的工具。其工作包括词法分析、语法分析、语义分析、优化和代码生成等阶段。 词法分析Lexical Analysis编译器首先会将源代码分解成基本单元这些单元被称为词法单元tokens比如关键字、标识符、运算符等。 语法分析Syntax Analysis在这个阶段编译器将词法单元组织成语法树以检查代码的结构是否符合语法规则。 语义分析Semantic Analysis编译器会检查代码的语义是否合法比如变量是否被正确声明和使用函数是否正确调用等。 优化Optimization在生成目标代码之前编译器可能会进行一系列的优化操作以提高程序的性能或减小生成的目标代码的体积。 代码生成Code Generation最终阶段是生成目标代码这是计算机可以直接执行的机器代码。这些代码通常是针对特定硬件架构的。
4.2示例
#include iostreamint main() {int x 5; // 变量声明和赋值int y 10;int result x y; // 加法操作std::cout The result is: result std::endl;return 0;
}在这个示例中编译器将首先进行词法分析将代码分解成词法单元。然后进行语法分析构建语法树。接着进行语义分析确保变量被正确声明和使用。最后编译器会生成目标代码执行加法操作并输出结果。这是一个简化的过程实际编译器会进行更多的优化和处理。
4.3编译器的优化代码例子 ——“常数折叠” 常数折叠Constant Folding是指编译器在编译阶段对常量表达式进行计算并将其结果直接替换回表达式的值。这个过程发生在编译器静态分析阶段有助于优化代码减少运行时的计算量。 举个例子
int result 5 3 * 2;在常数折叠过程中编译器会对表达式进行计算。在这个例子中3 * 2是一个常量表达式编译器可以在编译阶段计算出其结果为6。因此整个表达式可以被折叠为
int result 5 6;这个过程可以减少程序运行时的计算量提高代码的执行效率。
5.C链接器 链接器Linker是编译器工具链中的一个部分它负责将多个目标文件Object Files或者库文件Library Files中的代码和数据结合起来生成最终的可执行文件或者库文件。在程序编译过程中源代码会被编译成目标文件这些目标文件可能包含了程序的不同部分比如函数、变量等。 链接器的主要任务包括 符号解析Symbol Resolution处理各个目标文件中使用的符号如函数、变量名将其与实际的内存地址或者其他目标文件中的符号联系起来。 符号合并Symbol Combining将各个目标文件中定义的符号整合到最终的输出文件中解决多个目标文件中可能存在的重复定义。 地址重定位Address Binding将各个目标文件中的符号地址绑定到最终的可执行文件或库文件中以便在程序加载到内存并执行时能够正确地定位和访问各个符号。 生成可执行文件或库文件Executable or Library Generation最终将链接后的目标文件生成可执行文件例如.exe文件或者库文件例如.dll或.lib文件供操作系统或其他程序使用。
链接器的工作分为静态链接和动态链接两种方式。静态链接是将所有的目标文件和库文件在编译时链接成一个完整的可执行文件而动态链接是在程序运行时才进行链接可以实现共享库Shared Library的功能。
总的来说链接器在编译过程的最后阶段将各个模块或库文件整合在一起生成可执行的程序或者库文件使得程序能够正确地运行。
6.头文件
6.1定义与作用 头文件Header File是一种文本文件通常以.h为扩展名包含了函数、类、变量等的声明和定义。头文件的主要作用是提供接口和声明让程序能够访问和使用某些功能而无需了解其具体实现细节。 头文件的作用包括 声明和接口提供 头文件通常包含函数、类、变量的声明允许在不暴露实现细节的情况下使用这些元素。其他文件可以包含头文件从而访问其中声明的内容而不必了解其实现细节。 模块化和组织性 头文件有助于组织代码将相关的功能组织到单独的模块中并通过包含不同的头文件来实现模块化。这样可以提高代码的可维护性和可读性。 重用和共享代码 头文件中的声明和接口可以被多个文件共享和重用使得相同的函数、类或变量能够在多个文件中使用减少重复编写代码的工作量。
头文件在以下场景中能够发挥作用 多文件项目 当程序由多个源文件组成时头文件能够提供接口和声明使得各个文件能够互相访问和使用彼此的功能提高了代码的组织性和可维护性。 库开发 在开发库或框架时头文件对外提供了公共接口和声明允许其他开发者使用库的功能而无需了解其内部实现。 项目协作 在团队协作开发项目时头文件定义了函数、类等的接口让团队成员了解如何使用这些接口而无需深入了解实现细节。
6.2示例
创建头文件例如functions.h头文件包含函数的声明和可能的结构、常量或其他相关内容。
// functions.h
#pragma once// 函数声明
int add(int a, int b);
float divide(float a, float b);
在源文件中包含头文件在需要使用这些函数的源文件中包含头文件。
// main.cpp#include iostream
#include functions.h // 包含头文件int main() {int result add(5, 3); // 调用头文件中声明的函数std::cout Result: result std::endl;return 0;
}实现函数在源文件中实现头文件中声明的函数。
// functions.cpp#include functions.h // 包含对应的头文件int add(int a, int b) {return a b;
}float divide(float a, float b) {if (b ! 0) {return a / b;} else {return 0; // 可以根据实际需求进行异常处理或错误提示}
}好处 模块化和组织性 使用头文件可以将函数的声明和结构、常量等信息组织在一起使得代码更模块化和易于维护。 可读性 头文件提供了函数接口的清晰定义使得阅读和理解代码更为容易。 代码重用 通过头文件的方式可以在多个源文件中重复使用相同的函数声明提高了代码的重用性。 错误检查 预编译器指令#ifndef、#define、#endif用于防止头文件被多次包含避免了重复定义和编译错误。 维护方便 当需要修改函数接口时只需在头文件中修改函数的声明而不必修改所有使用该函数的源文件。
4.3在实现函数的 functions.cpp 文件中包含 #include “functions.h” 的目的
在实现函数的 functions.cpp 文件中包含 #include “functions.h” 是为了确保函数的实现与其声明一致。这样做有以下作用 检查一致性 #include “functions.h” 会将头文件中的函数声明包含到 .cpp 文件中这样编译器就能检查函数的实现是否与其声明一致。如果实现与声明不一致编译器会报错提醒修正错误。 可读性和维护性 包含头文件可以让读者清晰地看到函数的接口和声明。这样使得代码更易读、易懂并且方便维护。 代码一致性 保持函数声明和定义的一致性是良好编程实践的一部分有助于代码的可靠性和一致性。
在某些情况下如果函数的实现非常简单不需要使用头文件中的其他信息例如结构、常量等有时可以省略包含头文件。但是为了代码的可读性和一致性我们最好在函数的实现文件中包含对应的头文件。