定州做网站,宝安设备网站设计,wordpress页面的添加,广东网站建设公司哪家好点击蓝字关注我们因公众号更改推送规则#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络#xff0c;侵删对于我来说#xff0c;学习 C 语言好难啊。这门语言本身的基础知识并不是很难#xff0c;但是“用 C 语言编程”需要用到各种知识#xff0c;…点击蓝字关注我们因公众号更改推送规则请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络侵删对于我来说学习 C 语言好难啊。这门语言本身的基础知识并不是很难但是“用 C 语言编程”需要用到各种知识这些知识可没有那么容易掌握C 语言在各个平台和操作系统上的行为有所差异因此你需要了解平台C 语言有许多编译器选项和构建工具即使运行一个简单的程序也需要做出很多决定C 语言涉及很多与 CPU、操作系统、编译代码有关的概念C 语言的使用方式多种多样远不像其他语言那样有中心化的社区和统一的风格。在本文中我想总结一下 C 语言的学习要点和建议希望能对你有所帮助。学习资源值得借鉴的项目编译、链接、标题和符号不推荐使用的功能数组不是值编译器的各种选项三种类型的内存以及何时使用它们命名约定static结构方法模式const平台和标准 APII整数大小算术运算与整数提升char 类型的符号宏与 const 变量宏与内联函数学习资源TutorialsPoint C基本知识介绍awesome-c库和工具列表cppreferenceC 语言和标准库的技术参考值得借鉴的项目在学习的过程中阅读一些 C 语言代码会很有帮助。Bloopsaphone一个声音合成 Ruby 库其核心有一个很小的 C 模块。概念少结构好Simple Dynamic Stringssds有一个 .c 和 .h 文件是一个很好的学习C语言的例子说明了如何管理更复杂的资源Brogue CE一款类 Roguelike 视频游戏。这个库相对较大大约有3万行代码。我正在维护这个代码库而且我们还有许多贡献者都是C语言高手stb 单文件库其中包含许多中小型 C 模块主要面向嵌入式设备和游戏机。编译、链接、标题和符号下面是一些关于如何编译 C 语言的基础知识。C 语言的代码是用源文件 .c 编写的。每个源文件都会被编译成一个目标文件.o这个文件就像一个容器装载了.c文件中编译后的函数。但这些函数是不可执行的。目标文件内部有一个符号表这些符号是该文件中定义的全局函数和变量的名称。# compile to objects
cc -c thing.c -o thing.o
cc -c stuff.c -o stuff.o源文件之间是完全独立的可并行编译成对象。如果想跨文件调用函数和变量则必须使用头文件.h。这些文件也是 C 源文件只不过使用方式比较特殊。回顾一下目标文件只包含全局函数和变量的名称没有类型、宏甚至没有函数参数。如果想跨文件使用这些符号就需要指定额外的信息。我们将这些“声明”单独放在 .h 文件中然后由其他 .c 文件通过 #include 包含进来。为避免重复通常 .c 文件不会定义自己的类型/宏等而是只包含自己或自己所属的模块或组件的头文件。你可以将头文件视为 API 的规范只不过实现可以放在多个源文件中。你甚至可以在同一个头文件中实现不同的平台或目的。关注公众号C语言中文社区免费领取500G编程资料如果编译时遇到一个只有声明例如通过头文件、没有定义的符号引用时编译出的目标文件会将其标记为缺失需要填补。最终的这部分工作由编译器的“链接器”组件完成由它负责将一个或多个对象连接在一起匹配所有的符号引用然后输出完整的可执行文件或共享库。# link objects to executable
cc thing.o stuff.o -o gizmo概括起来C语言的源文件中不能包含其他源文件只能包括声明然后由链接器完成匹配。不推荐使用的功能C 语言拥有悠长的发展历史尽管 C 语言一直在努力实现向后兼容但仍有一些功能是我们应该避免使用的。atoi()与atol()这两个函数在出错时会返回 0但这也是一个有效的返回值。个人更推荐 strtoi() 等。gets() 不安全因为这些函数无法给出目标缓冲区的界限。个人更喜欢 fgets()。数组不是值在学习 C 语言的过程中我们必须认识到C 语言作为一种语言只处理大小已知的数据块。你可以认为 C 语言是一种“复制已知大小值的语言”。我们可以向程序传递整数或结构通过函数返回它们并将它们视为相应的对象因为 C 知道它们的大小因此 C 可以编译代码并复制完整的数据。然而数组却完全不同。对于 C 语言来说数组的大小是未知的。假设我在一个函数中声明了一个变量 int[5]实际上我得到的并不是类型 int[5] 的值而是一个 int* 值它指向的位置分配了 5 个整数。由于这只是一个指针因此程序员必须代替语言来负责复制真正的数据并保证数据有效。但是结构内的数组与值一样可以与结构一起复制。严格来讲指定了大小的数组是真正的类型而不仅仅是指针例如你可以通过 sizeof 得知整个数组的大小。只不过你不能将它们视为独立的值。编译器的各种选项C 语言的编译器有很多选项而且默认值不是很好用。下面是一些你可能需要的选项。-O2在发布代码时对代码进行优化。-g -Og用于调试代码可以让调试器输出额外的信息并根据调试进行优化。-Wall启用更多警告有点像 linter你可以通过-Wno禁用特定警告。-Werror警告变成错误。我建议启用 -Werrorimplicit这样可以确保调用未声明的函数会报错。-DNAME 和 -DNAMEvalue用于定义宏。-std...选择一个标准。在大多数情况下你可以省略这个选项使用编译器的默认值通常是最新标准。如果你想使用“经典”C可以指定 -stdc89。三种类型的内存以及何时使用它们自动存储用于保存局部变量。当函数被调用时就会创建一个新的自动存储区域并在函数返回结果时删除。只有返回值会被保留并被复制到调用它的函数的自动存储中。这意味着返回一个指向局部变量的指针是不安全的因为底层数据会被默默删除。自动存储通常被称为“栈”。关注公众号C语言中文社区免费领取500G编程资料分配的存储运行malloc() 会返回的内存类型这种内存会一直保留直到被 free() 函数释放所以可以被传递到任何地方包括返回给上级调用函数。通常被称为“堆”。静态存储在程序的整个生命周期内有效。在进程启动时分配全局变量都存储在这里。如果想通过一个函数“返回”内存不必通过调用 malloc可以直接将一个指向本地数据的指针传递给函数void getData(int *data) {
data[0] 1;
data[1] 4;
data[2] 9;
}
void main() {
int data[3];
getData(data);
printf(%d\n, data[1]);
}命名约定C 语言不支持命名空间。如果你想编写一个公共库或者想命名某个“模块”则需要给所有公共 API 的名称加上一个前缀。这些名称包括函数类型枚举值宏另外每个枚举也应该加上不同的前缀这样才能分辨某个值属于哪种枚举类型enum color {
COLOR_RED,
COLOR_BLUE,
...
}关于命名并没有太多真正的约定你可以随意选择蛇形命名法snake_case或驼峰式命名法camelCase但请记住保持一致由于许多标准 C 类型都采用了 ptrdiff_t、int32_t 等形式所以有人将类型命名为 my_type_t。static函数或文件级别的 static静态变量仅限文件内部访问。这些函数或变量不会作为符号导出因此无法在其他源文件中使用。static 也可以用在局部变量上可以让变量在多次函数调用之间保持值不变。你可以将其视为一个仅限于该函数使用的全局变量。你可以利用 static 计算和存储数据以供后续调用重用。但请记住这种使用方法与全局状态或共享状态有同样的问题例如线程安全、递归冲突等。结构方法模式如果你在学习 C 语言之前学习过更有特色的语言可能会发现很难将这些知识应用到 C 语言的学习中。例如面向对象编程常见的一个概念结构方法即函数接受指向结构的指针并通过指针修改结构或获取属性typedef struct {
int x;
int y;
} vec2;
void vec_add(vec2 *u, const vec2 *v) {
u-x v-x;
u-y v-y;
}
int vec_dot(const vec2 *u, const vec2 *v) {
return u-x * v-x u-y * v-y;
}你无法扩展结构或实现类似于面向对象的功能但采用这种思路来思考问题很有用。const以 const T 的形式声明类型 T 的变量或参数则表示这个变量或参数不能被修改。这意味着不能赋值而且如果 T 是指针或数组类型也不能被修改。你可以将 T 转换为 const T但反之不行。设置函数的指针参数默认为 const 是一个好习惯只有确实需要修改这些变量时再省略 const。平台和标准 API我们很难根据 #include some_header.h 来判断依赖项究竟是什么它有可能来自标准 C 库缩写为“stdlib”。比如stdio.h、stdlib.h、error.h。这是语言规范的一部分所有兼容的平台和编译器都应该实现。非常安全可以放心使用。https://en.cppreference.com/w/c/headerPOSIX操作系统 API 的标准。比如unistd.h、sys/time.h。一般由 Linux、macOS、BSDs 实现。默认情况下不可在Windows使用。如果使用 MinGW则可以使用 POSIX API。如果想获得更完整的支持可以使用 Cygwin 库。你可以通过官方的OpenGroup页面或帮助手册查看POSIX头文件的所有详细信息包括 C stdlib。非标准操作系统接口。特定于 Linux 的 API。Windows Win32以及 C/WinRT——这是一种更现代的 C 接口。Mac 的 OS API 是 Objective C现在是 Swift而不是 C。安装在标准位置的第三方库。你可以通过不依赖于平台的头文件与更多平台特定的代码进行交互这样就可以通过不同的方式实现。许多流行的 C 库本质上只是对特定于平台的功能进行了统一的、精心设计的抽象。整数C 语言中的整数是一个非常大的坑。编写代码时一定要小心。大小所有整数类型都有确定的最小位数。在一些常见的平台中整数的大小都大于最小位数例如 int 在 Windows、macOS 和 Linux 上都是 32 位的但其最小位数是 16 位的。在编写可移植的代码时你必须小心不能让整数的大小超过最小位数。如果想精确控制整数大小可以使用 stdint.h 中的标准类型如 int32_t、uint64_t 等。还有 _least_t 和 _fast_t 类型。算术运算与整数提升C语言中的算术运算有许多奇怪的规则并产生意想不到的或不可移植的结果。另外请格外小心整数提升。char 类型的符号所有其他整数类型默认都有符号但char可以有符号也可以没有符号具体取决于平台。因此只有在作为字符时这种类型才可移植。如果你想指定一个很小的数字比如只有8位也要指定符号。宏与 const 变量如果想定义一个非常简单的常量值你有两种选择static const int my_constant 5;
// 或者
#define MY_CONSTANT 5二者的不同之处在于前者是一个真正的变量而后者是一个复制粘贴的行内表达式。宏与变量不同你可以在需要“常量表达式”的上下文中使用宏例如数组长度或 switch 语句变量与宏不同你可以获得指向变量的指针。关注公众号C语言中文社区免费领取500G编程资料“常量表达式”实际上非常实用因此常常被定义为宏。而变量则更适合更大或更复杂的值如结构实例。宏与内联函数宏可以有参数并扩展为 C 代码。相较于函数宏的优势在于宏产生的代码相当于直接粘贴到周围的代码中而不像函数需要一个调用指令。这样代码的运行速度更快因为函数调用需要额外的开销宏不需要规定类型。例如任何数字类型都可以执行 x y 运算。如果写成函数就必须声明参数并指定类型比如类型大小、是否有符号因此使用很有限。缺点参数需要反复计算。假设我们有一个宏 MY_MACRO(x)如果定义中多次使用 x那么表达式 x 将被反复计算因为它只是简单地复制和粘贴。而相比之下函数的参数表达式只需要计算一次然后将结果传递给函数。宏更容易出错因为它们是源代码级别。尽可能多使用括号将宏的整个定义和每个参数都放到括号内这样表达式就不会不小心被合并。// 不推荐这种写法:
#define MY_MACRO(x) xx
// 应该写成:
#define MY_MACRO(x) ((x)(x))除非你需要多种泛型否则可以直接定义静态内联函数static inline function这样就可以兼具二者的优点。内联表示函数中的代码应该直接编译到使用的地方而不是被调用。你可以将静态内联函数放在头文件中就像宏一样。如果你年满18周岁以上又觉得学【C语言】太难想尝试其他编程语言那么我推荐你学Python现有价值499元Python零基础课程限时免费领取限10个名额▲扫描二维码-免费领取戳“阅读原文”我们一起进步