C语言的一点想法

C语言基础深度解析

一、程序结构与编译过程

 1. 程序入口与函数结构

在C语言中,一个完整的程序必须有且只能有一个 main() 函数,它是程序的入口点。标准的 main() 函数形式为 int main(void) { ... }  。函数体要用花括号 {} 把代码块括起来,而且每个语句都要以分号结尾。

比如下面这个经典的示例:

 

#include <stdio.h>

int main(void) {

    printf("Hello, World!\n");

    return 0;

}

C语言程序的编译过程可以分为四个阶段:

- 预处理阶段:主要处理 #include 指令和宏定义。比如 #include <stdio.h> 就是把标准输入输出头文件的内容包含进来; #define PI 3.14 这样的宏定义会在预处理时进行替换。预处理后会生成 .i 文件。

- 编译阶段:这个阶段会对代码进行语法检查,检查代码是否符合C语言的语法规则,然后将其生成汇编代码,也就是 .s 文件。

- 汇编阶段:把汇编代码进一步转换为二进制目标文件,即 .o 文件。

- 链接阶段:将目标文件与库函数以及启动代码进行合并,最终生成可执行文件。

2. 头文件的作用

像 #include <stdio.h> 这样的语句,它的作用是引入标准输入输出函数,比如我们常用的 printf 函数就包含在这个头文件里。 <> 这种形式的头文件引用会从系统路径去查找,而 "" 这种形式的引用会优先从项目目录中查找头文件。

二、数据类型与内存管理

 1. 基本数据类型

C语言中有几种基本的数据类型,它们的大小、格式符以及取值范围如下表所示:

| 类型 | 大小(字节) | 格式符 | 范围(示例) |

|-------------|--------------|--------|-----------------------|

|  char  | 1 |  %c  | -128~127 或 0~255 |

|  int  | 4 |  %d  | -2³¹ ~ 2³¹-1 |

|  float  | 4 |  %f  | 精度6位小数 |

|  double  | 8 |  %lf  | 精度15位小数 |

|  long long  | 8 |  %lld  | -2⁶³ ~ 2⁶³-1 |

我们可以通过 sizeof 操作符来验证数据类型的大小,例如:

printf("int size: %zu\n", sizeof(int)); // sizeof返回字节数 

2. 变量与作用域

 - 全局变量:是定义在函数外部的变量,它的生命周期从程序开始一直到程序结束,而且默认初始化为0。

- 局部变量:定义在函数内部的变量就是局部变量,在没有初始化的情况下,它的值是随机的,它的作用域只局限在定义它的代码块内。

- 静态变量:使用 static 关键字修饰的变量,比如 static int x;  。静态变量的生命周期会延长到程序结束,不过它的作用域还是受到限制的,只在定义它的代码块内有效。

三、常量与类型转换

1. 常量的四种形式

- 字面常量:直接写在代码中的常量,比如整数 100 、字符 'A' 这样的。

-  const 修饰的常量:使用 const 关键字修饰的变量,例如 const int MAX = 100;  ,它实际上是一个只读变量,在编译期会对其进行检查。

- 宏定义常量:用 #define 指令来定义,像 #define MAX 100  ,在预处理阶段会进行文本替换,而且宏定义没有类型检查。

- 枚举常量:使用 enum 关键字定义的常量,比如 enum Color { RED, GREEN };  ,枚举常量默认从0开始递增。 

2. 类型转换规则

- 隐式转换:当小的数据类型和大的数据类型进行运算时,小的数据类型会自动提升为大的数据类型,比如 char 类型会自动转换为 int 类型。不过在这个过程中,可能会丢失一些精度。

- 显式转换:通过强制类型转换运算符来进行转换,例如 (int)3.14  ,这种转换会直接截断小数部分,而不是进行四舍五入。 

四、运算符与控制结构

1. 易错运算符

 - 自增/减运算符: i++ 是先使用 i 的值,然后再对 i 进行自增操作; ++i 则是先对 i 进行自增操作,然后再使用 i 的值。

- 逻辑短路运算符: && 和 || 这两个逻辑运算符存在短路现象,如果左边操作数已经能够确定整个表达式的结果,那么右边的操作数就不会再执行了。

2. 分支与循环

-  switch 语句的陷阱:在 switch 语句中,每个 case 后面都需要加上 break 语句,否则会出现“穿透”现象,即执行完当前 case 的代码后,会继续执行后面 case 的代码。

-  for 循环的优化:在使用 for 循环时,如果循环条件中存在一些不变的计算,应该把这些计算移到循环外面,例如 for(int i=0; i<strlen(s); i++)  这种写法效率就比较低,因为 strlen(s) 每次循环都会重新计算。

五、进阶技巧与常见问题

1. 内存对齐

在定义结构体时,结构体成员会按照最大数据类型进行对齐,这样做的目的是为了提升内存访问的效率。例如:

 struct Example {

    char a; // 1字节,补3字节对齐

    int b; // 4字节

}; // 总大小=8字节

2. 未定义行为(UB)

 - 访问未初始化的局部变量是一种未定义行为,比如 int x; printf("%d", x);  ,由于 x 没有初始化,它的值是不确定的,所以打印出来的结果也是不可预测的。

- 数组越界或者指针的非法解引用同样属于未定义行为,这种情况很可能会导致程序崩溃。

 3. 调试与优化

- 在编译C语言程序时,可以使用 -Wall 编译选项,它会启用所有的警告信息,帮助我们捕捉代码中潜在的错误。

-  volatile 关键字的作用是防止编译器对某些变量的访问进行优化,特别是在访问硬件寄存器时,使用 volatile 关键字可以确保每次都从内存中读取变量的值,而不是使用编译器优化后的缓存值

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值