七库下载 手游攻略 手游攻略 学c语言之前应该学的知识,学c语言的前提是什么

学c语言之前应该学的知识,学c语言的前提是什么

时间:2024-04-03 03:10:08 来源:头条 浏览:0

【CSDN编者按】C语言对很多人来说是入门编程语言,但本文作者感叹“C太难了,早点知道就好了!”于是他在学习C的同时记录了一些很棒的项目,CSDN组织译者进行编辑,分享给大家。原文链接:https://tmewett.com/c-tips/#good-projects-to-learn-from

作者:汤姆·M.

译者:三日月

对于我来说,学习C语言是非常困难的。虽然语言本身的基础知识并没有那么难,但“C 语言编程”需要大量的知识,而且学起来也不是那么容易。

C 的行为因平台和操作系统而异,因此您需要了解您的平台。

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

C 语言包含许多与CPU、操作系统和编译代码相关的概念。

尽管C语言可以有多种不同的使用方式,但它不像其他语言那样有集中的社区或统一的风格。

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

学习资源

一个值得学习的项目

编译、链接、标头和符号

过时的功能

数组不是一个值

编译器选项

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

命名约定

静止的

结构方法模式

持续的

平台和标准APII

整数

尺寸

算术运算和整数提升

char类型符号

宏和常量变量

宏和内联函数

学习资源

教程C点:基础知识介绍

Awesome-c:库和工具列表

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

一个值得学习的项目

在学习时,阅读一些C 代码可能会有所帮助。

Bloopsaphone:一个以小型C 模块为中心的声音合成Ruby 库。概念很少,结构非常好。

简单动态字符串(sds):学习C 语言的一个很好的示例,其中的.c 和.h 文件展示了如何管理更复杂的资源。

Brogue CE:Roguelike 视频游戏。这个库比较大,大约有3万行代码。我维护这个代码库,有很多贡献者都是C 语言专家。

stb单文件库:包含大量中小型C模块,主要用于嵌入式设备和游戏机。

编译、链接、标头和符号

这里我们将介绍如何编译C语言的一些基础知识。

C语言代码写在源文件.c中。每个源文件都被编译成目标文件.o。它就像一个在.c 文件中加载编译函数的容器。然而,这些函数是不可执行的。目标文件内有一个符号表,这些符号是文件内定义的全局函数和变量的名称。

# 编译为对象cc -c thing.c -o thing.occ -c stuff.c -o stuff.o

源文件是完全独立的,可以并行编译成对象。

如果要在文件之间调用函数或变量,则必须使用头文件(.h)。这些文件也是C源文件,但它们的使用方式特殊。回想一下,目标文件仅包含全局函数和变量的名称,而不包含类型、宏,甚至函数参数。如果在整个文件中使用这些符号,则必须指定附加信息。将这些“声明”分别放在.h 文件中,并#include 它们到其他.c 文件中。

为了避免重复,c 文件通常不定义自己的类型/malos 等,而仅包含其自身或其所属模块或组件的头文件。

头文件可以被认为是API 的规范,只不过实现可以放置在多个源文件中。您还可以在同一个头文件中实现不同的平台和用途。

如果在编译过程中遇到仅声明但未定义的符号引用(例如在头文件中),则编译后的目标文件会将其标记为丢失并且必须嵌入。

这项工作的最后一部分是由编译器的“链接器”组件执行的。该组件负责链接一个或多个对象,匹配所有符号引用,并输出完整的可执行文件或共享库。

# 将对象链接到可执行文件cc thing.o thing.o -o gizmo

综上所述,C语言源文件不能包含其他源文件。它只能包含声明,链接器将完成匹配。

过时的功能

尽管C语言有着悠久的发展历史,并且一直努力向后兼容,但仍然有一些特性是应该避免的。

atoi() 和atol():这两个函数出错时返回0,这也是有效的返回值。我个人推荐strtoi()。

gets() :这些函数无法提供目标缓冲区的边界,因此它们是不安全的。就我个人而言,我更喜欢fgets()。

数组不是一个值

在学习C语言的过程中,你需要认识到C语言只处理已知大小的数据块。您可以将C 视为“用于复制已知大小的值的语言”。

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

