我要是在学习 C 语言之前知道这些就好了!

点击蓝字

4ceca051afaba5223a794fe0132a0ed0.png

关注我们

因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享

来源于网络,侵删

对于我来说,学习 C 语言好难啊。这门语言本身的基础知识并不是很难,但是“用 C 语言编程”需要用到各种知识,这些知识可没有那么容易掌握:

  • C 语言在各个平台和操作系统上的行为有所差异,因此你需要了解平台;

  • C 语言有许多编译器选项和构建工具,即使运行一个简单的程序也需要做出很多决定;

  • C 语言涉及很多与 CPU、操作系统、编译代码有关的概念;

  • C 语言的使用方式多种多样,远不像其他语言那样有中心化的社区和统一的风格。

在本文中,我想总结一下 C 语言的学习要点和建议,希望能对你有所帮助。

  • 学习资源

  • 值得借鉴的项目

  • 编译、链接、标题和符号

  • 不推荐使用的功能

  • 数组不是值

  • 编译器的各种选项

  • 三种类型的内存,以及何时使用它们

  • 命名约定

  • static

  • 结构方法模式

  • const

  • 平台和标准 APII

  • 整数

  • 大小

  • 算术运算与整数提升

  • char 类型的符号

  • 宏与 const 变量

  • 宏与内联函数

baa16bda55723cae79f79e96e12d11c4.png

学习资源

  • TutorialsPoint C:基本知识介绍

  • awesome-c:库和工具列表

  • cppreference:C 语言和标准库的技术参考

4a2a15593b23194693f9111e88aad7af.png

值得借鉴的项目

在学习的过程中,阅读一些 C 语言代码会很有帮助。

  • Bloopsaphone:一个声音合成 Ruby 库,其核心有一个很小的 C 模块。概念少,结构好;

  • Simple Dynamic Strings(sds):有一个 .c 和 .h 文件,是一个很好的学习C语言的例子,说明了如何管理更复杂的资源;

  • Brogue CE:一款类 Roguelike 视频游戏。这个库相对较大,大约有3万行代码。我正在维护这个代码库,而且我们还有许多贡献者都是C语言高手;

  • stb 单文件库:其中包含许多中小型 C 模块,主要面向嵌入式设备和游戏机。

ae4aa7a0ebc9fc313a125eb7c5c95edd.png

编译、链接、标题和符号

下面是一些关于如何编译 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语言的源文件中不能包含其他源文件,只能包括声明,然后由链接器完成匹配。

8feff5194e1cbffc4bfbbc07cc0f657e.png

不推荐使用的功能

C 语言拥有悠长的发展历史,尽管 C 语言一直在努力实现向后兼容,但仍有一些功能是我们应该避免使用的。

  • atoi()与atol():这两个函数在出错时会返回 0,但这也是一个有效的返回值。个人更推荐 strtoi() 等。

  • gets() :不安全,因为这些函数无法给出目标缓冲区的界限。个人更喜欢 fgets()。

54822861a36adfe647c7753becbe491f.png

数组不是值

在学习 C 语言的过程中,我们必须认识到,C 语言作为一种语言,只处理大小已知的数据块。你可以认为 C 语言是一种“复制已知大小值的语言”。

我们可以向程序传递整数或结构,通过函数返回它们,并将它们视为相应的对象,因为 C 知道它们的大小,因此 C 可以编译代码,并复制完整的数据。

然而,数组却完全不同。对于 C 语言来说,数组的大小是未知的。假设我在一个函数中声明了一个变量 int[5],实际上我得到的并不是类型 int[5] 的值,而是一个 int* 值,它指向的位置分配了 5 个整数。由于这只是一个指针,因此程序员必须代替语言来负责复制真正的数据并保证数据有效。

但是,结构内的数组与值一样,可以与结构一起复制。

(严格来讲,指定了大小的数组是真正的类型,而不仅仅是指针,例如你可以通过 sizeof 得知整个数组的大小。只不过,你不能将它们视为独立的值。)

1d2caa27cd2162c7aba4236c59e35821.png

编译器的各种选项

