我要是在学习 C 语言之前知道这些就好了!天安门上毛主席像已挂73年,还要挂多久?早在1980年邓公便已解答

2023-06-30 0 342

我要是在学习 C 语言之前知道这些就好了!天安门上毛主席像已挂73年,还要挂多久?早在1980年邓公便已解答

【CSDN萨德基】C词汇是很多人的程式设计进阶词汇,责任编辑译者感叹,C词汇昂西桑县了,就算我能快点知悉那些就好了!只好,他历史记录了自学C词汇前夕的许多好工程项目,CSDN 组织机构翻译者校对撷取给我们。

书名镜像:https://tmewett.com/c-tips/#good-projects-to-learn-from

译者:Tom M

翻译者:弯月

对我而言,自学 C 词汇好难啊。尖萼词汇这类的基础科学知识并并非极难,但“用 C 词汇程式设计”须要加进各式各样科学知识,那些科学知识可没有所以难掌控:

C 词汇在各网络平台和作业系统上的犯罪行为略有差别,因而你须要介绍网络平台;

C 词汇有很多校对器快捷键和构筑辅助工具,即便运转一个单纯的流程也须要作出很多下定决心;

C 词汇牵涉很多与 CPU、作业系统、校对标识符相关的基本概念;

C 词汇的采用形式各种各样,远不像其它词汇那般有虚拟化的街道社区和标准化的艺术风格。

在责任编辑中,我想归纳呵呵 C 词汇的自学关键点和提议,期望能对你略有协助。

自学天然资源

值得称赞先进经验的工程项目

校对、镜像、副标题和记号

不所推荐采用的机能

字符串并非值

校对器的各式各样快捷键

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

命名约定

static

结构方法模式

const

网络平台和标准 APII

整数

大小

算术运算与整数提升

char 类型的记号

宏与 const 变量

宏与内联函数

自学天然资源

TutorialsPoint C:基本科学知识介绍

awesome-c:库和辅助工具列表

cppreference:C 词汇和标准库的技术参考

值得称赞先进经验的工程项目

在自学的过程中,阅读许多 C 词汇标识符会很有协助。

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

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

Brogue CE:一款类 Roguelike 视频游戏。这个库相对较大,大约有3万行标识符。我正在维护这个标识符库,而且我们还有很多贡献者都是C词汇高手;

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

校对、镜像、副标题和记号

下面是许多关于如何校对 C 词汇的基础科学知识。

C 词汇的标识符是用源文件 .c 编写的。每个源文件都会被校对成一个目标文件.o,这个文件就像一个容器装载了.c文件中校对后的函数。但那些函数是不可执行的。目标文件内部有一个记号表,那些记号是该文件中定义的全局函数和变量的名称。

# compile to objectscc -c thing.c -o thing.occ -c stuff.c -o stuff.o

源文件之间是完全独立的,可并行校对成对象。

如果想跨文件调用函数和变量,则必须采用头文件(.h)。那些文件也是 C 源文件,只不过采用形式比较特殊。回顾呵呵,目标文件只包含全局函数和变量的名称,没有类型、宏,甚至没有函数参数。如果想跨文件采用那些记号,就须要指定额外的信息。我们将那些“声明”单独放在 .h 文件中,然后由其它 .c 文件通过 #include 包含进来。

为避免重复,通常 .c 文件不会定义自己的类型/宏等,而是只包含自己或自己所属的模块或组件的头文件。

你可以将头文件视为 API 的规范,只不过实现可以放在多个源文件中。你甚至可以在同一个头文件中实现不同的网络平台或目的。

如果校对时遇到一个只有声明(例如通过头文件)、没有定义的记号引用时,校对出的目标文件会将其标记为缺失须要填补。

最终的这部分工作由校对器的“镜像器”组件完成,由它负责将一个或多个对象连接在一起,匹配所有的记号引用,然后输出完整的可执行文件或共享库。

# link objects to executablecc 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:警告变成错误。我提议启用 -Werror=implicit,这样可以确保调用未声明的函数会报错。

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

-std=…:选择一个标准。在大多数情况下,你可以省略这个快捷键,采用校对器的默认值(通常是最新标准)。如果你想采用“经典”C,可以指定 -std=c89。

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

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

分配的存储:运转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 词汇以后,自学过

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 来判断依赖项究竟是什么,它有可能来自:

标准 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 库本质上只是对特定于网络平台的机能进行了标准化的、精心设计的抽象。

整数

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 标识符。

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

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

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

缺点:

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

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

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

除非你须要多种泛型,否则可以直接定义静态内联函数(static inline function),这样就可以兼具二者的优点。内联表示,函数中的标识符应该直接校对到采用的地方,而并非被调用。你可以将静态内联函数放在头文件中,就像宏一样。

我要是在学习 C 语言之前知道这些就好了!天安门上毛主席像已挂73年,还要挂多久?早在1980年邓公便已解答
广告
可御可甜 有颜有料 惩罚整蛊任你选 >>进入直播间与主播亲密互动
×
☞☞

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务