然而,数组却完全不同。在C语言中,数组的大小是未知的。当你在函数中声明一个变量int[5] 时,你实际得到的并不是一个int[5] 类型的值,而是一个指向5 个整数被赋值位置的int* 值。由于这只是一个指针,因此程序员必须复制实际数据,而不是语言,并负责确保数据有效。

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

(严格来说,给定大小的数组是真实类型,而不仅仅是指针。例如,您可以使用sizeof 找出整个数组的大小。但是,将它们视为独立值是可以的' t。)

编译器选项

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\', 数据[1]);}

命名约定

C语言不支持命名空间。如果您创建公共库或想要命名您的“模块”,则必须为所有公共API 的名称添加前缀。这些名称包括:

功能

类型

枚举值

大的

此外,每个枚举类型应该有不同的前缀,以便您可以区分特定值属于哪个枚举类型。

枚举颜色{COLOR_RED,COLOR_BLUE,}

命名方面并没有太多规则。随意使用snake_case或CamelCase,但记住要保持一致。许多标准C 类型采用ptrdiff_t、int32_t 等形式,因此有人将类型命名为my_type_t。

静态函数或文件级静态变量只能在文件内访问。这些函数或变量不会导出为符号,并且不能在其他源文件中使用。

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) {返回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 和BSD 实现。

默认情况下在Windows 上不可用。如果您使用MinGW,则可以使用POSIX API。如果您需要更完整的支持,可以使用Cygwin 库。

您可以在OpenGroup 的官方页面或帮助手册上查看有关POSIX 头文件(包括C stdlib)的所有详细信息。

非标准操作系统接口。

Linux 特定的API。

Windows Win32(以及更现代的C++ 接口C++/WinRT——)。

(Mac OS API 是Objective C(现在是Swift),而不是C。)

第三方库安装在标准位置。

您可以通过独立于平台的头文件来使用更多特定于平台的代码,这些头文件可以通过多种方式实现。许多流行的C 库本质上都是精心设计的抽象,集成了特定于平台的功能。

整数

C 中的整数是一个巨大的陷阱。编写代码时要小心。

大小所有整数类型都有一定的最小位数。在某些常见平台上,整数的大小超出了最小位数。例如,在Windows、macOS 和Linux 上,int 为32 位,但最小位数为16 位。编写可移植代码时,必须注意确保整数不超过最小位数。

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

算术运算和整数提升C 算术运算有许多奇怪的规则,会产生意外且不可移植的结果。

另外,要特别小心整数提升。

对char 类型进行签名默认情况下,所有其他整数类型都是有符号的,但char 可以有符号或无符号,具体取决于平台。因此,该类型仅在用作角色时才可移植。如果您指定一个非常小的数字(例如仅8 位数字),请同时指定符号。

宏和常量变量

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

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))

除非您需要多个泛型,否则您可以直接定义静态内联函数并获得两全其美的效果。内联意味着函数内的代码必须在使用的地方直接编译,而不是被调用。与宏类似,静态内联函数可以放置在头文件中。

标题:学c语言之前应该学的知识,学c语言的前提是什么
链接:https://www.7kxz.com/news/gl/19447.html
版权:文章转载自网络,如有侵权,请联系删除!
资讯推荐
更多
天地劫幽城再临归真4-5攻略:第四章归真4-5八回合图文通关教学

天地劫幽城再临归真4-5攻略:第四章归真4-5八回合图文通关教学[多图],天地劫幽城再临归真4-5怎么样八回合内通

2024-04-03
航海王热血航线艾尼路怎么玩?艾尼路加点连招攻略大全

航海王热血航线艾尼路怎么玩?艾尼路加点连招攻略大全[多图],航海王热血航线艾尼路怎么加点?艾尼路怎么连招?关

2024-04-03
坎公骑冠剑国际服怎么玩?国际服新手攻略

坎公骑冠剑国际服怎么玩?国际服新手攻略[多图],坎公骑冠剑国际服的玩法是什么样的?关于游戏中的一些新手玩法

2024-04-03
王者荣耀鸿运6+1地狱之眼怎么抽?鸿运抽奖地狱之眼概率获取攻略

王者荣耀鸿运6+1地狱之眼怎么抽?鸿运抽奖地狱之眼概率获取攻略[多图],王者荣耀鸿运抽奖活动的奖池中还有传说

2024-04-03