C 语言的编译器有很多选项,而且默认值不是很好用。下面是一些你可能需要的选项。

  • -O2:在发布代码时,对代码进行优化。

  • -g -Og:用于调试代码,可以让调试器输出额外的信息,并根据调试进行优化。

  • -Wall:启用更多警告(有点像 linter),你可以通过-Wno禁用特定警告。

  • -Werror:警告变成错误。我建议启用 -Werror=implicit,这样可以确保调用未声明的函数会报错。

  • -DNAME 和 -DNAME=value:用于定义宏。

  • -std=...:选择一个标准。在大多数情况下,你可以省略这个选项,使用编译器的默认值(通常是最新标准)。如果你想使用“经典”C,可以指定 -std=c89。

7cd974e3c9cb04fb2f63597ac5977235.png

三种类型的内存,以及何时使用它们

自动存储:用于保存局部变量。当函数被调用时,就会创建一个新的自动存储区域,并在函数返回结果时删除。只有返回值会被保留,并被复制到调用它的函数的自动存储中。这意味着,返回一个指向局部变量的指针是不安全的,因为底层数据会被默默删除。自动存储通常被称为“栈”。

关注公众号: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]);
}

330688cd21b10365418fb0e546108a26.png

命名约定

C 语言不支持命名空间。如果你想编写一个公共库,或者想命名某个“模块”,则需要给所有公共 API 的名称加上一个前缀。这些名称包括:

  • 函数

  • 类型

  • 枚举值

另外,每个枚举也应该加上不同的前缀,这样才能分辨某个值属于哪种枚举类型:

enum color {
COLOR_RED,
COLOR_BLUE,
...
}

关于命名,并没有太多真正的约定,你可以随意选择蛇形命名法(snake_case)或驼峰式命名法(camelCase),但请记住保持一致!由于许多标准 C 类型都采用了 ptrdiff_t、int32_t  等形式,所以有人将类型命名为 my_type_t。

static

函数或文件级别的 static(静态)变量仅限文件内部访问。这些函数或变量不会作为符号导出,因此无法在其他源文件中使用。

static 也可以用在局部变量上,可以让变量在多次函数调用之间保持值不变。你可以将其视为一个仅限于该函数使用的全局变量。你可以利用 static 计算和存储数据,以供后续调用重用。但请记住,这种使用方法与全局状态或共享状态有同样的问题,例如线程安全、递归冲突等。

9222ffac309387330b0b7d6608f112ed.png

结构方法模式

如果你在学习 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。

6e1ab272bf83c3ed8e9ab2bd403b045e.png

平台和标准 API

我们很难根据 #include <some_header.h> 来判断依赖项究竟是什么,它有可能来自:

  • 标准 C 库(缩写为“stdlib”)。比如:stdio.h、stdlib.h、error.h。

  • 这是语言规范的一部分,所有兼容的平台和编译器都应该实现。非常安全,可以放心使用。

  • https://en.cppreference.com/w/c/header

  • POSIX:操作系统 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 库本质上只是对特定于平台的功能进行了统一的、精心设计的抽象。

63d6072b3468bc59a68c79643632a8b3.png

整数

C 语言中的整数是一个非常大的坑。编写代码时,一定要小心。

大小

所有整数类型都有确定的最小位数。在一些常见的平台中,整数的大小都大于最小位数,例如 int 在 Windows、macOS 和 Linux 上都是 32 位的,但其最小位数是 16 位的。在编写可移植的代码时,你必须小心,不能让整数的大小超过最小位数。

如果想精确控制整数大小,可以使用 stdint.h 中的标准类型,如 int32_t、uint64_t 等。还有 _least_t 和 _fast_t 类型。

算术运算与整数提升

C语言中的算术运算有许多奇怪的规则,并产生意想不到的或不可移植的结果。

另外,请格外小心整数提升。

char 类型的符号

所有其他整数类型默认都有符号,但char可以有符号,也可以没有符号,具体取决于平台。因此,只有在作为字符时,这种类型才可移植。如果你想指定一个很小的数字,比如只有8位,也要指定符号。

a2327902dfd72048ce2396dde5aa240b.png

宏与 const 变量

如果想定义一个非常简单的常量值,你有两种选择:

