doc: update c.md document (#913)

This commit is contained in:
jaywcjlove 2025-01-01 21:33:24 +08:00
parent d7df8bc6fe
commit e7b8958368

613
docs/c.md
View File

@ -7,7 +7,6 @@ C 备忘清单
----
### hello.c
<!--rehype:wrap-class=row-span-2-->
```c
#include <stdio.h>
@ -35,40 +34,51 @@ $ ./hello
<!--rehype:wrap-class=row-span-2-->
```c
int myNum = 15;
int myNum = 15; // 定义并初始化变量 myNum
int myNum2; // 声明变量 myNum2
myNum2 = 15; // 初始化变量 myNum2
int myNum2; // 声明变量 myNum2
// 变量声明后第一次赋值我们称为初始化
// 如果 初始化 和 赋值 在同一行
// 那么我们可以直接称为 定义变量 myNum2
myNum2 = 15;
int myNum3 = 15; // 定义并初始化变量 myNum3
myNum3 = 10; // 重新赋值 myNum3
```
int myNum3 = 15; // myNum3 值为 15
myNum3 = 10; // 现在 myNum3 值为 10
定义不同类型的变量
float myFloatNum = 5.99; // 浮点数
char myLetter = 'D'; // 字符
```c
// 定义并初始化浮点数变量
float myFloatNum = 5.99;
// 定义并初始化字符变量
char myLetter = 'D';
```
int x = 5;
int y = 6;
int sum = x + y; // 添加变量相加
// 声明多个变量
变量相加
```c
int x = 5, y = 6;
int sum = x + y; // 变量相加
```
声明并初始化多个变量
```c
int x = 5, y = 6, z = 50;
int a, b, c = 10;
```
//仅声明变量不初始化
仅声明变量不初始化
```c
int result;
// 如果未初始化,变量的值是未定义的,使用它会导致错误的结果。
// 此时,变量的值是随机的,因此在使用该变量之前必须进行初始化。
result = result + 10; // 错误:未初始化的变量 result 会导致不可预测的结果
// 部分编译器会输出 Warning警告未初始化的变量可能导致未定义行为。
// 未初始化的变量 result 会导致不可预测的结果
result = result + 10; // 错误:未初始化的变量
// 部分编译器会警告未初始化的变量可能导致未定义行为
```
### 常量 Constants
<!--rehype:wrap-class=row-span-2-->
常量在 C 语言中我们一般理解为不能被改变的值,活用常量与符号常量,可以使代码更加清晰和安全
常量是不能被改变的值,使用常量可以使代码更清晰和安全。
```c
const int minutesPerHour = 60;
@ -81,37 +91,53 @@ const float PI = 3.14;
const int BIRTHYEAR = 1980;
```
说明与补充:
1. **常量命名规范**:常量通常使用全大写字母,多个单词之间用下划线分隔(例如 BIRTHYEAR、MAX_LENGTH
#### **命名规范**
<!--rehype:style=text-align: left;-->
2. **`#define``const`**
- **`#define`**:宏常量通常在预处理阶段进行替换,不进行类型检查;
- **`const`**:类型安全的常量,编译器可以检查类型,一般更推荐使用 `const`
常量通常使用全大写字母,单词间用下划线分隔(如 BIRTHYEAR、MAX_LENGTH
3. **数组大小**:可以使用 `const` 来定义数组的常量大小,这样编译器会将其作为编译时常量处理
#### **数组大小**
<!--rehype:style=text-align: left;-->
使用 `const` 定义数组大小,编译器将其作为编译时常量处理。
#### **`#define``const`**
<!--rehype:style=text-align: left;-->
- `#define`:宏常量在预处理阶段替换,不进行类型检查
- `const`:类型安全的常量,编译器可检查类型,推荐使用
#### **注意事项**
- `const` 常量在定义时必须初始化,否则会导致编译错误。
- `const` 常量的值不能被修改,任何尝试修改 `const` 常量的操作都会导致编译错误。
- 使用 `const` 常量可以提高代码的可读性和可维护性,避免魔法数字的使用。
### 注释
```c
// 这是一个注释
printf("Hello World!"); // 这是一个注释
// 这是一个单行注释
printf("Hello World!"); // 行内注释
/*
多行注释:用于注释跨多行的内容
上面的代码将打印出 Hello World! 到屏幕上
多行注释:
用于注释跨多行的内容
注意:多行注释不能嵌套,否则会导致编译错误
*/
```
**注意**:
单行注释`//`可以嵌套,`////////这种注释也是对的`
但行内注释应避免过长,以免影响代码可读性
多行注释不能嵌套,否则会导致编译错误(详见下文 [### Warning 和 Error](#warning-和-error)
**注意**:
- 单行注释 `//` 可以嵌套,如 `/////`
- 行内注释应避免过长,以免影响代码可读性。
- 多行注释不能嵌套,否则会导致编译错误。
```c
/* 这是一个多行注释的开始
/* 这是嵌套的多行注释C语言不支持 */
/* 嵌套的多行注释C语言不支持 */
*/
```
<!--rehype:style=background:#ff000030;-->
### 打印文本
@ -126,21 +152,34 @@ printf("Value = %f", f);
short a = 0b1010110; // 2 进制数字
int b = 02713; // 8 进制数字
long c = 0X1DAB83; // 16 进制数字
//变量a和c分别为short和long型所以输出必须加上对应的修饰符h和l
```
// 以 8 进制形式输出
变量a和c分别为 `short``long` 型,所以输出必须加上对应的修饰符 `h``l`
#### 以 8 进制形式输出
```c
printf("a=%ho, b=%o, c=%lo\n", a, b, c);
// 输出 => a=126, b=2713, c=7325603
```
// 以 10 进制形式输出
#### 以 10 进制形式输出
```c
printf("a=%hd, b=%d, c=%ld\n", a, b, c);
// 输出 => a=86, b=1483, c=1944451
```
// 以 16 进制形式输出(字母小写)
#### 以 16 进制形式输出(字母小写)
```c
printf("a=%hx, b=%x, c=%lx\n", a, b, c);
// 输出 => a=56, b=5cb, c=1dab83
```
// 以 16 进制形式输出(字母大写)
#### 以 16 进制形式输出(字母大写)
```c
printf("a=%hX, b=%X, c=%lX\n", a, b, c);
// 输出 => a=56, b=5CB, c=1DAB83
```
@ -169,73 +208,75 @@ printf("%-9d %-9d %-9d\n", d1, d2, d3);
34 0 23
```
`%-9d` 中,`d` 表示以 `10` 进制输出,`9` 表示最少占 `9` 个字符的宽度,宽度不足以空格补齐,`-` 表示左对齐,如果不使用左对齐则默认右对齐
解释:`%-9d``d` 表示十进制输出,`9` 表示最少占 9 个字符宽度,`-` 表示左对齐,不使用 `-` 则默认右对齐。
对于整型数据:
对于整型数据,假设:
```c
int a = 12345;
printf(“%md”,a);
printf("%md", a);
```
1. 若 m <= 实际数据的宽度,则按实际情况输出
2. 若 m > 实际数据的宽度,则在实际数据的左边用空格补齐
3. `printf(“%0md”,a);` 则实际的结果不够 m 位的在数据的左边用0补齐
对于浮点型数据,**数据的宽度=整数部分的位数+小数点+小数部分的宽度**
假设:
- 若 `m <=` 实际数据宽度,则按实际情况输出。
- 若 `m >` 实际数据宽度,则在左边用空格补齐。
- `printf("%0md", a);` 则在左边用 `0` 补齐。
对于浮点型数据:
```c
float a = 1.2345;
printf(“%m.nf”,a); //m --整个数据的宽度n--小数位数
printf("%m.nf", a);
// m -- 整个数据宽度n -- 小数位数
```
1. 实际小数位数>n截去小数右边多余的小数截去的第一位要注意四舍五入
2. 实际小数位数< n在小数的最后补0
3. 若m省略则写作%.n ,整数部分按照实际输出,小数部分按照以上两个规则进行
4. m < n+1自动突破宽度为m的限制按照实际数据进行输出
5. m > n+1整个数据的最左边补空格
- 实际小数位数 `> n`,截去多余小数,注意四舍五入。
- 实际小数位数 `< n`,在小数最后补 `0`
- 若 `m` 省略则写作 `%.n`,整数部分按实际输出,小数部分按以上规则。
- 若 `m < n+1`,自动突破宽度限制,按实际数据输出。
- 若 `m > n+1`,左边补空格。
### 字符串 Strings
在C语言中字符串是以 `\0` 结尾的字符数组,而不是一种单独的数据类型
可以通过字符数组来表示字符串
### 字符串 (Strings)
在 C 语言中,字符串是以 `\0` 结尾的字符数组,而不是一种单独的数据类型。可以通过字符数组来表示字符串。
#### 定义并打印字符串
定义并打印字符串
```c
char greetings[] = "Hello World!";
printf("%s", greetings);
```
访问字符串中的字符
#### 访问字符串中的字符
```c
char greetings[] = "Hello World!";
printf("%c", greetings[0]);
//这里我们访问字符串 greetings 的第一个字符'H'
```
修改字符串中的字符
访问字符串 `greetings` 的第一个字符 `H`
#### 修改字符串中的字符
```c
char greetings[] = "Hello World!";
greetings[0] = 'J'; //// 修改第一个字符为 'J'
greetings[0] = 'J'; // 修改第一个字符为 'J'
printf("%s", greetings);
// 输出 "Jello World!"
```
另一种创建字符串的方法
#### 另一种创建字符串的方法
```c
char greetings[] = {'H','e','l','l','\0'};
printf("%s", greetings);
// 输出 "Hell!"
// 输出 "Hell"
```
C语言中**没有**`String`类型,字符串是由字符数组(Array)`char[]`来表示的,且必须以 \0 结尾以标识字符串的结束
**注意**C 语言中没有 `String` 类型,字符串是由字符数组 `char[]` 表示的,且必须以 `\0` 结尾以标识字符串的结束。
### 条件判断
<!--rehype:wrap-class=row-span-2-->
```c
int time = 20;
@ -245,6 +286,7 @@ if (time < 18) {
printf("晚上好!");
}
// 输出 -> "晚上好!"
int time = 22;
if (time < 10) {
printf("早上好!");
@ -265,25 +307,40 @@ if (time > 8) {
// 输出 -> "中午好!"
```
#### 说明
- `if` 语句用于根据条件执行代码块。
- `else` 语句在 `if` 条件不满足时执行。
- `else if` 语句用于检查多个条件。
- 可以嵌套 `if` 语句以检查多个条件。
### 三元运算符
<!--rehype:wrap-class=col-span-2-->
三元运算符(? :)是一种简洁的条件判断方式,常用于根据条件选择表达式的值,由三个部分组成:
- 一个条件表达式
三元运算符(`? :`)是一种简洁的条件判断方式,用于根据条件选择表达式的值。由三个部分组成:
- 条件表达式
- 条件为真时的结果
- 条件为假时的结果
基本语法:`(condition) ? expression1 : expression2;`
如果`condition`为真,则返回`expression1`否则返回`expression2`
基本语法:`(条件) ? 表达式1 : 表达式2;`
如果 `条件` 为真,则返回 `表达式1`,否则返回 `表达式2`
#### 示例
```c
int time = 20;
(time < 18) ? printf("再会") : printf("晚上好");
(time < 18) ? printf("再会")
: printf("晚上好!");
// 输出 -> "晚上好!"
```
三元运算符可以嵌套使用,但嵌套层级太多会导致代码可读性下降,不建议在实际场景使用
嵌套使用示例(不建议过多嵌套):
```c
int time = 22;
printf((time < 10) ? "早上好" : (time < 20) ? "再会" : "晚上好");
printf((time < 10) ? "早上好"
: (time < 20) ? "再会"
: "晚上好!");
// 输出 -> "晚上好!"
```
@ -295,30 +352,34 @@ int day = 4;
switch (day) {
case 3: printf("周三"); break;
case 4: printf("周四"); break;
default:
printf("期待周末");
default: printf("期待周末");
}
// 输出 -> "周四" (day 4)
/* 解释:
switch 语句会根据表达式 day 的值来跳转到匹配的 case 标签
匹配到 case 4 时,会执行相应的代码并通过 break 跳出 switch 语句
如果没有匹配到任何 case则会执行 default 语句(如果存在)
*/
// 输出 -> "周四"
```
注意:
1. switch 后面括号中的表达式可以是整型、字符型和枚举型
2. 在`case`后的各常量表达式的值不能相同
3. 在`case`后,允许有多个语句,可以不用{ }括起来,而整个 switc h结构一定要有一对{}
4. 各`case``default`语句的先后顺序可以改变,不影响程序执行结果
5. `break`语句用于结束 switch如果没有`break`,程序会穿透到下一个`case`继续执行,例如
#### 说明
- `switch` 语句根据表达式的值跳转到匹配的 `case` 标签。
- 匹配到 `case` 后执行相应代码,并通过 `break` 跳出 `switch`
- 如果没有匹配到任何 `case`,则执行 `default` 语句(如果存在)。
#### 注意事项
- `switch` 表达式可以是整型、字符型和枚举型。
- `case` 后的常量表达式值不能相同。
- `case` 后可以有多个语句,不需要 `{ }` 括起来。
- `case``default` 语句的顺序不影响程序执行结果。
- `break` 语句用于结束 `switch`,如果没有 `break`,程序会继续执行下一个 `case`
#### 示例
```c
int day = 3;
switch (day) {
case 3: printf("周三");
case 4: printf("周四"); break;
default:
printf("期待周末");
default: printf("期待周末");
}
// 输出 -> "周三周四"
```
@ -332,14 +393,53 @@ while (i < 5) {
printf("%d\n", i);
i++;
}
/*解释:
在 while 循环中,首先检查条件 i < 5 是否为真
如果为真,程序进入循环体,执行打印操作并增加 i 的值
循环会继续进行,直到 i 达到 5此时条件 i < 5 不再为真循环结束
*/
// 输出 -> 0 1 2 3 4
```
**注意**:不要忘记在循环体内修改循环条件中的变量(如 i++),否则循环将永远执行下去,成为“死循环”
#### 解释
- `while` 循环首先检查条件 `i < 5` 是否为真。
- 如果为真,程序进入循环体,执行打印操作并增加 `i` 的值。
- 循环会继续进行,直到 `i` 达到 5此时条件 `i < 5` 不再为真,循环结束。
#### 打印 1 到 10 的数字
```c
int i = 1;
while (i <= 10) {
printf("%d\n", i);
i++;
}
// 输出 -> 1 2 3 4 5 6 7 8 9 10
```
#### 打印偶数
```c
int i = 0;
while (i <= 10) {
if (i % 2 == 0) {
printf("%d\n", i);
}
i++;
}
// 输出 -> 0 2 4 6 8 10
```
#### 无限循环(需手动终止)
```c
int i = 0;
while (1) {
printf("无限循环\n");
i++;
if (i == 5) break; // 添加条件以退出循环
}
// 输出 -> 无限循环 (打印 5 次)
```
### Do/While 循环
@ -350,33 +450,55 @@ do {
printf("%d\n", i);
i++;
} while (i < 5);
/*解释:
在 do/while 循环中,循环体至少执行一次,因为条件检查是在循环体执行之后进行的
首先执行 do 中的代码,然后检查条件 i < 5 是否为真
如果为真,继续执行循环;如果为假,则退出循环
/*
// 输出 -> 0 1 2 3 4
```
**注意**:与`while`循环不同,`do/while`循环会确保至少执行一次循环体,即使条件一开始就为假
例如,以下代码虽然条件`i < 5`初始时为假,但仍然会执行一次:
```C
#### 解释
- `do/while` 循环至少执行一次循环体,因为条件检查在循环体执行之后进行。
- 首先执行 `do` 中的代码,然后检查条件 `i < 5` 是否为真。
- 如果为真,继续执行循环;如果为假,则退出循环。
#### 打印从 5 开始的数字,直到条件不满足
```c
int i = 5;
do {
printf("%d\n", i); // 输出 5
i++;
} while (i < 5);
// 输出 -> 5
```
#### 计算 1 到 10 的和
```c
int i = 1;
int sum = 0;
do {
sum += i;
i++;
} while (i <= 10);
printf("Sum: %d\n", sum);
// 输出 -> Sum: 55
```
### For 循环
语法:
```C
for(表达式 1;表达式 2;表达式 3){
```c
for (表达式1; 表达式2; 表达式3) {
循环体语句;
}
```
表达式 1设置初始条件只执行一次为 0 个或多个变量设置初值
表达式 2是循环条件表达式用来判定是否继续循环。在每次执行循环体之前要先执行表达式 2然后再决定是否继续执行循环
表达式 3作为循环的调整比如是循环体变量增值它是执行循环体语句之后再执行
- 表达式1设置初始条件只执行一次。
- 表达式2循环条件表达式每次循环前检查。
- 表达式3循环体执行后的调整操作。
示例:
```c
int i;
@ -384,40 +506,28 @@ int i;
for (i = 0; i < 5; i++) {
printf("%d\n", i);
}
/* 输出:
0
1
2
3
4
*/
// 输出 -> 0 1 2 3 4
```
**注意**
1. for 语句的三个表达式不是必须的
2. 当条件表达式(表达式 2)的值为假(为 0)时 for 循环语句就结束
3. 可以在循环体内执行`break`,`continue`,`goto`语句
4. 若表达式 2 是空的,表明表达式 2 的值一直是真,即死循环
如:`for(;;) //死循环语句 相当于while(1)`
#### 注意事项
**变体**
- `for(i = m; i < n; i++)`会从 i = m 开始到 i = n-1循环 n - m 次
- `for(i = m; i <= n; i++)`会从 i = m 到 i = n循环 n - m + 1 次
- `for` 语句的三个表达式不是必须的。
- 当条件表达式表达式2为假时`for` 循环结束。
- 可以在循环体内使用 `break``continue``goto` 语句。
- 如果表达式2为空则表示无限循环`for(;;)` 相当于 `while(1)`
#### 变体
- `for (i = m; i < n; i++)``i = m` 开始到 `i = n-1`,循环 `n - m` 次。
- `for (i = m; i <= n; i++)``i = m``i = n`,循环 `n - m + 1` 次。
### 跳出循环 (Break/Continue/Goto)
<!--rehype:wrap-class=row-span-3-->
#### `break` 语句
### 跳出循环(中断语句) Break/Continue/Goto
<!--rehype:wrap-class=row-span-2-->
`break`语句:跳出本层循环,执行循环后的语句
`continue`语句:跳出本次循环,执行下一次循环
`goto`语句:跳出到指定的标号位
跳出当前循环或 `switch` 语句,执行后续代码。
**1.`break`语句**:
`break`用于跳出当前的循环或`switch`语句,直接跳到循环或`switch`语句之后的下一条语句
通常用于在特定条件下提前结束循环
```c
int i;
@ -427,16 +537,13 @@ for (i = 0; i < 10; i++) {
}
printf("%d\n", i);
}
//输出:
//0 1 2 3 4(换行符使用空格代替)
// 输出 -> 0 1 2 3
```
`i`等于 4 时,`break`语句使得循环提前结束,因此没有打印出 4 及之后的值
#### `continue` 语句
**2.`continue`语句**:
跳过当前循环的剩余语句,直接进入下一次循环。
`continue`用于跳过当前循环中的剩余语句(在for循环中相当于直接执行表达式3),直接进入下一次循环的条件判断阶段
通常用于跳过某些特定的循环迭代
```c
int i;
@ -446,17 +553,14 @@ for (i = 0; i < 10; i++) {
}
printf("%d\n", i);
}
//输出:
//0 1 2 3 5 6 7 8 9(换行符使用空格代替)
// 输出 -> 0 1 2 3 5 6 7 8 9
```
`i`等于 4 时,`continue`会跳过打印语句,因此 4 被跳过,不会输出
#### `goto` 语句
**3.`goto`语句**:
无条件跳转到指定标签位置。
`goto`用于无条件跳转到程序中的指定标签位置
```C
```c
int i = 0;
while (i < 10) {
@ -469,56 +573,20 @@ while (i < 10) {
skip:
printf("Exited the loop at i = %d\n", i);
/* 输出:
0
1
2
3
Exited the loop at i = 4
*/
// 输出 -> 0 1 2 3 Exited the loop at i = 4
```
`goto skip`语句将程序的控制流跳转到标签`skip`处,导致循环在`i = 4`时直接结束,并输出`Exited the loop at i = 4`
#### 注意事项
**注意**:
1. `label标签`必须是在当前函数内定义的标签。标签的命名规则与变量的命名规则相同但标签不能与C语言的关键字冲突
2. `goto`语句通常用于跳转到同一函数内的某个位置,但也可以跳转到同一文件中的其他函数内的标签(不推荐)
3. goto语句应慎用因为过多或滥用goto语句可能会导致代码逻辑混乱、难以维护和阅读。通常情况下可以使用循环或条件语句等结构化的控制流语句来代替goto语句
- 标签必须在当前函数内定义,命名规则与变量相同。
- `goto` 语句应慎用,避免代码逻辑混乱,通常使用循环或条件语句代替。
### 数组 Arrays
<!--rehype:wrap-class=row-span-2-->
#### 设置数组大小
```c
int myNumbers[] = {25, 50, 75, 100};
printf("%d", myNumbers[0]);
// 输出 25
```
更改数组元素
```c
int myNumbers[] = {25, 50, 75, 100};
myNumbers[0] = 33;
printf("%d", myNumbers[0]);
```
循环遍历数组
```c
int myNumbers[] = {25, 50, 75, 100};
int i;
for (i = 0; i < 4; i++) {
printf("%d\n", myNumbers[i]);
}
```
设置数组大小
```c
// 声明一个由四个整数组成的数组:
// 声明一个由四个整数组成的数组
int myNumbers[4];
// 添加元素
myNumbers[0] = 25;
myNumbers[1] = 50;
@ -566,6 +634,35 @@ switch(day){
}
```
### 数组 Arrays
#### 定义和访问数组元素
```c
int myNumbers[] = {25, 50, 75, 100};
printf("%d", myNumbers[0]); // 输出 25
```
#### 更改数组元素
```c
int myNumbers[] = {25, 50, 75, 100};
myNumbers[0] = 33;
printf("%d", myNumbers[0]); // 输出 33
```
#### 循环遍历数组
```c
int myNumbers[] = {25, 50, 75, 100};
int i;
for (i = 0; i < 4; i++) {
printf("%d\n", myNumbers[i]);
}
// 输出 -> 25 50 75 100
```
### 用户输入
```c
@ -597,39 +694,27 @@ printf("Hello %s.", firstName);
### 内存地址
创建变量时,会为该变量分配一个内存地址
创建变量时,会为该变量分配一个内存地址
```c
int myAge = 43;
printf("%p", &myAge);
// 输出0x7ffe5367e044
// 输出 myAge 的内存地址,例如0x7ffe5367e044
```
要访问它,请使用引用运算符 (`&`)
要访问变量的内存地址,请使用引用运算符 (`&`)。
### 创建指针
```c
int myAge = 43; // 一个 int 变量
printf("%d", myAge); // 输出 myAge(43)的值
// 输出 myAge 的内存地址0x7ffe5367e044
printf("%p", &myAge);
```
### 指针变量
<!--rehype:wrap-class=col-span-2-->
```c
int myAge = 43; // 一个 int 变量
int* ptr = &myAge; // 名为 ptr 的指针变量,用于存储 myAge 的地址
int myAge = 43; // 一个 int 变量
int *ptr = &myAge; // 创建指向 myAge 的指针
printf("%d\n", myAge); // 输出 myAge (43) 的值
printf("%p\n", &myAge); // 输出 myAge 的内存地址0x7ffe5367e044
printf("%p\n", ptr); // 用指针0x7ffe5367e044输出myAge的内存地址
printf("%d\n", myAge); // 输出 myAge 的值 -> 43
printf("%p\n", &myAge); // 输出 myAge 的内存地址例如0x7ffe5367e044
printf("%p\n", ptr); // 输出指针 ptr 的值(即 myAge 的内存地址)
printf("%d\n", *ptr); // 通过指针访问 myAge 的值 -> 43
```
### 取消引用
@ -645,6 +730,18 @@ printf("%p\n", ptr);
printf("%d\n", *ptr);
```
### 指针变量
<!--rehype:wrap-class=col-span-2-->
```c
int myAge = 43; // 一个 int 变量
int* ptr = &myAge; // 名为 ptr 的指针变量,用于存储 myAge 的地址
printf("%d\n", myAge); // 输出 myAge (43) 的值
printf("%p\n", &myAge); // 输出 myAge 的内存地址0x7ffe5367e044
printf("%p\n", ptr); // 用指针0x7ffe5367e044输出myAge的内存地址
```
运算符
---
@ -987,46 +1084,78 @@ int main(void) {
### Warning 和 Error
<!--rehype:wrap-class=row-span-2-->
在 C 语言中警告Warning和错误Error是编译器用于标识代码潜在问题或阻止代码编译的两种机制
在 C 语言中警告Warning和错误Error是编译器用于标识代码潜在问题或阻止代码编译的机制。
#### 警告
**警告**
警告提示代码中可能存在的问题,但不会阻止代码编译。处理警告可以提升代码质量和可移植性。
**常见警告示例**
1. 未使用的变量
`int x; printf("%d",x);`
2. 类型隐式转换(可能导致数据丢失)
`int x = 3.14; //浮点数被隐式转换`
`int a = 2147483647 + 1; //可能溢出`
3. 函数声明与定义不匹配
#### **常见警告示例**
**错误**
警告提示代码中可能存在的问题,但不会阻止代码编译。处理警告可以提升代码质量和可移植性。
未使用的变量
```c
int x; printf("%d", x);
```
类型隐式转换(可能导致数据丢失)
```c
int x = 3.14; // 浮点数被隐式转换
int a = 2147483647 + 1; // 可能溢出
```
函数声明与定义不匹配
#### 错误
错误会阻止代码编译,必须修复才能继续编译。
**常见错误示例**
1. 语法错误(如缺少分号)
`int x=1 `
2. 函数定义冲突
```C
语法错误(如缺少分号)
```c
int x = 1
```
函数定义冲突
```c
void func(int);
void func(double);
```
3. 函数或变量未定义
`y = 10; printf("%d",y);`
4. 头文件缺失或冲突
`#include <unknown.h>`
**使用编译器指令控制警告和错误**:
1. 抑制警告
函数或变量未定义
```c
y = 10; printf("%d", y);
```
头文件缺失或冲突
```c
#include <unknown.h>
```
### 使用编译器指令控制警告和错误
<!--rehype:wrap-class=col-span-2-->
#### 抑制警告
可以使用编译器选项来关闭特定的警告,例如在 GCC 中:
```GCC
```sh
gcc -w file.c # 禁用所有警告
gcc -Wall file.c # 启用所有常见警告
gcc -Werror file.c # 将警告视为错误
```
2. 使用 #pragma 控制警告
在某些编译器中,可以使用`#pragma`指令启用或禁用警告
```C
#### 使用 `#pragma` 控制警告
在某些编译器中,可以使用 `#pragma` 指令启用或禁用警告:
```c
#include <stdio.h>
#pragma warning(disable : 4996) // 禁用警告(适用于 MSVC 编译器)
@ -1036,7 +1165,8 @@ int main() {
}
```
**总结**
#### 总结
| 区别点 | Warning警告 | Error错误 |
|------------------|----------------------------------------|----------------------------------------|
| 严重程度 | 程序可继续编译,但可能存在隐患 | 编译无法完成,必须修复 |
@ -1045,7 +1175,6 @@ int main() {
| 修复必要性 | 可选择修复,但建议修复以避免潜在问题 | 必须修复才能继续编译 |
| 编译器选项调整 | 可以忽略或转换为错误(如 `-Werror` | 无法调整,必须修复 |
函数
----