C 语言学习笔记之基础篇
xcbyao 小妖

0x00 前言概览

这个系列主要是整理 C 语言相关的一些知识,方便后期的复习与检索,适用者为学过基础的入门者编写程序时提供快捷便利的检索条件,纯小白可先收藏。此系列大致分为基础篇、进阶篇、高级篇,本文为基础篇,主要是一些零散知识点的整理,内容没有先后顺序,建议读者 Ctrl+F 查询;同时本着极简原则,一些知识点会直接举例说明。

0x01 切入正题

注释

1
2
/*单多行注释*/
//单行注释(C99)

命名

编译器只识别前 63 个字符,对于外部标识符只允许使用 31 个字符(C99/C11);名称第一个字符必须是大小写字母或下划线,不能是数字,区分大小写,但标识符最好不要超过 8 位。

变量与常量

变量未初始化可能为 0 或垃圾值,不允许连续赋值,如 int a=b=c=5; 是不合法的。

1
2
定义常量:#define identifier value
const type variable = value;

参数

实参是传递给函数的特定值;形参是函数中用于存储值的变量。

编译器

编译器报错的位置比真正的错误位置滞后一行;无法检测语义错误,即遵循了 C 规则,但输出结果错误。

使用调试器,通过跟踪程序执行步骤,记录每个变量,监视程序状态排错。

关键字和保留标识符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
auto	声明自动变量
break 跳出当前循环
case 开关语句分支
default 开关语句中的其它分支
char 声明字符型变量或函数返回值类型
const 定义常量
do 循环语句的循环体
float 声明单精度浮点型变量或函数返回值类型
double 声明双精度浮点型变量或函数返回值类型
else 条件语句否定分支(与 if 连用)
static 声明静态变量
extern 声明变量或函数是在其它文件或本文件的其他位置定义
for 循环语句
goto 无条件跳转语句
if 条件语句
int 声明整型变量或函数
long 声明长整型变量或函数返回值类型
short 声明短整型变量或函数返回值类型
return 子程序返回语句(参数可带可不带)
signed 声明有符号类型变量或函数
sizeof 计算数据类型或变量长度(即所占字节数)
switch 用于开关语句
typedef 用以给数据类型取别名
struct 声明结构体类型
union 声明共用体类型
enum 声明枚举类型
while 循环语句的循环条件
void 声明函数无返回值或无参数,声明无类型指针
volatile 说明变量在程序执行中可被隐含地改变
unsigned 声明无符号类型变量或函数
register 声明寄存器变量
continue 结束当前循环,开始下一轮循环
1
2
3
4
C99 新增关键字
_Bool 布尔值 _Complex 复数 _Imaginary 虚数 inline restrict
C11 新增关键字
_Alignas _Alignof _Atomic _Generic _Noreturn _Static_assert _Thread_local

基本数据类型及取值范围

注:ANSI 标准定义 int 是占 2 个字节,TC 是按 ANSI 标准的,它的 int 是占 2 个字节的。但是在 VC 里,一个 int 是占 4 个字节的。

类型转换

自动类型转换:如下图
强制类型转换:(数据类型) (表达式)

注:转换后不会改变原数据的类型及变量值,只在本次运算中临时性转换;强制转换后的运算结果不遵循四舍五入原则。

常用的算术转换是隐式地把值强制转换为相同的类型。编译器首先执行整数提升,如果操作数类型不同,则它们会被转换为上列层次中出现的最高层次的类型;常用的算术转换不适用于赋值运算符、逻辑运算符 && 和 ||。

printf 输出 doublefloat 都可以用 %f 混用,而 double 还可用 %lf。而 scanf 输入情况下 double 必须用 %lffloat 必须用 %f 不能混用。

格式符/占位符

sizeof 是内置运算符,以字节为单位给出指定类型的大小。C99 和 C11 提供 %zd 转换说明匹配 sizeof 的返回类型。一些不支持 C99 和 C11 的编译器可用 %u%lu 代替 %zd。默认右对齐,%-d 为左对齐。

转义序列

用于代表难以表示或无法输入的字符。

从 C90 开始,可以用十进制、八进制、十六进制形式表示字符常量。

运算符及优先级

注释:https://zh.cppreference.com/w/c/language/operator_precedence#cite_note-1

同一优先级的运算符,运算次序由结合方向所决定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
括号成员是老大;      // 括号运算符 []() 成员运算符.  ->

全体单目排老二; // 所有的单目运算符比如 ++、 --、 +(正)、 -(负) 、指针运算 *、&

乘除余三,加减四; // 这个"余"是指取余运算即 %

移位五,关系六; // 移位运算符:<< >> 关系运算符:> < >= <=

等与不等排行七; // == !=

位与异或和位或; // 位运算: 位与(&) 异或(^) 位或(|)

"三分天下"八九十;

逻辑与,逻辑或; // 逻辑运算符: && ||

十一十二紧挨着; // 注意顺序: && 高于 ||

条件只比赋值高, // 三目运算符优先级排到 13 位只比赋值运算符和 ","

逗号运算最低级! //逗号运算符优先级最低

循环

  1. 在没有循环结构的情况下,break 不能用在单独的 if-else 语句中。
  2. 在知道循环次数的情况下更适合使用 for 循环。
  3. 在不知道循环次数的情况下适合使用 while 或者 do-while 循环。
  4. 如果有可能一次都不循环应考虑使用 while 循环。
  5. 如果至少循环一次应考虑使用 do-while 循环。

switch 语句时还应注意以下几点:

  1. case 后的各常量表达式的值不能相同,否则会出现错误。
  2. case 子句后如果没有 break; 会一直往后执行直到遇到 break; 才会跳出 switch 语句。
  3. switch 后面的表达式语句只能是整型或者字符类型。
  4. case 后,允许有多个语句,可以不用 {} 括起来。
  5. casedefault 子句的先后顺序可以变动,而不会影响程序执行结果。
  6. default 子句可以省略不用。
  7. 自定义函数尽量放在 main 函数之前,若要放在 main 函数后面,需要在 main 函数之前先声明自定义函数。

编译器不优化情况下,for 循环里 ++i 比 i++ 要快一些。

存储类别

1
2
3
4
自动(auto
静态(static
寄存器(register
外部(extern

只有 局部自动变量 和 形式参数 可以作为 寄存器变量;

一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;

局部静态变量 不能定义为 寄存器变量。

1
2
3
4
auto 是局部变量的默认存储类, 限定变量只能在函数内部使用;
register 代表了寄存器变量,不在内存中使用;
static 是全局变量的默认存储类,表示变量在程序生命周期内可见;
extern 表示全局变量,即对程序内所有文件可见

VS2019 中,看任何变量的内存,将该变量取地址&,拖入内存窗口来看

微软的编译器设计,不同的变量之间有 8 个字节的保护空间(Mac 和 Linux是没有的)

字符串函数

获取数组长度

1
int length = sizeof(arr)/sizeof(arr[0]);

二维数组定义的时候,可以不指定行的数量,但是必须指定列的数量。

0x02 References

《C Primer Plus》
C语言入门这一篇就够了-学习笔记
C语言教程

0x03 写在最后

因为基础篇的知识点比较零散,好在断断续续写了几天总算写完了,细心的朋友可以发现我在文中指出了一些 C99 和 C11 的新特性与 C89 和 C90 作为区分,以便在不同的编译器上能够更好的排错。还有很多知识点没有提到,后期补充时会写在进阶篇和高级篇中,文中难免有纰漏之处,请读者不吝指出。

 Comments