static const int my_constant = 5;
// 或者
#define MY_CONSTANT 5

二者的不同之处在于,前者是一个真正的变量,而后者是一个复制粘贴的行内表达式。

  • 宏:与变量不同,你可以在需要“常量表达式”的上下文中使用宏,例如数组长度或 switch 语句

  • 变量:与宏不同,你可以获得指向变量的指针。

  • 关注公众号:C语言中文社区,免费领取500G编程资料

“常量表达式”实际上非常实用,因此常常被定义为宏。而变量则更适合更大或更复杂的值,如结构实例。

5e852b8ed0b31a8cd96a2a3c7c411713.png

宏与内联函数

宏可以有参数,并扩展为 C 代码。

相较于函数,宏的优势在于:

  • 宏产生的代码相当于直接粘贴到周围的代码中,而不像函数需要一个调用指令。这样代码的运行速度更快,因为函数调用需要额外的开销;

  • 宏不需要规定类型。例如,任何数字类型都可以执行 x + y 运算。如果写成函数,就必须声明参数,并指定类型,比如类型大小、是否有符号,因此使用很有限。

缺点:

  • 参数需要反复计算。假设我们有一个宏 MY_MACRO(x),如果定义中多次使用 x,那么表达式 x 将被反复计算,因为它只是简单地复制和粘贴。而相比之下,函数的参数表达式只需要计算一次,然后将结果传递给函数。

  • 宏更容易出错,因为它们是源代码级别。尽可能多使用括号,将宏的整个定义和每个参数都放到括号内,这样表达式就不会不小心被合并。

// 不推荐这种写法:
#define MY_MACRO(x) x+x
// 应该写成:
#define MY_MACRO(x) ((x)+(x))

除非你需要多种泛型,否则可以直接定义静态内联函数(static inline function),这样就可以兼具二者的优点。内联表示,函数中的代码应该直接编译到使用的地方,而不是被调用。你可以将静态内联函数放在头文件中,就像宏一样。

如果你年满18周岁以上,又觉得学【C语言】太难?想尝试其他编程语言,那么我推荐你学Python,现有价值499元Python零基础课程限时免费领取,限10个名额!
▲扫描二维码-免费领取

戳“阅读原文”我们一起进步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/336978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

opencv yuv保存本地_OpenCV-dlib-python3实现人脸戴墨镜和含Y的抖音效果

1 说明&#xff1a;1.1 吸烟有害健康&#xff01;&#xff01;纯属娱乐和学习python的相关知识。1.2 虽然是娱乐&#xff0c;但是opencv、dlib和python在人工智能、人脸识别、自动化等有很大作用&#xff0c;目前已经或者未来会有更多的应用&#xff0c;作为一名普通人&#xf…

为什么永远不会有语言取代 C/C++?

关注星标&#xff0c;每天学习C语言新技能因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源&#xff1a;网络数以百计的 C/C 替代品将会出现&#xff0c;但 C/C 将永远与我们同在&#xff01;每个 CPU 都带有一种称为 ISA&#xff08;指…

qt 表格中插入一行_在EXCEL表格中,快速插入多行、多列的技巧

在使用Excel过程中&#xff0c;我们会遇到需要插入相同格式的多行或多列&#xff0c;如果一行行或一列列的插入&#xff0c;对于插入的数量较少的情况还是适用的。可是如果需要插入上百的行或列&#xff0c;使用此方法就比较费时费力啦。分享几个小技巧实现快速插入多行或多列.…

amber 口译_口译员设计模式示例

amber 口译本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#xff…

别再自己瞎写工具类了,SpringBoot内置工具类应有尽有,建议收藏!!

关注星标&#xff0c;每天学习C语言新技能因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源&#xff1a;网络断言断言是一个逻辑判断&#xff0c;用于检查不应该发生的情况Assert 关键字在 JDK1.4 中引入&#xff0c;可通过 JVM 参数-en…

ad转换器工作原理_AD转换中参考电压的作用

