C 语言是一种广泛使用的计算机编程语言,由美国贝尔实验室的丹尼斯・里奇(Dennis Ritchie)在 20 世纪 70 年代初设计开发。它兼具高级语言的易用性和低级语言的直接硬件访问能力,是许多系统软件、应用程序和编程语言的基础,至今仍在编程领域占据重要地位。
一,C语言概述
1、C 语言的发展背景
起源:1972 年,丹尼斯・里奇在开发 UNIX 操作系统时,为了解决 B 语言的局限性(如缺乏类型检查、难以处理复杂数据结构)而设计了 C 语言。UNIX 系统的大部分代码后来都用 C 语言重写,二者相互促进,推动了彼此的普及。
标准化:1989 年,美国国家标准协会(ANSI)发布了 C 语言的第一个标准(ANSI C,即 C89);1990 年,国际标准化组织(ISO)采纳该标准,命名为 C90。此后又经历了多次修订,形成了 C99、C11、C17 等版本,逐步增加了新特性(如布尔类型、变长数组、泛型宏等)。
2、C 语言的核心特点
结构化编程
支持顺序、选择(if-else)、循环(for、while)等控制结构,鼓励模块化设计(通过函数拆分程序),使代码更清晰、易维护。
高效性
语法简洁,编译后生成的机器码执行效率接近汇编语言,适合对性能要求高的场景(如操作系统、嵌入式系统)。允许直接操作内存(通过指针),但也增加了内存泄漏、越界访问等风险。
可移植性
标准 C 语言代码在不同操作系统(如 Windows、Linux、macOS)和硬件平台上只需少量修改即可编译运行,这也是其广泛应用的重要原因
功能丰富且灵活
支持基本数据类型(int、float、char 等)和复杂数据结构(数组、结构体、枚举、联合体)。
提供预处理指令(如#include
、#define
),方便代码复用和条件编译。允许直接调用汇编代码,进一步扩展了底层操作能力。
3、C 语言的应用领域
系统软件:操作系统(如 UNIX、Linux 内核)、编译器、数据库管理系统(如 MySQL 部分模块)等。
嵌入式系统:智能设备(如路由器、智能家居)、汽车电子、工业控制等,因 C 语言对硬件资源占用少、效率高。
应用软件:早期的许多桌面应用(如部分图形界面程序)、游戏引擎核心模块等。
编程语言基础:C++、Java、Python 等许多语言的设计都借鉴了 C 语言的语法,掌握 C 语言有助于理解其他语言的底层逻辑。
4、学习 C 语言的意义
理解计算机底层工作原理(如内存管理、指针操作),建立编程的 “底层思维”。
为学习更复杂的语言(如 C++、Go)或领域(如操作系统开发、逆向工程)打下基础。
培养严谨的编程习惯,因为 C 语言对语法和内存管理的要求较严格,能帮助开发者规避常见错误。
二,基本语法结构
1、预处理指令:#include
与 #define
预处理指令是在程序编译前由预处理器执行的命令,以#
开头,且不需要分号结尾。它们用于处理代码结构、定义常量或引入外部资源。
#include:引入头文件
功能:将指定头文件的内容插入到当前代码中,用于获取库函数声明、宏定义或类型定义。
两种格式:
#include <头文件名>
:用于引入系统标准库头文件(如stdio.h
、stdlib.h
),编译器会到系统默认路径查找。
#include "头文件名"
:用于引入用户自定义头文件(如myfunc.h
),编译器优先在当前项目路径查找,找不到再去系统路径。
#include <stdio.h> // 引入标准输入输出库,使用printf、scanf等函数
#include "calc.h" // 引入自定义的计算相关头文件
#define
:定义宏(符号常量或代码片段)
功能:用一个标识符替代一段文本,分为无参数宏和带参数宏。
无参数宏(符号常量):
格式:#define 标识符 替换文本
作用:定义常量,提高代码可读性和可维护性(修改时只需改一处)。
#define PI 3.1415926 // 用PI代替3.1415926
#define MAX_SIZE 100 // 用MAX_SIZE代替100
// 使用示例
float area = PI * r * r; // 等价于3.1415926 * r * r
int arr[MAX_SIZE]; // 等价于int arr[100];
带参数宏(类似函数的代码片段):
格式:#define 宏名(参数) 替换文本
作用:实现简单逻辑的代码复用,比函数调用更高效(无函数调用开销),但需注意优先级问题。
#define ADD(a, b) (a + b) // 定义加法宏
#define SQUARE(x) ((x) * (x)) // 加括号避免优先级错误
// 使用示例
int sum = ADD(3, 5); // 等价于int sum = (3 + 5);
int square = SQUARE(4); // 等价于int square = ((4) * (4));
注意:宏替换是简单的文本替换,不进行语法检查,定义时需谨慎处理运算符优先级(多用括号)。
2、主函数 main()
:程序的入口
C 程序必须包含且只能包含一个main()
函数,它是程序执行的起点(操作系统通过调用main()
启动程序)。
基本格式:
int main() {
// 函数体:程序逻辑
return 0; // 返回0表示程序正常结束
}
参数与返回值:
返回值类型为int
:用于向操作系统返回程序的执行状态(0
表示成功,非0
表示异常,具体含义由系统定义)。
带参数的main()
(用于接收命令行参数):
基本格式:
int main(int argc, char *argv[]) {
// argc:命令行参数的数量(至少为1,包含程序名)
// argv:参数数组,argv[0]是程序名,argv[1]~argv[argc-1]是实际参数
return 0;
}
执行流程
程序从main()
的第一行代码开始执行,依次执行函数体内的语句,直到遇到return
语句或函数结束,程序终止。
3、语句:以分号 ;
结尾
语句是 C 程序中执行具体操作的指令,必须以分号;
作为结束标志,分号是语句的 “终止符”。
简单语句:
- 赋值语句:
int a = 5;
- 函数调用语句:
printf("Hello");
- 空语句:
;
(什么也不做,有时用于循环体占位)。
复合语句:
由多个简单语句组成,通常被代码块({}
)包裹,但仍需注意内部语句的分号。
int x = 10;
if (x > 5) {
printf("x大于5\n"); // 内部语句需分号
x = 0; // 内部语句需分号
} // 代码块
注意:遗漏分号是常见错误,会导致编译器报错(提示 “语法错误”)。
代码块可以嵌套(花括号内再包含花括号)。
良好的缩进习惯(如每个代码块内的语句缩进 4 个空格)能提高代码可读性。
三,核心概念
1. 数据类型
基本类型
基本类型是 C 语言中最基础的数据存储形式,直接对应硬件层面的存储单元。
整型:用于存储整数,根据取值范围和占用内存分为以下几种:
int
:最常用的整数类型,通常占用 4 字节(32 位),取值范围约为 - 21 亿~21 亿。short
(或short int
):短整型,占用 2 字节(16 位),取值范围约为 - 32768~32767,适合存储较小的整数以节省内存。long
(或long int
):长整型,通常占用 4 字节(32 位)或 8 字节(64 位),取值范围更大。char
:字符型,本质是 1 字节(8 位)的整数,用于存储 ASCII 字符(如'A'
的 ASCII 值为 65),也可作为最小的整数类型使用。
示例:
int num = 100;
short year = 2023;
long population = 1400000000;
char grade = 'B'; // 等价于 char grade = 66;
浮点型:用于存储带小数的数值,支持科学计数法表示。
float
:单精度浮点型,占用 4 字节,精度约为 6~7 位有效数字,赋值时需加f
后缀(如3.14f
)。double
:双精度浮点型,占用 8 字节,精度约为 15~17 位有效数字,是默认的浮点类型(如3.14
默认为double
)。
示例:float pi_float = 3.14159f; double pi_double = 3.1415926535; double distance = 1.5e3; // 科学计数法,等价于1500.0
派生类型
派生类型基于基本类型扩展,用于表示更复杂的数据结构。
- 数组:相同类型元素的有序集合,通过下标访问元素(下标从 0 开始)。
- 定义格式:
类型 数组名[长度];
int scores[5] = {90, 85, 95, 88, 92}; // 定义包含5个整数的数组
char name[6] = "Alice"; // 字符数组(字符串),末尾自动添加'\0'结束符
- 指针:存储变量内存地址的变量,通过
*
操作符访问指向的变量值,通过&
操作符获取变量地址。
int a = 10;
int *p = &a; // p指向a的地址
printf("%d", *p); // 输出10(访问p指向的变量值)
- 结构体:自定义的复合类型,可包含不同类型的成员,用于描述一个 "实体" 的多个属性。
struct Student {
char name[20];
int age;
float score;
};
struct Student s = {"Tom", 18, 90.5}; // 定义结构体变量并初始化
- 共用体(联合体):与结构体类似,但所有成员共享同一块内存(大小为最大成员的大小),同一时间只能存储一个成员的值。
union Data {
int i;
float f;
char c;
};
union Data d;
d.i = 10; // 存储整数
d.f = 3.14f; // 覆盖整数,存储浮点数
2. 变量与常量
变量
变量是程序运行中值可以改变的存储单元,使用前必须先声明类型。
-
声明格式:
类型 变量名 = 初始值;
(初始化可选,未初始化的变量值不确定)。 -
作用域:变量的有效范围,如函数内定义的局部变量仅在函数内有效,函数外定义的全局变量在整个文件中有效。
示例:
int global_var = 100; // 全局变量
void func() {
int local_var = 20; // 局部变量
global_var += local_var; // 全局变量可被函数访问
}
常量
常量是值不可改变的量,有两种定义方式:
-
const
修饰的常量:具有类型检查,更安全,本质是 "只读变量"。
const int MAX_SIZE = 100; // 定义整型常量,不可修改
const float PI = 3.14159f;
#define
定义的宏常量:预编译阶段的文本替换,无类型检查,可定义更复杂的常量或表达式。
#define MAX_AGE 120 // 无类型,直接替换文本
#define AREA(r) (3.14 * (r) * (r)) // 带参数的宏常量(计算圆面积)
区别:const
常量在编译阶段生效,有类型;#define
在预编译阶段生效,仅做文本替换。
3. 运算符
算术运算符
用于数值计算,注意整数除法会自动截断小数部分。
int a = 10, b = 3;
int sum = a + b; // 13
int div = a / b; // 3(整数除法)
int rem = a % b; // 1(取余,仅用于整数)
关系运算符
用于比较两个值的关系,返回1
(真)或0
(假)。
int x = 5, y = 10;
int is_eq = (x == y); // 0(5不等于10)
int is_ge = (x >= 3); // 1(5大于等于3)
逻辑运算符
用于逻辑判断,遵循 "短路求值" 规则(前半部分可确定结果时,后半部分不执行)。
int a = 1, b = 0;
int and_res = (a && b); // 0(逻辑与:两者都为真才为真)
int or_res = (a || b); // 1(逻辑或:至少一个为真则为真)
int not_res = !a; // 0(逻辑非:取反)
位运算符
直接对二进制位进行操作,常用于底层编程或优化运算。
int a = 6; // 二进制:0110
int b = 3; // 二进制:0011
int and_bit = a & b; // 0010(2,按位与)
int or_bit = a | b; // 0111(7,按位或)
int xor_bit = a ^ b; // 0101(5,按位异或)
int shift_left = a << 1; // 1100(12,左移1位等价于乘2)
赋值运算符
将右侧值赋给左侧变量,复合赋值运算符是简化写法。
int x = 5;
x += 3; // 等价于x = x + 3 → x=8
x *= 2; // 等价于x = x * 2 → x=16
x %= 3; // 等价于x = x % 3 → x=1
4. 控制结构
条件语句
根据条件执行不同分支,else if
可用于多条件判断。
int score = 85;
if (score >= 90) {
printf("优秀");
} else if (score >= 60) {
printf("及格"); // 执行此分支
} else {
printf("不及格");
}
循环语句
重复执行代码块,三种循环各有适用场景:
-
while
循环:先判断条件,条件为真则执行循环体,适合未知循环次数的场景。
int i = 1;
while (i <= 5) {
printf("%d ", i); // 输出:1 2 3 4 5
i++;
}
do-while
循环:先执行一次循环体,再判断条件,至少执行一次。
int j = 1;
do {
printf("%d ", j); // 输出:1 2 3
j++;
} while (j <= 3);
for
循环:初始化、条件判断、增量更新集中在一处,适合已知循环次数的场景。
for (int k = 0; k < 3; k++) {
printf("循环第%d次\n", k+1); // 输出3次循环信息
}
跳转语句
改变程序执行流程:
-
break
:跳出当前循环或switch
语句
for (int i = 0; i < 10; i++) {
if (i == 5) break; // 当i=5时跳出循环
printf("%d ", i); // 输出:0 1 2 3 4
}
continue
:跳过本次循环剩余部分,直接进入下一次循环。
for (int i = 0; i < 5; i++) {
if (i == 2) continue; // 跳过i=2的循环体
printf("%d ", i); // 输出:0 1 3 4
}
goto
:无条件跳转到同一函数内的标签处(不推荐使用,易导致代码混乱)。
int x = 1;
if (x == 1) {
goto label; // 跳转到label处
}
printf("这里不会执行");
label:
printf("跳转到这里");
return
:结束当前函数并返回值(无返回值的函数用return;
)。
总结:
不积硅步无以至千里,不积小流无以成江海。其实C语言的主要知识点就是这些东西,其余的都是在这些基础上的拓展,如果对嵌入式感兴趣可以关注我,以后会循序渐进的分享与嵌入式或我当前了解学习的更多知识点,共勉。