AD转换AD转换就是模数转换。顾名思义&#xff0c;就是把模拟信号转换成数字信号。主要包括积分型、逐次逼近型、并行比较型/串并行型、Σ-Δ调制型、电容阵列逐次比较型及压频变换型。A/D转换器是用来通过一定的电路将模拟量转变为数字量。模拟量可以是电压、电流等电信号&…

面试大全 | C语言高级部分总结

关注星标&#xff0c;每天学习C语言新技能因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源&#xff1a;网络一、内存大话题1.0、内存就是程序的立足之地&#xff0c;体现内存重要性。1.1、内存理解&#xff1a;内存物理看是有很多个Ban…

ideal pom文件安装到maven库中_java学习之web基础(14)Maven基础学习

maven介绍Maven 是一个项目管理工具&#xff0c;它包含了一个项目对象模型 (POM&#xff1a; Project Object Model)&#xff0c;一组标准集合&#xff0c;一个项目生命周期(Project Lifecycle)&#xff0c;一个依赖管理系统(Dependency Management System)&#xff0c;和用来运…

戴尔集群监控与管理系统_监控与管理

戴尔集群监控与管理系统本文是我们名为“ EAI的Spring集成 ”的学院课程的一部分。 在本课程中&#xff0c;向您介绍了企业应用程序集成模式以及Spring Integration如何解决它们。 接下来&#xff0c;您将深入研究Spring Integration的基础知识&#xff0c;例如通道&#xff0…

三位数除以两位数竖式计算没有余数_苏教四上期末复习——两、三位数除以两位数...

期末复习读万卷书 &#xff1c;做一好题第二单元两、三位数除以两位数计算能力1、竖式计算5106740961700262914246829810132、简便方法计算150253810(92)560353、填空720秒( )分300分( )时336时( )日调商1、小李计算一道除法是两位数的除法算式&#xff0c;商是12&#x…

单例模式示例_单例设计模式示例

单例模式示例本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#x…

解读C++即将迎来的重大更新(一):C++20的四大新特性

关注星标&#xff0c;每天学习C语言新技能因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源&#xff1a;网络C20&#xff08;C 编程语言标准 2020 版&#xff09;将是 C 语言一次非常重大的更新&#xff0c;将为这门语言引入大量新特性。…

小尼机器人_小尼被机器人嫌弃“唱歌难听,长相一般”?

我们如今所处的时代&#xff0c;科技创新的速度日新月异,生活方式多彩多姿。人人都说&#xff1a;科技改变了生活。今晚《开门大吉》也迎来了三大改变生活的神奇黑科技&#xff01;智能且生态的“移动城堡”在网上预定好酒店以后&#xff0c;到了现场没有前台和服务员&#xff…

产品原型示例_原型设计模式示例

产品原型示例本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#x…

13 年,MySQL 之父赌赢了:另起炉灶的 MariaDB 成功上市!

关注星标&#xff0c;每天学习C语言新技能因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源&#xff1a;网络&#xff0c;侵权删&#xff01;今年 2 月&#xff0c;开源数据库厂商 MariaDB 完成了 1.04 亿美元的 D 轮融资&#xff0c;同…

太阳粒子是什么东西_太阳光子前世今生告诉我们现在享受之阳光是十几万年前诞生的老光...

我们都知道天晴时阳光明媚&#xff0c;但这个阳光是怎么来的呢&#xff0c;很多人就不一定清楚了。在这里我们首先来了解几个概念&#xff1a;光子、光、电磁波、电磁辐射。光子是传递电磁波相互作用的基本粒子&#xff0c;是一种规范波色子&#xff0c;是电磁辐射的载体&#…

java 观察者模式示例_观察者设计模式示例

java 观察者模式示例本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 …

int类型存小数 mysql_MySQL基本数据类型

1&#xff09;整形1.介绍分类&#xff1a;tinyint , smallint , mediumint , int , bigint 应用场景&#xff1a;存储年龄&#xff0c;等级&#xff0c;id&#xff0c;各种号码等典型存储范围介绍:https://images2017.cnblogs.com/blog/1036857/201708/1036857-201708011814337…

枚举重名_举重设计模式示例

枚举重名本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#xff01…

装饰着模式示例_装饰器设计模式示例

装饰着模式示例本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &…