函数速查表
printf格式控制符表
| 格式控制符 | 含义 | 适用数据类型 | 参数说明 | 示例 | 输出结果(示例) |
|---|---|---|---|---|---|
| 整型相关 | |||||
| %d | 有符号十进制整数 | int、short | 接收int/short类型变量,按十进制输出 | printf("%d", 123); |
123 |
| %i | 有符号十进制整数(与%d功能一致) | int、short | 同%d,兼容早期C标准 | printf("%i", -45); |
-45 |
| %u | 无符号十进制整数 | unsigned int | 接收unsigned int类型,仅输出非负数 | printf("%u", 123U); |
123 |
| %o | 无符号八进制整数(无前缀0) | unsigned int | 按八进制输出,不显示前缀0 | printf("%o", 10); |
12 |
| %x | 无符号十六进制整数(小写字母,无前缀0x) | unsigned int | 0-9用数字,10-15用a-f | printf("%x", 255); |
ff |
| %X | 无符号十六进制整数(大写字母,无前缀0X) | unsigned int | 0-9用数字,10-15用A-F | printf("%X", 255); |
FF |
| %#o | 无符号八进制整数(带前缀0) | unsigned int | 自动添加前缀0标识八进制 | printf("%#o", 10); |
012 |
| %#x | 无符号十六进制整数(带前缀0x) | unsigned int | 自动添加前缀0x标识十六进制 | printf("%#x", 255); |
0xff |
| 浮点型相关 | |||||
| %f | 单/双精度浮点数(小数形式) | float、double | 接收float/double,默认保留6位小数 | printf("%f", 3.14); |
3.140000 |
| %lf | 双精度浮点数(printf中与%f无区别) | double | 仅在scanf中区分float(%f)和double(%lf) | printf("%lf", 3.1415); |
3.141500 |
| %e | 浮点数(指数形式,小写e) | float、double | 按尾数e+指数格式,尾数默认6位小数 |
printf("%e", 300.0); |
3.000000e+02 |
| %E | 浮点数(指数形式,大写E) | float、double | 按尾数E+指数格式,尾数默认6位小数 |
printf("%E", 300.0); |
3.000000E+02 |
| %g | 自动选%f/%e(短格式,无意义0不显示) | float、double | 优先用%f,若数值过大/过小自动切换%e,删除末尾无意义0 | printf("%g", 3.000); |
3 |
| %G | 自动选%f/%E(短格式,无意义0不显示) | float、double | 同%g,指数部分用大写E | printf("%G", 0.000300); |
0.0003 |
| 字符/字符串相关 | |||||
| %c | 单个字符 | char | 接收char类型,输出对应ASCII字符 | printf("%c", 'A'); |
A |
| %s | 字符串 | char*(字符串指针) | 接收以\0结尾的字符数组/指针,输出到\0停止 |
printf("%s", "Hello"); |
Hello |
| 指针相关 | |||||
| %p | 指针地址(十六进制形式) | void* | 接收任意类型指针,输出其内存地址(依赖系统位数) | int a; printf("%p", &a); |
0x7ffd42a1b23c(64位系统) |
| 特殊符号 | |||||
| %% | 输出一个百分号% | 无 | 无参数,用于打印%本身(避免被解析为格式符) | printf("%%"); |
% |
示例
#include <stdio.h>
int main() {int a = 123; // 整型unsigned int b = 456; // 无符号整型float c = 3.14159f; // 单精度浮点型double d = 2.71828; // 双精度浮点型char e = 'A'; // 字符型char f[] = "Hello"; // 字符串型int *g = &a; // 指针(指向整型a)// 基础格式符输出printf("1. 整型(%d):%d\n", a, a); // %d:有符号十进制整数printf("2. 无符号整型(%u):%u\n", b, b); // %u:无符号十进制整数printf("3. 单精度浮点(%f):%.3f\n", c, c); // %f:浮点数,%.3f保留3位小数printf("4. 双精度浮点(%lf):%.4lf\n", d, d); // %lf:双精度浮点数(printf中%f也可)printf("5. 字符(%c):%c\n", e, e); // %c:单个字符printf("6. 字符串(%s):%s\n", f, f); // %s:字符串(以'\0'结尾)printf("7. 指针地址(%p):%p\n", g, g); // %p:指针的十六进制地址(格式因系统而异)return 0;
}
** printf附加格式修饰符表**
| 修饰符 | 含义 | 参数说明 | 示例 | 输出结果(示例) |
|---|---|---|---|---|
| %md | 输出宽度为m,不足用空格填充(右对齐) | m为int型,指定输出总宽度 | printf("%5d", 10); |
10(共5位,前面补3个空格) |
| %-md | 输出宽度为m,不足用空格填充(左对齐) | m为int型,-表示左对齐 | printf("%-5d", 10); |
10 (共5位,后面补3个空格) |
| %0md | 输出宽度为m,不足用0填充(右对齐) | m为int型,0表示用0填充空位 | printf("%05d", 10); |
00010(共5位,前面补3个0) |
| %m.nf | 输出宽度为m,保留n位小数 | m:总宽度;n:小数位数 | printf("%8.2f", 3.14159); |
3.14(总8位,前面4个空格,保留2位小数) |
| %.nf | 仅保留n位小数(宽度不限) | n:小数位数 | printf("%.3f", 3.14); |
3.140(保留3位小数) |
| %.f | 宽度和小数位由参数指定 | 第一个对应宽度m,第二个对应小数位n | printf("%*.*f", 8, 2, 3.14); |
3.14(等价于%8.2f) |
示例
#include <stdio.h>
int main() {int num = 10;// 附加修饰符:%md(宽度)、%-md(左对齐)、%0md(0填充)printf("1. 指定宽度5(%5d):[%5d]\n", num, num); // 宽度5,右对齐(默认),不足补空格printf("2. 左对齐宽度5(%-5d):[%-5d]end\n", num, num); // 左对齐,不足补空格printf("3. 0填充宽度5(%05d):%05d\n", num, num); // 宽度5,不足补0printf("4. 正负号显示(%+d):%+d 和 %+d\n", num, -num, num); // %+d强制显示正负号return 0;
}
运算符优先级与结合性表
| 优先级(1最高,16最低) | 运算符 | 含义 | 操作数数量 | 结合性 | 参数/表达式示例 | 说明 |
|---|---|---|---|---|---|---|
| 1 | () | 函数调用/表达式分组 | 1(函数调用)/2(分组) | 左到右 | func(3, 4)、(a+b)*c |
函数调用时括号内为参数列表;分组时改变运算顺序 |
| 1 | [] | 数组下标 | 2(数组名+下标) | 左到右 | arr[5] |
等价于*(arr+5),下标为int型 |
| 1 | -> | 指针访问结构体成员 | 2(指针+成员名) | 左到右 | p->name |
p为结构体指针,name为结构体成员 |
| 1 | . | 结构体/联合体访问成员 | 2(变量+成员名) | 左到右 | obj.age |
obj为结构体变量,age为成员 |
| 2 | ++(后置) | 后置自增 | 1(变量) | 左到右 | a++ |
先使用a的值,再a=a+1 |
| 2 | --(后置) | 后置自减 | 1(变量) | 左到右 | a-- |
先使用a的值,再a=a-1 |
| 2 | & | 取地址 | 1(变量) | 右到左 | &a |
返回变量a的内存地址(指针类型) |
| 2 | * | 解引用(指针取值) | 1(指针) | 右到左 | *p |
返回指针p指向的变量值 |
| 2 | sizeof | 计算内存大小 | 1(类型/变量) | 右到左 | sizeof(int)、sizeof(a) |
返回字节数,类型需加括号,变量可省略 |
| 3 | ++(前置) | 前置自增 | 1(变量) | 右到左 | ++a |
先a=a+1,再使用a的值 |
| 3 | --(前置) | 前置自减 | 1(变量) | 右到左 | --a |
先a=a-1,再使用a的值 |
| 3 | +(正) | 正号 | 1(数值/变量) | 右到左 | +3、+a |
无实际运算,仅标识正数 |
| 3 | -(负) | 负号 | 1(数值/变量) | 右到左 | -3、-a |
取变量的相反数 |
| 3 | ! | 逻辑非 | 1(布尔值/变量) | 右到左 | !flag |
真(非0)变假(0),假变真(1) |
| 3 | ~ | 位非(按位取反) | 1(整型变量) | 右到左 | ~a |
对变量的每一位二进制取反(0变1,1变0) |
| 3 | (type) | 强制类型转换 | 1(变量/表达式) | 右到左 | (int)3.14 |
将数据转换为指定类型,可能丢失精度 |
| 4 | *(乘) | 乘法 | 2(数值/变量) | 左到右 | a*b |
整型/浮点型乘法 |
| 4 | /(除) | 除法 | 2(数值/变量) | 左到右 | a/b |
整型除法取商(如5/2=2),浮点除法取精确值(5.0/2=2.5) |
| 4 | %(取模) | 取余数 | 2(整型变量) | 左到右 | a%b |
仅支持整型,结果符号与被除数一致(如5%2=1,-5%2=-1) |
| 5 | +(加) | 加法 | 2(数值/变量) | 左到右 | a+b |
整型/浮点型加法 |
| 5 | -(减) | 减法 | 2(数值/变量) | 左到右 | a-b |
整型/浮点型减法 |
| 6 | <<(左移) | 按位左移 | 2(整型变量+移位位数) | 左到右 | a<<2 |
变量a的二进制左移2位,右边补0(等价于a*2^2) |
| 6 | >>(右移) | 按位右移 | 2(整型变量+移位位数) | 左到右 | a>>2 |
有符号数左补符号位,无符号数左补0(等价于a/2^2) |
| 7 | > | 大于 | 2(数值/变量) | 左到右 | a>b |
成立返回1(真),不成立返回0(假) |
| 7 | >= | 大于等于 | 2(数值/变量) | 左到右 | a>=b |
成立返回1,不成立返回0 |
| 7 | < | 小于 | 2(数值/变量) | 左到右 | a<b |
成立返回1,不成立返回0 |
| 7 | <= | 小于等于 | 2(数值/变量) | 左到右 | a<=b |
成立返回1,不成立返回0 |
| 8 | == | 等于 | 2(数值/变量/指针) | 左到右 | a==b、p==NULL |
比较值是否相等,成立返回1,不成立返回0 |
| 8 | != | 不等于 | 2(数值/变量/指针) | 左到右 | a!=b |
比较值是否不相等,成立返回1,不成立返回0 |
| 9 | &(位与) | 按位与 | 2(整型变量) | 左到右 | a&b |
对应位均为1则为1,否则为0 |
| 10 | ^(位异或) | 按位异或 | 2(整型变量) | 左到右 | a^b |
对应位不同则为1,相同则为0 |
| 11 | |(位或) | 按位或 | 2(整型变量) | 左到右 | a|b |
对应位有1则为1,否则为0 |
| 12 | &&(逻辑与) | 逻辑与 | 2(布尔值/表达式) | 左到右 | a&&b |
短路特性:左为假则右不执行,均为真返回1,否则0 |
| 13 | ||(逻辑或) | 逻辑或 | 2(布尔值/表达式) | 左到右 | a||b |
短路特性:左为真则右不执行,有一个真返回1,否则0 |
| 14 | ?:(条件运算符) | 三目运算 | 3(表达式1?表达式2:表达式3) | 右到左 | a>b?a:b |
表达式1为真则取表达式2的值,否则取表达式3的值 |
| 15 | = | 赋值 | 2(变量=表达式) | 右到左 | a=3、a=b+c |
将右边表达式的值赋给左边变量 |
| 15 | += | 加赋值 | 2(变量+=表达式) | 右到左 | a+=2 |
等价于a=a+2 |
| 15 | -= | 减赋值 | 2(变量-=表达式) | 右到左 | a-=2 |
等价于a=a-2 |
| 15 | *= | 乘赋值 | 2(变量*=表达式) | 右到左 | a*=2 |
等价于a=a*2 |
| 15 | /= | 除赋值 | 2(变量/=表达式) | 右到左 | a/=2 |
等价于a=a/2 |
| 15 | %= | 模赋值 | 2(变量%=表达式) | 右到左 | a%=2 |
等价于a=a%2 |
| 15 | <<= | 左移赋值 | 2(变量<<=位数) | 右到左 | a<<=2 |
等价于a=a<<2 |
| 15 | >>= | 右移赋值 | 2(变量>>=位数) | 右到左 | a>>=2 |
等价于a=a>>2 |
| 15 | &= | 位与赋值 | 2(变量&=表达式) | 右到左 | a&=b |
等价于a=a&b |
| 15 | ^= | 位异或赋值 | 2(变量^=表达式) | 右到左 | a^=b |
等价于a=a^b |
| 15 | |= | 位或赋值 | 2(变量|=表达式) | 右到左 | a|=b |
等价于a=a|b |
| 16 | ,(逗号运算符) | 表达式分隔 | 多个(表达式1,表达式2,...) | 左到右 | a=3, b=4, a+b |
从左到右依次执行,最终取最后一个表达式的值(结果为7) |
字符串操作函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
char *strcpy(char *dest, const char *src) |
将源字符串(含终止符\0)复制到目标缓冲区,覆盖目标缓冲区原有内容 |
- dest:char*,目标缓冲区(可读写,需足够大)- src:const char*,源字符串(只读,以\0结尾) |
成功:返回dest(目标缓冲区首地址)失败:无(但可能因缓冲区溢出导致程序崩溃) |
<string.h> |
1. 不安全:不检查dest长度,若src过长会溢出2. 自动复制 src的\0到dest |
char *strncpy(char *dest, const char *src, size_t n) |
将源字符串的前n个字符复制到目标缓冲区,是strcpy的安全版本,限制复制长度 |
- dest:char*,目标缓冲区- src:const char*,源字符串- n:size_t,最大复制字节数 |
成功:返回dest失败:无(相对安全,溢出风险低) |
<string.h> |
1. 相对安全:最多复制n字节2. 若 src长度≤n,复制src和\0;若src长度>n,仅复制前n字节,不自动加\0(需手动补) |
char *strcat(char *dest, const char *src) |
将源字符串追加到目标字符串的末尾(从目标字符串的\0位置开始),自动复制\0 |
- dest:char*,目标字符串(可读写,以\0结尾,需足够大)- src:const char*,源字符串(只读,以\0结尾) |
成功:返回dest失败:无(可能溢出) |
<string.h> |
1. 不安全:不检查dest剩余空间,src过长会溢出2. 从 dest的\0位置开始拼接,自动复制src的\0 |
char *strncat(char *dest, const char *src, size_t n) |
将源字符串的前n个字符追加到目标字符串末尾,自动添加\0,是strcat的安全版本 |
- dest:char*,目标字符串- src:const char*,源字符串- n:size_t,最大拼接字节数 |
成功:返回dest失败:无 |
<string.h> |
1. 安全:最多拼接n字节,自动在末尾加\02. 拼接长度 = min( strlen(src),n) |
int strcmp(const char *s1, const char *s2) |
按ASCII值逐字符比较两个字符串,直到遇到不同字符或\0,判断字符串的字典序关系 |
- s1:const char*,第一个字符串- s2:const char*,第二个字符串 |
返回值: - <0:s1字典序小于s2- 0:s1与s2完全相同- >0:s1字典序大于s2 |
<string.h> |
1. 按ASCII值逐字符比较,直到\0或不同字符2. 区分大小写(如 'A'(65)< 'a'(97)) |
size_t strlen(const char *s) |
计算字符串的长度(字节数),从字符串起始位置到第一个\0为止,不包含\0 |
- s:const char*,字符串(以\0结尾) |
成功:返回字符串长度(字节数,不含\0)失败:无( s为NULL会段错误) |
<string.h> |
1. 时间复杂度O(n),需遍历到\02. 若字符串无 \0,会越界访问(结果不确定) |
char *strchr(const char *s, int c) |
从字符串起始位置往后查找指定字符(含\0),返回首次出现该字符的指针 |
- s:const char*,字符串- c:int,要查找的字符(ASCII值,低8位有效) |
成功:返回指向该字符第一次出现位置的指针 失败:返回 NULL |
<string.h> |
1. 从字符串开头往后查找,包括\0(如strchr(s, '\0')返回s+strlen(s))2. 找到后返回的指针可用于修改字符(若 s非const) |
char *strrchr(const char *s, int c) |
从字符串末尾位置往前查找指定字符(含\0),返回最后一次出现该字符的指针 |
- s:const char*,字符串- c:int,要查找的字符 |
成功:返回指向该字符最后一次出现位置的指针 失败:返回 NULL |
<string.h> |
1. 从字符串末尾往前查找(反向查找) 2. 其他特性同 strchr |
char *strstr(const char *haystack, const char *needle) |
在主字符串中查找子字符串首次出现的位置,返回子字符串的起始指针;子字符串为空时返回主字符串 | - haystack:const char*,主字符串(被查找的字符串)- needle:const char*,子字符串(要查找的字符串) |
成功:返回指向子字符串第一次出现位置的指针 失败:返回 NULL |
<string.h> |
1. 若needle为空字符串,返回haystack(标准规定)2. 区分大小写,不支持通配符 |
char *strtok(char *str, const char *delim) |
按指定分隔符集合分割字符串,将分隔符替换为\0,返回当前分割的子字符串指针 |
- str:char*,要分割的字符串(首次调用传非NULL,后续传NULL)- delim:const char*,分隔符集合(如",. "表示逗号、句号、空格均为分隔符) |
成功:返回当前分割出的子字符串指针 失败/分割完毕:返回 NULL |
<string.h> |
1. 修改原字符串:将分隔符替换为\02. 非线程安全(同一进程多个线程调用会相互干扰) 3. 示例: char s[]="a,b.c"; strtok(s, ",.");返回"a",下次传NULL返回"b" |
void *memcpy(void *dest, const void *src, size_t n) |
按字节从源内存复制n字节数据到目标内存,不关心数据类型,支持任意内存块复制 |
- dest:void*,目标内存(可读写)- src:const void*,源内存(只读)- n:size_t,要复制的字节数 |
成功:返回dest失败:无( src/dest为NULL会段错误) |
<string.h> |
1. 按字节复制,不关心数据类型(可复制结构体、数组等) 2. 源/目标内存重叠时行为未定义(需用 memmove) |
void *memmove(void *dest, const void *src, size_t n) |
按字节从源内存复制n字节数据到目标内存,内部处理内存重叠问题,是memcpy的安全版本 |
- dest:void*,目标内存- src:const void*,源内存- n:size_t,复制字节数 |
成功:返回dest失败:无 |
<string.h> |
1. 功能同memcpy,但内部会处理内存重叠(先将数据临时存储)2. 比 memcpy稍慢,但更安全 |
void *memset(void *s, int c, size_t n) |
按字节将目标内存的前n字节设置为指定值c,常用于内存初始化(如清零) |
- s:void*,目标内存(可读写)- c:int,填充值(按字节填充,低8位有效)- n:size_t,填充字节数 |
成功:返回s失败:无 |
<string.h> |
1. 仅按字节填充,不适用于int数组初始化(如memset(arr, 1, sizeof(arr))会将每个字节设为1,int值为0x01010101≠1)2. 常用场景:内存清零( memset(s, 0, n)) |
示例
#include <stdio.h>
#include <string.h> // 字符串函数头文件int main() {char str1[20] = "Hello";char str2[20] = "World";char str3[20];int len, cmp_result;// 1. strlen:计算字符串长度(不含'\0')len = strlen(str1);printf("1. strlen(str1) = %d\n", len); // 输出5("Hello"共5个字符)// 2. strcpy:复制字符串(str2 → str3,不安全:可能溢出)strcpy(str3, str2); // 把str2的"World"复制到str3printf("2. strcpy(str3, str2) → str3 = %s\n", str3); // 输出World// 3. strncpy:安全复制(限制复制长度,避免溢出)char str4[10];strncpy(str4, str1, 3); // 复制str1的前3个字符到str4str4[3] = '\0'; // 手动补'\0'(strncpy不自动加'\0',需手动处理)printf("3. strncpy(str4, str1, 3) → str4 = %s\n", str4); // 输出Hel// 4. strcat:追加字符串(str2 → str1,不安全:可能溢出)strcat(str1, " "); // 先给str1追加空格(str1变为"Hello ")strcat(str1, str2); // 再追加str2(str1变为"Hello World")printf("4. strcat(str1, str2) → str1 = %s\n", str1); // 输出Hello World// 5. strcmp:比较字符串(按ASCII码字典序)// 返回值:0(相等)、正数(str1>str2)、负数(str1<str2)cmp_result = strcmp("Apple", "Banana");printf("5. strcmp(\"Apple\", \"Banana\") = %d\n", cmp_result); // 输出负数(A的ASCII < B)cmp_result = strcmp("Hello", "Hello");printf(" strcmp(\"Hello\", \"Hello\") = %d\n", cmp_result); // 输出0(相等)return 0;
}
标准IO函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
FILE *fopen(const char *pathname, const char *mode) |
打开指定文件,返回文件指针用于后续IO操作;文件不存在时根据mode决定是否创建 | - pathname:const char*,指定文件的完整路径(如"/home/test.txt")或当前目录文件名(如"test.txt")- mode:const char*,打开模式,常用值:- "r":只读,文件不存在则失败- "w":只写,文件不存在则创建,存在则清空内容- "a":追加写,文件不存在则创建,写入从文件尾开始- "r+":读写,文件不存在则失败- "w+":读写,文件不存在则创建,存在则清空- "a+":读写,文件不存在则创建,写入从文件尾开始 |
成功:返回指向FILE结构体的指针(文件指针),用于后续IO操作失败:返回 NULL,并设置errno(可通过perror打印错误) |
<stdio.h> |
1. 文本文件与二进制文件打开模式一致(Linux下无区别) 2. 打开后必须调用 fclose关闭,避免资源泄漏 |
int fclose(FILE *stream) |
关闭已打开的文件指针,释放文件相关资源,并自动刷新缓冲区数据到文件 | - stream:FILE*,fopen返回的文件指针 |
成功:返回0 失败:返回-1,并设置 errno |
<stdio.h> |
1. 关闭前会自动刷新缓冲区(全缓冲/行缓冲) 2. 关闭后 stream指针变为野指针,不可再使用 |
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) |
从指定文件中按数据块读取数据,存储到用户缓冲区,适用于二进制文件或批量数据读取 | - ptr:void*,用户缓冲区指针(用于存储读取到的数据,需提前分配内存)- size:size_t,单个数据块的字节数(如sizeof(int))- nmemb:size_t,要读取的数据块数量- stream:FILE*,文件指针 |
成功:返回实际读取的数据块数量(不是字节数) 失败/EOF:返回0,需通过 feof(stream)判断是否到文件尾,ferror(stream)判断是否错误 |
<stdio.h> |
1. 适用于二进制文件(如图片、视频),也可用于文本文件 2. 读取字节数 = 返回值 × size 3. 若读取到部分数据(如文件尾),返回值小于nmemb |
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) |
将用户缓冲区中的数据按数据块写入指定文件,适用于二进制文件或批量数据写入 | - ptr:const void*,用户缓冲区指针(存储要写入的数据)- size:size_t,单个数据块的字节数- nmemb:size_t,要写入的数据块数量- stream:FILE*,文件指针 |
成功:返回实际写入的数据块数量 失败:返回0,并设置 errno |
<stdio.h> |
1. 写入字节数 = 返回值 × size 2. 全缓冲模式下,数据先存缓冲区,满了才写入文件;需 fflush强制刷新 |
int fgetc(FILE *stream) |
从指定文件中读取单个字符,文件指针自动向后移动,支持文本文件的字符级读取 | - stream:FILE*,文件指针(如stdin表示标准输入) |
成功:返回读取到的字符(ASCII值,范围0-255) 失败/EOF:返回 EOF(宏定义为-1) |
<stdio.h> |
1. 每次读取1个字符,文件指针自动后移 2. 读取标准输入时, stream为stdin(如fgetc(stdin)等价于getchar()) |
int fputc(int c, FILE *stream) |
向指定文件中写入单个字符,文件指针自动向后移动,支持文本文件的字符级写入 | - c:int,要写入的字符(ASCII值,低8位有效)- stream:FILE*,文件指针(如stdout表示标准输出) |
成功:返回写入的字符(ASCII值) 失败:返回 EOF |
<stdio.h> |
1. 每次写入1个字符,文件指针自动后移 2. 写入标准输出时, stream为stdout(如fputc('A', stdout)等价于putchar('A')) |
char *fgets(char *s, int size, FILE *stream) |
从指定文件中按行读取文本数据,存储到用户字符数组,自动添加字符串终止符\0 |
- s:char*,用户字符数组(存储读取的行数据)- size:int,数组最大长度(需预留1字节存\0)- stream:FILE*,文件指针 |
成功:返回s(数组首地址)失败/EOF:返回 NULL |
<stdio.h> |
1. 读取到\n或size-1字节后停止,自动在末尾加\02. 会读取 \n并存入数组(区别于gets)3. 避免缓冲区溢出( size需小于数组长度) |
int fputs(const char *s, FILE *stream) |
将指定字符串写入目标文件,写入到字符串终止符\0为止,不自动添加换行符 |
- s:const char*,要写入的字符串(需以\0结尾)- stream:FILE*,文件指针 |
成功:返回非负整数(具体值依赖系统) 失败:返回 EOF |
<stdio.h> |
1. 不自动添加\n(区别于puts)2. 写入到 \0停止,\0不写入文件 |
int fprintf(FILE *stream, const char *format, ...) |
按指定格式将可变参数数据写入目标文件,支持格式化输出(如整数、字符串、浮点数) | - stream:FILE*,文件指针(如stdout表示屏幕输出)- format:const char*,格式控制字符串(含%d、%s等)- ...:可变参数,与format中的格式符一一对应 |
成功:返回实际写入的字节数(不含\0)失败:返回负数 |
<stdio.h> |
1. 格式符需与参数类型匹配(如%d对应int)2. 标准输出用 fprintf(stdout, ...),等价于printf(...) |
int fscanf(FILE *stream, const char *format, ...) |
按指定格式从目标文件中读取数据,存储到指定变量,支持格式化输入 | - stream:FILE*,文件指针(如stdin表示键盘输入)- format:const char*,格式控制字符串- ...:可变参数,需传变量地址(如&a) |
成功:返回成功匹配并赋值的参数个数 失败/EOF:返回 EOF |
<stdio.h> |
1. 跳过空白符(空格、\t、\n)2. 读取字符串时,遇空白符停止(不读取空白符) 3. 标准输入用 fscanf(stdin, ...),等价于scanf(...) |
int fseek(FILE *stream, long offset, int whence) |
调整文件指针的位置,实现文件的随机访问,支持从文件头、当前位置或文件尾偏移 | - stream:FILE*,文件指针- offset:long,偏移量(正数向右移,负数向左移)- whence:int,偏移基准:- SEEK_SET:从文件头开始(offset≥0)- SEEK_CUR:从当前位置开始- SEEK_END:从文件尾开始(offset常为负) |
成功:返回0 失败:返回-1,并设置 errno |
<stdio.h> |
1. 用于随机访问文件,不适用于管道、socket等流式文件 2. 文本文件中,offset需为 ftell返回值(避免换行符转换问题) |
long ftell(FILE *stream) |
获取当前文件指针相对于文件头的偏移量(字节数),用于记录文件位置或计算文件长度 | - stream:FILE*,文件指针 |
成功:返回当前位置到文件头的字节数 失败:返回-1L,并设置 errno |
<stdio.h> |
1. 常与fseek配合使用(如记录当前位置,后续恢复)2. 文本文件中,换行符可能被转换(如Windows下 \n存为\r\n),影响字节数计算 |
int fflush(FILE *stream) |
强制刷新文件的缓冲区,将缓冲区中的数据立即写入文件,避免数据滞留丢失 | - stream:FILE*,文件指针;若为NULL,刷新所有打开的流 |
成功:返回0 失败:返回 EOF,并设置errno |
<stdio.h> |
1. 强制将缓冲区数据写入文件(适用于全缓冲/行缓冲) 2. 标准输出 stdout默认行缓冲,加\n或fflush(stdout)可刷新3. 只读流(如 "r"模式)调用fflush行为未定义 |
示例
#include <stdio.h>
#include <stdlib.h> // 用于exit()函数int main() {FILE *fp; // 文件指针char buffer[100];int num = 123;float pi = 3.14f;// 1. fopen:打开文件(模式"w"=只写,文件不存在则创建,存在则清空)fp = fopen("test.txt", "w");if (fp == NULL) { // 必须判断打开是否成功(如权限不足会失败)perror("fopen write error"); // 打印错误信息exit(1); // 退出程序(异常状态)}// 2. fprintf:向文件写数据(格式与printf类似,多一个文件指针参数)fprintf(fp, "This is a test file.\n");fprintf(fp, "Integer: %d\n", num);fprintf(fp, "Float: %.2f\n", pi);printf("文件写入完成!\n");// 3. fclose:关闭文件(必须关闭,避免数据丢失)fclose(fp);fp = NULL; // 指针置空,避免野指针// 4. 重新打开文件(模式"r"=只读,读取刚才写入的内容)fp = fopen("test.txt", "r");if (fp == NULL) {perror("fopen read error");exit(1);}// 5. fgets:从文件读一行数据到缓冲区(最多读99个字符,留1个给'\0')printf("\n从文件读取内容:\n");while (fgets(buffer, sizeof(buffer), fp) != NULL) { // 读到文件末尾返回NULLprintf("%s", buffer); // 打印读取的内容}// 6. 再次关闭文件fclose(fp);fp = NULL;return 0;
}
系统IO函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
int open(const char *pathname, int flags, ...) |
打开或创建指定文件,返回文件描述符用于底层IO操作;支持细粒度的打开选项(如非阻塞、追加) | - pathname:const char*,文件路径/名称(同fopen)- flags:int,打开选项(必选+可选组合,用|连接):- 必选(三选一): O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)- 可选: O_CREAT(文件不存在则创建,需传第3参数mode)、O_APPEND(追加写,每次写前移到文件尾)、O_TRUNC(文件存在则清空)、O_NONBLOCK(非阻塞模式)- ...:可变参数,仅当flags含O_CREAT时有效,为mode_t mode(文件权限,如0644) |
成功:返回文件描述符(非负整数,0=标准输入,1=标准输出,2=标准错误) 失败:返回-1,并设置 errno |
<fcntl.h> |
1. 权限计算:实际权限 = mode - umask(系统默认umask为0022或0002) 2. 打开后需调用 close关闭,避免描述符泄漏3. 区别于 fopen:返回文件描述符(int),而非文件指针(FILE*) |
int close(int fd) |
关闭指定的文件描述符,释放文件相关内核资源;若多个描述符指向同一文件,需全部关闭才释放文件 | - fd:int,open返回的文件描述符 |
成功:返回0 失败:返回-1,并设置 errno |
<unistd.h> |
1. 关闭后fd不可再使用2. 若多个描述符指向同一文件(如 dup复制),需全部关闭才释放文件资源 |
ssize_t read(int fd, void *buf, size_t count) |
从指定文件描述符中读取数据,存储到用户缓冲区,支持底层字节级读取 | - fd:int,文件描述符- buf:void*,用户缓冲区(存储读取的数据,需提前分配)- count:size_t,要读取的最大字节数 |
成功:返回实际读取的字节数(0表示EOF) 失败:返回-1,并设置 errno |
<unistd.h> |
1. 阻塞模式下,若无数据则等待;非阻塞模式下,无数据返回-1(errno=EAGAIN)2. 读取字节数可能小于count(如文件尾、网络数据不完整) |
ssize_t write(int fd, const void *buf, size_t count) |
将用户缓冲区中的数据写入指定文件描述符,支持底层字节级写入 | - fd:int,文件描述符- buf:const void*,用户缓冲区(存储要写入的数据)- count:size_t,要写入的字节数 |
成功:返回实际写入的字节数 失败:返回-1,并设置 errno |
<unistd.h> |
1. 若磁盘满或缓冲区满,写入字节数可能小于count 2. 标准输出 fd=1,标准错误fd=2(如write(1, "Hello", 5)等价于printf("Hello")) |
off_t lseek(int fd, off_t offset, int whence) |
调整文件描述符对应的文件偏移量,实现底层文件的随机访问 | - fd:int,文件描述符- offset:off_t,偏移量(正数右移,负数左移)- whence:int,偏移基准(同fseek的SEEK_SET/SEEK_CUR/SEEK_END) |
成功:返回当前位置到文件头的字节数 失败:返回-1,并设置 errno |
<unistd.h> |
1. 适用于可随机访问的文件(如普通文件),不适用于管道、socket 2. 偏移到文件尾外会产生“文件空洞”(占用磁盘空间,但内容为0) |
int fcntl(int fd, int cmd, ...) |
对已打开的文件描述符进行控制操作,如获取/设置文件属性、复制描述符、设置非阻塞 | - fd:int,文件描述符- cmd:int,控制命令(常用):- F_GETFL:获取文件状态标志(如是否非阻塞)- F_SETFL:设置文件状态标志(如添加O_NONBLOCK)- F_DUPFD:复制文件描述符,返回最小未用描述符- ...:可变参数,依赖cmd(如F_SETFL需传标志值) |
成功:返回值依赖cmd(如F_GETFL返回标志值,F_DUPFD返回新描述符)失败:返回-1,并设置 errno |
<fcntl.h> |
1. 常用场景:设置非阻塞模式( fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) )2. 不同 cmd的参数和返回值需查阅man 2 fcntl |
int dup(int oldfd) |
复制指定的文件描述符,返回系统中最小的未使用文件描述符;新描述符与原描述符指向同一文件 | - oldfd:int,已打开的文件描述符 |
成功:返回最小未使用的新文件描述符 失败:返回-1,并设置 errno |
<unistd.h> |
1. 新描述符与oldfd指向同一文件,共享文件偏移量和状态2. 关闭其中一个描述符,不影响另一个 |
int dup2(int oldfd, int newfd) |
复制指定的文件描述符,并将新描述符指定为newfd;若newfd已打开,先关闭再复制 |
- oldfd:int,已打开的文件描述符- newfd:int,目标新描述符 |
成功:返回newfd失败:返回-1,并设置 errno |
<unistd.h> |
1. 若newfd已打开,先关闭newfd,再复制oldfd为newfd2. 常用于重定向(如 dup2(fd, 1)将标准输出重定向到fd对应的文件) |
示例
#include <stdio.h>
#include <unistd.h> // 系统IO核心头文件(open/close/read/write)
#include <fcntl.h> // 包含open函数的模式宏(如O_WRONLY、O_CREAT)
#include <stdlib.h>
#include <string.h>int main() {int fd; // 文件描述符(系统IO用整数标识文件,区别于标准IO的FILE*)char write_buf[] = "Hello from System IO!"; // 要写入的内容char read_buf[100] = {0}; // 读取缓冲区(初始化为0)ssize_t write_len, read_len; // 实际读写的字节数(ssize_t是系统定义的带符号整数)// 1. open:打开/创建文件(系统IO的“打开”函数)// 模式说明:O_WRONLY(只写)| O_CREAT(文件不存在则创建)// 第三个参数:文件权限(0644=所有者读写,其他只读)fd = open("sys_io_test.txt", O_WRONLY | O_CREAT, 0644);if (fd == -1) { // 打开失败返回-1perror("open write error");exit(1);}// 2. write:向文件写数据(系统IO的“写”函数)write_len = write(fd, write_buf, strlen(write_buf));if (write_len == -1) {perror("write error");close(fd); // 出错也要关闭文件exit(1);}printf("系统IO写入字节数:%zd\n", write_len); // 输出21(字符串长度)// 3. close:关闭文件(系统IO的“关闭”函数)close(fd);fd = -1; // 置为无效值,避免误用// 4. 重新打开文件(模式O_RDONLY=只读)fd = open("sys_io_test.txt", O_RDONLY);if (fd == -1) {perror("open read error");exit(1);}// 5. read:从文件读数据(系统IO的“读”函数)read_len = read(fd, read_buf, sizeof(read_buf) - 1); // 留1个字节给'\0'if (read_len == -1) {perror("read error");close(fd);exit(1);}read_buf[read_len] = '\0'; // 手动加字符串结束符(read不自动加)printf("系统IO读取内容:%s\n", read_buf); // 输出Hello from System IO!// 6. 再次关闭文件close(fd);fd = -1;return 0;
}
内存操作函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
void *malloc(size_t size) |
从堆内存中申请指定字节数的连续内存块,不初始化内存内容(内容为随机值) | - size:size_t,要申请的堆内存字节数 |
成功:返回指向堆内存的指针(void*,需强制类型转换) 失败:返回 NULL,并设置errno |
<stdlib.h> |
1. 申请的内存未初始化(内容为随机值) 2. 需检查返回值是否为 NULL(避免野指针)3. 内存需用 free释放,否则内存泄漏 |
void *calloc(size_t nmemb, size_t size) |
从堆内存中申请nmemb × size字节的连续内存块,自动将内存初始化为0,适用于创建堆数组 |
- nmemb:size_t,元素个数- size:size_t,单个元素字节数 |
成功:返回指向堆数组的指针 失败:返回 NULL,并设置errno |
<stdlib.h> |
1. 申请nmemb × size字节的堆内存,并初始化为02. 等价于 malloc(nmemb×size) + memset(..., 0, ...)3. 适用于创建数组(如 calloc(5, sizeof(int))创建5个int的堆数组) |
void *realloc(void *ptr, size_t size) |
调整已有的堆内存块大小,可扩大或缩小;扩大时可能分配新内存并复制数据,缩小则截断内存 | - ptr:void*,已有的堆内存指针(malloc/calloc返回值;若为NULL,等价于malloc(size))- size:size_t,新的内存大小(字节数) |
成功:返回指向新堆内存的指针 失败:返回 NULL,原内存ptr不变 |
<stdlib.h> |
1. 若新size>原size:可能扩展原内存,或分配新内存并复制数据(原内存自动释放) 2. 若新size<原size:截断内存,多余部分释放 3. 避免用原指针接收返回值(失败时原指针被覆盖为 NULL) |
void free(void *ptr) |
释放之前通过malloc/calloc/realloc申请的堆内存,将内存归还给系统,避免内存泄漏 |
- ptr:void*,malloc/calloc/realloc返回的堆内存指针 |
无返回值 | <stdlib.h> |
1. 仅释放堆内存,不修改指针值(ptr变为野指针,需手动设为NULL)2. 不可重复释放(同一指针释放多次会崩溃) 3. 不可释放栈内存(如局部变量地址) |
void bzero(void *s, size_t n) |
将目标内存的前n字节清零,功能等价于memset(s, 0, n),是BSD扩展函数 |
- s:void*,目标内存(可读写)- n:size_t,要清零的字节数 |
无返回值 | <strings.h> |
1. 功能同memset(s, 0, n),仅清零2. 是BSD扩展函数,部分系统可能不支持(建议用 memset保证可移植性) |
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 定义结构体(用于动态内存存储)
typedef struct {char name[20];int age;
} Person;int main() {// 1. malloc:分配指定字节数的内存(未初始化,内容随机)int *arr1 = (int *)malloc(5 * sizeof(int)); // 分配5个int的内存(约20字节)if (arr1 == NULL) { // 必须判断内存分配是否成功(失败返回NULL)perror("malloc arr1 error");exit(1);}// 手动初始化动态数组for (int i = 0; i < 5; i++) {arr1[i] = i * 10; // arr1 = [0, 10, 20, 30, 40]}printf("1. malloc动态数组arr1:");for (int i = 0; i < 5; i++) {printf("%d ", arr1[i]);}printf("\n");// 2. calloc:分配内存并初始化为0(适合数组)int *arr2 = (int *)calloc(3, sizeof(int)); // 分配3个int,初始化为0if (arr2 == NULL) {perror("calloc arr2 error");free(arr1); // 若分配失败,需释放已分配的内存exit(1);}printf("2. calloc动态数组arr2(初始化为0):");for (int i = 0; i < 3; i++) {printf("%d ", arr2[i]); // 输出 [0, 0, 0]}printf("\n");// 3. realloc:调整已分配内存的大小(扩容/缩容)int *arr1_new = (int *)realloc(arr1, 8 * sizeof(int)); // 将arr1从5个int扩容到8个if (arr1_new == NULL) {perror("realloc arr1_new error");free(arr1); // 扩容失败时,原内存arr1仍有效,需手动释放free(arr2);exit(1);}arr1 = arr1_new; // 扩容成功,更新指针指向新内存// 给新增的3个元素赋值for (int i = 5; i < 8; i++) {arr1[i] = i * 10; // arr1 = [0,10,20,30,40,50,60,70]}printf("3. realloc扩容后arr1:");for (int i = 0; i < 8; i++) {printf("%d ", arr1[i]);}printf("\n");// 4. free:释放动态内存(必须调用,避免内存泄漏)free(arr1); // 释放arr1指向的内存free(arr2); // 释放arr2指向的内存arr1 = NULL; // 指针置空,避免野指针(指向已释放的内存)arr2 = NULL;printf("4. 动态内存已全部释放\n");// 5. 动态内存存储结构体Person *p = (Person *)malloc(sizeof(Person));if (p == NULL) { perror("malloc Person error"); exit(1); }strcpy(p->name, "Li Si"); // 给结构体成员赋值p->age = 22;printf("5. 动态结构体:姓名=%s,年龄=%d\n", p->name, p->age);free(p);p = NULL;return 0;
}
进程操作函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
pid_t fork(void) |
创建子进程,子进程完全复刻父进程的内存空间(代码段、数据段、栈、堆),父子进程从fork后并发执行 |
无参数 | 成功: - 父进程:返回子进程的PID(正整数) - 子进程:返回0 失败:返回-1,并设置 errno |
<unistd.h> |
1. 子进程复刻父进程的内存空间(代码段、数据段、栈、堆),但PID不同 2. 父子进程并发执行,顺序不确定(需同步机制控制) 3. 子进程继承父进程的打开文件、环境变量等 |
pid_t wait(int *wstatus) |
阻塞等待任意子进程退出,回收子进程的内核资源,避免僵尸进程;可获取子进程退出状态 | - wstatus:int*,存储子进程退出状态;若为NULL,表示不关心退出状态 |
成功:返回回收的子进程PID 失败: - 无子进程:返回-1, errno=ECHILD- 被信号中断:返回-1, errno=EINTR |
<sys/wait.h> |
1. 阻塞等待:父进程暂停执行,直到有子进程退出 2. 若有多个子进程,仅回收第一个退出的子进程 3. 可通过宏解析 wstatus:- WIFEXITED(wstatus):是否正常退出- WEXITSTATUS(wstatus):获取正常退出的返回值- WIFSIGNALED(wstatus):是否被信号杀死 |
pid_t waitpid(pid_t pid, int *wstatus, int options) |
指定回收某个或某组子进程,支持阻塞/非阻塞模式,比wait更灵活;可避免父进程长时间阻塞 |
- pid:pid_t,指定回收的子进程:- pid > 0:回收PID为pid的子进程- pid = -1:回收任意子进程(同wait)- pid = 0:回收与父进程同组的子进程- pid < -1:回收组ID为-pid的子进程- wstatus:int*,存储退出状态(同wait)- options:int,选项:- 0:阻塞等待- WNOHANG:非阻塞,无僵尸子进程则立即返回0 |
成功: - 阻塞模式:返回回收的子进程PID - 非阻塞模式:有子进程回收则返回PID,无则返回0 失败:返回-1,并设置 errno |
<sys/wait.h> |
1. 比wait更灵活,可指定子进程和等待模式2. 常用场景:非阻塞轮询回收子进程( waitpid(-1, NULL, WNOHANG)) |
int execl(const char *path, const char *arg, ...) |
加载并执行指定路径的程序,替换当前进程的代码段和数据段(PID不变);参数以NULL结尾 |
- path:const char*,要执行的程序路径(如"/bin/ls")- arg:const char*,程序的第一个参数(通常为程序名)- ...:可变参数,后续参数为程序的命令行参数,以(char*)NULL结尾 |
成功:不返回(程序替换当前进程内存空间) 失败:返回-1,并设置 errno |
<unistd.h> |
1. 替换当前进程的代码段和数据段,PID不变 2. 若执行成功,后续代码不执行;仅失败时返回 3. 示例: execl("/bin/ls", "ls", "-l", NULL)执行ls -l |
int execlp(const char *file, const char *arg, ...) |
加载并执行程序,自动搜索PATH环境变量查找程序路径;参数以NULL结尾,无需手动指定完整路径 |
- file:const char*,程序名(如"ls"),自动搜索PATH环境变量- arg及...:同execl |
成功:不返回 失败:返回-1,并设置 errno |
<unistd.h> |
1. 区别于execl:无需指定完整路径,自动搜索PATH(如execlp("ls", "ls", "-l", NULL))2. 若 file含/,则按路径查找(同execl) |
int system(const char *command) |
创建子进程执行指定的系统命令,父进程阻塞等待命令执行完毕;命令输出默认到标准输出 | - command:const char*,要执行的系统命令字符串(如"ls -l") |
成功:返回命令的退出状态(需用WEXITSTATUS解析)失败:返回-1,并设置 errno |
<stdlib.h> |
1. 内部创建子进程执行命令,父进程阻塞等待 2. 命令执行结果输出到标准输出(屏幕) 3. 不适用于需交互的命令(如 "vi") |
FILE *popen(const char *command, const char *type) |
创建管道和子进程,执行系统命令;通过管道实现父进程与子进程的通信(读命令输出或写命令输入) | - command:const char*,系统命令字符串- type:const char*,管道方向:- "r":读取命令的输出(子进程stdout -> 父进程)- "w":向命令输入数据(父进程 -> 子进程stdin) |
成功:返回指向管道的文件指针(FILE*) 失败:返回 NULL,并设置errno |
<stdio.h> |
1. 创建管道和子进程,执行命令,返回文件指针用于读写 2. 需用 pclose关闭,pclose会等待命令执行完毕3. 示例: FILE *fp = popen("ls -l", "r");读取ls -l的输出 |
int pclose(FILE *stream) |
关闭popen创建的管道文件指针,等待子进程(命令)执行完毕,回收子进程资源 |
- stream:FILE*,popen返回的文件指针 |
成功:返回命令的退出状态 失败:返回-1,并设置 errno |
<stdio.h> |
1. 关闭管道,等待子进程退出 2. 不可用 fclose替代(fclose不等待子进程) |
void exit(int status) |
终止当前进程,刷新所有打开的流缓冲区,关闭文件,释放进程资源;调用终止处理函数 | - status:int,进程退出状态(0表示正常,非0表示异常) |
无返回值 | <stdlib.h> |
1. 刷新所有打开的流缓冲区,关闭文件,释放资源 2. 调用注册的终止处理函数( atexit注册)3. 区别于 _exit:_exit不刷新缓冲区,直接终止 |
void _exit(int status) |
立即终止当前进程,不刷新缓冲区,不调用终止处理函数,直接释放内核资源 | - status:int,退出状态 |
无返回值 | <unistd.h> |
1. 立即终止进程,不刷新缓冲区,不调用终止处理函数 2. 常用于子进程(避免刷新父进程的缓冲区) |
示例
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main() {printf("父进程启动,PID=%d\n", getpid());// 创建子进程pid_t pid = fork();if (pid < 0) {perror("fork失败");exit(1);}// 子进程逻辑if (pid == 0) {printf("子进程启动,PID=%d,父进程PID=%d\n", getpid(), getppid());// 子进程执行任务(示例:休眠2秒后退出)sleep(2);printf("子进程执行完毕,即将退出\n");exit(0); // 子进程退出,返回状态码0}// 父进程逻辑(继续执行)printf("父进程等待子进程(PID=%d)退出...\n", pid);int status;// 阻塞等待子进程退出,回收资源(避免僵尸进程)waitpid(pid, &status, 0);// 解析子进程退出状态if (WIFEXITED(status)) {printf("父进程:子进程正常退出,退出码=%d\n", WEXITSTATUS(status));}printf("父进程执行完毕,退出\n");return 0;
}
线程操作函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) |
创建新线程,指定线程执行函数和参数;新线程与主线程并发执行,共享进程资源 | - thread:pthread_t*,存储新线程的TID(线程ID)- attr:const pthread_attr_t*,线程属性(NULL表示默认属性)- start_routine:void*(*)(void*),线程函数指针(函数返回void,参数为void)- arg:void*,传递给线程函数的参数(需强制类型转换) |
成功:返回0 失败:返回错误码(非-1,需用 strerror打印错误) |
<pthread.h> |
1. 编译需加-lpthread选项(如gcc test.c -o test -lpthread)2. 线程函数返回的void*需为全局/堆变量(避免栈变量销毁) 3. 线程默认joinable(需 pthread_join回收) |
void pthread_exit(void *retval) |
终止当前线程,释放线程的栈资源;返回值通过pthread_join传递给回收线程 |
- retval:void*,线程返回值(传递给pthread_join) |
无返回值 | <pthread.h> |
1. 终止当前线程,释放线程资源(但进程资源需pthread_join回收)2. retval不可为栈变量(线程退出后栈释放) |
int pthread_join(pthread_t thread, void **retval) |
阻塞等待指定线程退出,回收线程资源,获取线程返回值;仅支持joinable状态的线程 | - thread:pthread_t,要回收的线程TID- retval:void**,存储线程返回值(pthread_exit的参数);若为NULL,不关心返回值 |
成功:返回0 失败:返回错误码 |
<pthread.h> |
1. 阻塞等待:调用线程暂停,直到thread线程退出2. 仅能回收joinable状态的线程;detach状态的线程不可join 3. 示例: pthread_join(tid, (void**)&ret);获取线程返回值ret |
int pthread_detach(pthread_t thread) |
将指定线程设置为detach状态,线程退出后自动释放资源,无需pthread_join回收 |
- thread:pthread_t,要设置为detach状态的线程TID |
成功:返回0 失败:返回错误码 |
<pthread.h> |
1. 设置线程为detach状态,退出后自动释放资源(无需pthread_join)2. 不可对已join的线程调用(会失败) 3. 也可在创建时通过属性设置detach( pthread_attr_setdetachstate) |
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) |
初始化互斥锁,用于保护多线程共享的临界资源,避免线程竞争导致的数据不一致 | - mutex:pthread_mutex_t*,互斥锁变量- attr:const pthread_mutexattr_t*,互斥锁属性(NULL为默认属性) |
成功:返回0 失败:返回错误码 |
<pthread.h> |
1. 互斥锁用于保护临界资源(多线程共享数据) 2. 也可静态初始化: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
int pthread_mutex_lock(pthread_mutex_t *mutex) |
获取互斥锁,若锁已被占用则阻塞等待;成功获取后进入临界区,确保临界区代码独占执行 | - mutex:pthread_mutex_t*,互斥锁变量 |
成功:返回0(获取锁) 失败:返回错误码 |
<pthread.h> |
1. 阻塞等待:若锁已被占用,线程暂停,直到锁释放 2. 临界区代码需放在 lock和unlock之间,避免竞争 |
int pthread_mutex_unlock(pthread_mutex_t *mutex) |
释放互斥锁,允许其他等待锁的线程竞争获取;仅持有锁的线程可释放 | - mutex:pthread_mutex_t*,互斥锁变量 |
成功:返回0(释放锁) 失败:返回错误码 |
<pthread.h> |
1. 仅持有锁的线程可释放(其他线程释放会失败) 2. 释放后,等待锁的线程竞争获取 |
示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 共享资源(临界资源)
int shared_num = 0;
// 互斥锁(保护共享资源)
pthread_mutex_t mutex;// 线程函数:累加共享变量
void* thread_func(void* arg) {int thread_id = *(int*)arg;for (int i = 0; i < 5000; i++) {// 加锁:进入临界区(独占访问共享资源)pthread_mutex_lock(&mutex);shared_num++;// 解锁:退出临界区(释放锁允许其他线程访问)pthread_mutex_unlock(&mutex);}printf("线程%d执行完毕,当前shared_num=%d\n", thread_id, shared_num);return NULL;
}int main() {pthread_t tid1, tid2;int id1 = 1, id2 = 2;// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建两个线程pthread_create(&tid1, NULL, thread_func, &id1);pthread_create(&tid2, NULL, thread_func, &id2);// 等待线程执行完毕pthread_join(tid1, NULL);pthread_join(tid2, NULL);// 销毁互斥锁pthread_mutex_destroy(&mutex);printf("主线程:最终shared_num=%d(预期10000)\n", shared_num);return 0;
}
管道操作函数
| 函数原型 | 功能说明 | 参数说明 | 返回值 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
int pipe(int fd[2]); |
创建匿名管道,生成读/写两个文件描述符 | - fd[2]:整型数组(固定长度2)- fd[0]:管道读端(仅用于read)- fd[1]:管道写端(仅用于write) |
成功返回0;失败返回 -1(设置errno) |
<unistd.h> |
1. 需在fork()前创建,子进程继承管道描述符;2. 不能多进程同时写(数据可能覆盖); 3. 管道无定位功能(仅支持顺序读写,不可用 lseek) |
ssize_t read(int fd, void *buf, size_t count); |
从管道读端读取数据 | - fd:管道读端描述符(fd[0])- buf:接收数据的缓冲区- count:缓冲区最大字节数 |
成功返回实际读取字节数; 0 = 管道写端关闭; 失败返回 -1 |
<unistd.h> |
1. 默认阻塞:管道为空时,读进程挂起等待; 2. 非阻塞模式下,管道为空返回 -1(errno=EAGAIN) |
ssize_t write(int fd, const void *buf, size_t count); |
向管道写端写入数据 | - fd:管道写端描述符(fd[1])- buf:待写入数据的缓冲区- count:待写入字节数 |
成功返回实际写入字节数; 失败返回 -1 |
<unistd.h> |
1. 默认阻塞:管道满时,写进程挂起等待; 2. 单个写操作≤PIPE_BUF(通常4096字节)时保证原子性; 3. 若读端已关闭,写操作会触发 SIGPIPE信号(进程默认终止) |
int close(int fd); |
关闭管道的读端/写端 | - fd:需关闭的描述符(fd[0]或fd[1]) |
成功返回0;失败返回 -1 |
<unistd.h> |
1. 需主动关闭无用端(如父进程写后关fd[1],避免子进程读阻塞);2. 两端均关闭后,管道资源被内核回收 |
int mkfifo(const char *pathname, mode_t mode); |
创建具名管道文件(仅需一次) | - pathname:管道文件路径(如/tmp/myfifo)- mode:文件权限(八进制,如0666) |
成功返回0;失败返回 -1(如路径已存在) |
<sys/types.h><sys/stat.h> |
1. 不可创建在Windows共享文件夹(不支持管道文件); 2. 实际权限 = mode - umask(需提前调整umask,如umask(0));3. 创建后需用 open打开才能读写 |
int open(const char *pathname, int flags); |
打开已创建的具名管道 | - pathname:管道文件路径(mkfifo的pathname)- flags:打开模式(必选+可选)- 必选: O_RDONLY(读)/ O_WRONLY(写)- 可选: O_NONBLOCK(非阻塞) |
成功返回文件描述符; 失败返回 -1 |
<fcntl.h> |
1. 默认阻塞:仅读端打开时,写端需等待读端;反之亦然; 2. 用 O_RDWR打开可避免阻塞(但失去单向通信意义);3. 多进程可同时打开(支持多写一读,依赖写入原子性) |
ssize_t read(int fd, void *buf, size_t count); |
从具名管道读取数据 | 同匿名管道的read(参数/返回值一致) |
同匿名管道的read |
<unistd.h> |
1. 写入原子性:单个写操作≤PIPE_BUF时,数据不被分割(适用于日志系统等多进程写场景); 2. 其他特性同匿名管道 read |
ssize_t write(int fd, const void *buf, size_t count); |
向具名管道写入数据 | 同匿名管道的write(参数/返回值一致) |
同匿名管道的write |
<unistd.h> |
1. 多进程同时写时,≤PIPE_BUF的操作保证原子性(数据不覆盖); 2. 其他特性同匿名管道 write |
int close(int fd); |
关闭具名管道的文件描述符 | - fd:open返回的管道描述符 |
成功返回0;失败返回 -1 |
<unistd.h> |
1. 所有进程关闭后,管道文件仍存在(需手动rm删除);2. 关闭后描述符不可再用 |
匿名管道(父子进程通信)示例
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>int main() {int pipe_fd[2]; // 管道描述符:pipe_fd[0]读端,pipe_fd[1]写端// 创建匿名管道if (pipe(pipe_fd) == -1) {perror("pipe创建失败");return 1;}pid_t pid = fork();if (pid < 0) {perror("fork失败");return 1;}// 子进程:从管道读数据if (pid == 0) {close(pipe_fd[1]); // 子进程不写,关闭写端char buf[100] = {0};// 读管道(阻塞等待数据)ssize_t read_len = read(pipe_fd[0], buf, sizeof(buf)-1);if (read_len > 0) {printf("子进程收到数据:%s\n", buf);}close(pipe_fd[0]); // 关闭读端return 0;}// 父进程:向管道写数据close(pipe_fd[0]); // 父进程不读,关闭读端const char* msg = "Hello from 父进程!";write(pipe_fd[1], msg, strlen(msg));printf("父进程发送数据:%s\n", msg);close(pipe_fd[1]); // 关闭写端(子进程read会返回0)waitpid(pid, NULL, 0); // 等待子进程return 0;
}
具名管道(FIFO,任意进程通信)示例
写端程序(fifo_write.c)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>#define FIFO_PATH "/tmp/my_fifo" // 命名管道路径int main() {// 创建命名管道(若不存在)if (mkfifo(FIFO_PATH, 0666) == -1) {perror("mkfifo失败(可能已存在)");}// 打开管道(写模式,阻塞等待读端)int fd = open(FIFO_PATH, O_WRONLY);if (fd == -1) {perror("open失败");return 1;}// 向管道写数据const char* msg = "Hello from 命名管道写端!";write(fd, msg, strlen(msg));printf("写端发送:%s\n", msg);close(fd);return 0;
}
读端程序(fifo_read.c)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>#define FIFO_PATH "/tmp/my_fifo"int main() {// 打开管道(读模式,阻塞等待写端)int fd = open(FIFO_PATH, O_RDONLY);if (fd == -1) {perror("open失败");return 1;}// 从管道读数据char buf[100] = {0};ssize_t read_len = read(fd, buf, sizeof(buf)-1);if (read_len > 0) {printf("读端收到:%s\n", buf);}close(fd);return 0;
}
信号操作函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler) |
为指定信号注册处理函数,定义信号的响应方式(忽略、默认处理、自定义处理) | - signum:int,信号值(如SIGINT(2)、SIGCHLD(17))- handler:sighandler_t,信号处理函数:- SIG_IGN:忽略该信号- SIG_DFL:默认处理(如SIGINT默认终止进程)- 自定义函数: void func(int sig)(sig为信号值) |
成功:返回之前的信号处理函数指针 失败:返回 SIG_ERR(-1),并设置errno |
<signal.h> |
1. 常用信号: - SIGINT(2):Ctrl+C触发,默认终止- SIGCHLD(17):子进程退出触发,默认忽略- SIGKILL(9)、SIGSTOP(19):不可捕获、忽略2. 自定义处理函数执行时,会自动屏蔽当前信号(避免重入) |
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) |
修改进程的信号阻塞集,实现信号的屏蔽(暂不处理)或解除屏蔽(恢复处理) | - how:int,操作类型:- SIG_BLOCK:将set中的信号添加到阻塞集- SIG_UNBLOCK:从阻塞集中删除set中的信号- SIG_SETMASK:用set替换阻塞集- set:const sigset_t,要操作的信号集;若为NULL,仅获取当前阻塞集- oldset:sigset_t,存储原阻塞集;若为NULL,不保存 |
成功:返回0 失败:返回-1,并设置 errno |
<signal.h> |
1. 信号集操作需配合以下函数: - sigemptyset:清空信号集- sigaddset:添加信号到集- sigismember:检查信号是否在集2. 阻塞的信号不会被丢弃,解除阻塞后会被处理 |
int sigemptyset(sigset_t *set) |
初始化信号集,将信号集清空(不包含任何信号),为后续添加信号做准备 | - set:sigset_t*,要清空的信号集 |
成功:返回0 失败:返回-1,并设置 errno |
<signal.h> |
初始化信号集为空(无任何信号) |
int sigaddset(sigset_t *set, int signum) |
将指定信号添加到信号集中,用于构建需要操作的信号集合(如阻塞、解除阻塞) | - set:sigset_t*,信号集- signum:int,要添加的信号值 |
成功:返回0 失败:返回-1,并设置 errno |
<signal.h> |
向信号集中添加指定信号 |
int sigismember(const sigset_t *set, int signum) |
检查指定信号是否包含在信号集中,用于判断信号是否处于阻塞或待处理状态 | - set:const sigset_t*,信号集- signum:int,要检查的信号值 |
成功: - 信号在集中:返回1 - 信号不在集中:返回0 失败:返回-1,并设置 errno |
<signal.h> |
判断信号是否在指定信号集中 |
示例 1:自定义信号处理函数(捕获 Ctrl+C)
#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 自定义信号处理函数(捕获SIGINT信号)
void sigint_handler(int sig) {printf("\n收到信号%d(Ctrl+C),程序不退出,3秒后继续运行...\n", sig);sleep(3); // 模拟处理耗时
}int main() {// 注册SIGINT信号的处理函数(默认处理是终止进程)if (signal(SIGINT, sigint_handler) == SIG_ERR) {perror("signal register failed");return 1;}printf("程序运行中,按Ctrl+C测试信号处理(输入q退出)...\n");char c;while ((c = getchar()) != 'q') {// 循环等待,直到输入q退出}printf("程序正常退出\n");return 0;
}
示例 2:信号阻塞与解除阻塞
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void sigint_handler(int sig) {printf("收到信号%d(Ctrl+C)\n", sig);
}int main() {sigset_t sig_set, old_set;// 1. 注册信号处理函数signal(SIGINT, sigint_handler);// 2. 初始化信号集并添加SIGINTsigemptyset(&sig_set); // 清空信号集sigaddset(&sig_set, SIGINT); // 添加SIGINT到信号集// 3. 阻塞SIGINT信号(期间收到该信号会暂存,不处理)if (sigprocmask(SIG_BLOCK, &sig_set, &old_set) < 0) {perror("sigprocmask block failed");return 1;}printf("SIGINT信号已阻塞,接下来5秒内按Ctrl+C无响应...\n");sleep(5);// 4. 解除SIGINT信号阻塞(暂存的信号会被处理)if (sigprocmask(SIG_UNBLOCK, &sig_set, NULL) < 0) {perror("sigprocmask unblock failed");return 1;}printf("SIGINT信号已解除阻塞,按Ctrl+C测试...\n");// 等待信号sleep(10);return 0;
}
互斥锁、读写锁操作函数
| 函数原型 | 功能说明 | 参数说明 | 返回值 | 头文件 |
|---|---|---|---|---|
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); |
初始化互斥锁 | - mutex:互斥锁指针- attr:属性(默认NULL) |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_mutex_destroy(pthread_mutex_t *mutex); |
销毁互斥锁 | - mutex:互斥锁指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_mutex_lock(pthread_mutex_t *mutex); |
阻塞获取互斥锁(临界区入口) | - mutex:互斥锁指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_mutex_trylock(pthread_mutex_t *mutex); |
非阻塞获取互斥锁 | - mutex:互斥锁指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_mutex_unlock(pthread_mutex_t *mutex); |
释放互斥锁(临界区出口) | - mutex:互斥锁指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); |
初始化读写锁 | - rwlock:读写锁指针- attr:属性(默认NULL) |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); |
阻塞获取读锁(共享锁) | - rwlock:读写锁指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); |
阻塞获取写锁(排他锁) | - rwlock:读写锁指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); |
释放读写锁 | - rwlock:读写锁指针 |
成功返回0,失败返回错误码 | <pthread.h> |
互斥锁示例(多线程安全访问共享变量)
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 共享变量(临界资源)
int g_count = 0;
// 互斥锁(全局变量,确保多线程可见)
pthread_mutex_t g_mutex;// 线程函数:循环累加共享变量
void *thread_func(void *arg) {int id = *(int *)arg;for (int i = 0; i < 10000; i++) {// 1. 加锁:进入临界区(独占访问共享资源)pthread_mutex_lock(&g_mutex);g_count++;// 2. 解锁:退出临界区(释放锁,允许其他线程访问)pthread_mutex_unlock(&g_mutex);}printf("线程%d执行完毕,g_count = %d\n", id, g_count);return NULL;
}int main() {pthread_t tid1, tid2;int id1 = 1, id2 = 2;// 3. 初始化互斥锁(默认属性)pthread_mutex_init(&g_mutex, NULL);// 创建两个线程pthread_create(&tid1, NULL, thread_func, &id1);pthread_create(&tid2, NULL, thread_func, &id2);// 等待线程执行完毕pthread_join(tid1, NULL);pthread_join(tid2, NULL);// 4. 销毁互斥锁pthread_mutex_destroy(&g_mutex);printf("主线程:最终g_count = %d(正确结果应为20000)\n", g_count);return 0;
}
读写锁示例(多读少写场景优化)
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>int g_data = 10;
pthread_rwlock_t g_rwlock;// 读线程:共享访问数据(无修改)
void *read_thread(void *arg) {int id = *(int *)arg;while (1) {// 加读锁(多个读线程可同时获取)pthread_rwlock_rdlock(&g_rwlock);printf("读线程%d:g_data = %d\n", id, g_data);pthread_rwlock_unlock(&g_rwlock);sleep(1); // 模拟读操作耗时}return NULL;
}// 写线程:排他访问数据(修改数据)
void *write_thread(void *arg) {int id = *(int *)arg;while (1) {// 加写锁(仅一个写线程可获取,读线程需等待)pthread_rwlock_wrlock(&g_rwlock);g_data++;printf("=== 写线程%d:修改g_data为%d ===\n", id, g_data);pthread_rwlock_unlock(&g_rwlock);sleep(3); // 模拟写操作耗时}return NULL;
}int main() {pthread_t tid[4];int ids[4] = {1,2,3,4};pthread_rwlock_init(&g_rwlock, NULL);// 创建2个读线程、2个写线程pthread_create(&tid[0], NULL, read_thread, &ids[0]);pthread_create(&tid[1], NULL, read_thread, &ids[1]);pthread_create(&tid[2], NULL, write_thread, &ids[2]);pthread_create(&tid[3], NULL, write_thread, &ids[3]);// 等待线程(实际中可按需求终止)for (int i = 0; i < 4; i++) {pthread_join(tid[i], NULL);}pthread_rwlock_destroy(&g_rwlock);return 0;
}
POSIX信号量操作函数
| 函数原型 | 功能说明 | 参数说明 | 返回值 | 头文件 |
|---|---|---|---|---|
int sem_init(sem_t *sem, int pshared, unsigned int value); |
初始化匿名信号量 | - sem:信号量指针- pshared:0=线程间使用,非0=进程间使用- value:信号量初始值(资源数量) |
成功返回0,失败返回-1 | <semaphore.h> |
int sem_destroy(sem_t *sem); |
销毁匿名信号量 | - sem:信号量指针 |
成功返回0,失败返回-1 | <semaphore.h> |
int sem_post(sem_t *sem); |
V操作(释放资源,信号量+1) | - sem:信号量指针 |
成功返回0,失败返回-1 | <semaphore.h> |
int sem_wait(sem_t *sem); |
P操作(申请资源,信号量-1,阻塞) | - sem:信号量指针 |
成功返回0,失败返回-1 | <semaphore.h> |
int sem_trywait(sem_t *sem); |
P操作(非阻塞版,资源不足返回失败) | - sem:信号量指针 |
成功返回0,失败返回-1 | <semaphore.h> |
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); |
创建/打开具名信号量(进程间用) | - name:信号量名称(/dev/shm/下)- oflag:打开标记(如O_CREAT)- mode:文件权限(创建时需指定)- value:初始值(创建时需指定) |
成功返回信号量指针,失败返回SEM_FAILED | <semaphore.h>、<fcntl.h> |
int sem_close(sem_t *sem); |
关闭具名信号量 | - sem:信号量指针 |
成功返回0,失败返回-1 | <semaphore.h> |
int sem_unlink(const char *name); |
删除具名信号量文件 | - name:信号量名称 |
成功返回0,失败返回-1 | <semaphore.h> |
匿名信号量示例(线程间同步)
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>sem_t sem; // 匿名信号量(线程间共享)
int g_data = 0;// 线程A:生产数据(V操作释放资源)
void *thread_a(void *arg) {while (1) {g_data++;printf("线程A:生产数据=%d,发送信号\n", g_data);sem_post(&sem); // V操作:信号量+1(资源可用)sleep(2);}return NULL;
}// 线程B:消费数据(P操作申请资源)
void *thread_b(void *arg) {while (1) {sem_wait(&sem); // P操作:信号量-1(无资源则阻塞)printf("线程B:接收信号,消费数据=%d\n", g_data);sleep(1);}return NULL;
}int main() {pthread_t tid1, tid2;// 初始化信号量:线程间使用(pshared=0),初始值=0sem_init(&sem, 0, 0);pthread_create(&tid1, NULL, thread_a, NULL);pthread_create(&tid2, NULL, thread_b, NULL);pthread_join(tid1, NULL);pthread_join(tid2, NULL);sem_destroy(&sem); // 销毁信号量return 0;
}
具名信号量示例(进程间同步)
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>int main() {// 创建具名信号量:名称"/my_sem",权限0666,初始值0sem_t *sem = sem_open("/my_sem", O_CREAT, 0666, 0);if (sem == SEM_FAILED) {perror("sem_open failed");return 1;}int data = 1;while (1) {printf("进程A:生产数据=%d,释放信号量\n", data);sem_post(sem); // V操作data++;sleep(3);}sem_close(sem);sem_unlink("/my_sem"); // 最后退出的进程删除信号量return 0;
}
条件量操作函数
| 函数原型 | 功能说明 | 参数说明 | 返回值 | 头文件 |
|---|---|---|---|---|
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); |
初始化条件量 | - cond:条件量指针- attr:属性(默认NULL) |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_cond_destroy(pthread_cond_t *cond); |
销毁条件量 | - cond:条件量指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); |
阻塞等待条件满足(自动释放互斥锁) | - cond:条件量指针- mutex:关联的互斥锁 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); |
限时等待条件满足 | - cond:条件量指针- mutex:关联的互斥锁- abstime:超时时间(绝对时间) |
成功返回0,超时返回ETIMEDOUT,失败返回错误码 | <pthread.h> |
int pthread_cond_signal(pthread_cond_t *cond); |
唤醒一个等待的线程 | - cond:条件量指针 |
成功返回0,失败返回错误码 | <pthread.h> |
int pthread_cond_broadcast(pthread_cond_t *cond); |
广播唤醒所有等待的线程 | - cond:条件量指针 |
成功返回0,失败返回错误码 | <pthread.h> |
条件量示例(生产者 - 消费者模型)
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>#define BUF_SIZE 3 // 缓冲区大小
int g_buf[BUF_SIZE]; // 共享缓冲区
int g_idx = 0; // 缓冲区当前索引pthread_mutex_t g_mutex; // 保护缓冲区的互斥锁
pthread_cond_t g_not_full; // 缓冲区非满条件(生产者等待)
pthread_cond_t g_not_empty; // 缓冲区非空条件(消费者等待)// 生产者线程
void *producer(void *arg) {int data = 1;while (1) {pthread_mutex_lock(&g_mutex);// 缓冲区满则等待(释放互斥锁,避免死锁)while (g_idx == BUF_SIZE) {pthread_cond_wait(&g_not_full, &g_mutex);}// 生产数据到缓冲区g_buf[g_idx++] = data;printf("生产者:生产数据%d,缓冲区索引=%d\n", data, g_idx);data++;pthread_mutex_unlock(&g_mutex);pthread_cond_signal(&g_not_empty); // 唤醒消费者(缓冲区非空)sleep(1);}return NULL;
}// 消费者线程
void *consumer(void *arg) {while (1) {pthread_mutex_lock(&g_mutex);// 缓冲区空则等待while (g_idx == 0) {pthread_cond_wait(&g_not_empty, &g_mutex);}// 消费缓冲区数据int data = g_buf[--g_idx];printf("消费者:消费数据%d,缓冲区索引=%d\n", data, g_idx);pthread_mutex_unlock(&g_mutex);pthread_cond_signal(&g_not_full); // 唤醒生产者(缓冲区非满)sleep(2);}return NULL;
}int main() {pthread_t tid_prod, tid_cons;// 初始化互斥锁和条件量pthread_mutex_init(&g_mutex, NULL);pthread_cond_init(&g_not_full, NULL);pthread_cond_init(&g_not_empty, NULL);pthread_create(&tid_prod, NULL, producer, NULL);pthread_create(&tid_cons, NULL, consumer, NULL);pthread_join(tid_prod, NULL);pthread_join(tid_cons, NULL);// 销毁资源pthread_mutex_destroy(&g_mutex);pthread_cond_destroy(&g_not_full);pthread_cond_destroy(&g_not_empty);return 0;
}
UDP通信操作函数
| 函数原型 | 功能说明 | 参数说明 | 返回值 | 头文件 |
|---|---|---|---|---|
int socket(int domain, int type, int protocol); |
创建网络通信套接字(UDP/TCP通用) | - domain:通信协议(AF_INET=IPv4)- type:套接字类型(SOCK_DGRAM=UDP)- protocol:协议控制(默认0) |
成功返回文件描述符,失败返回-1 | <sys/socket.h> |
ssize_t sendto(int sockfd, const void buf[], size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); |
UDP发送数据 | - sockfd:套接字描述符- buf:发送数据缓冲区- len:数据长度- flags:特殊标记(默认0)- dest_addr:目标地址结构体- addrlen:地址长度 |
成功返回发送字节数,失败返回-1 | <sys/socket.h> |
ssize_t recvfrom(int sockfd, void buf[], size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); |
UDP接收数据 | - sockfd:套接字描述符- buf:接收数据缓冲区- len:缓冲区大小- flags:特殊标记(默认0)- src_addr:源地址结构体- addrlen:地址长度(输入输出型) |
成功返回接收字节数,失败返回-1 | <sys/socket.h> |
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
绑定套接字与地址(UDP接收端必需) | - sockfd:套接字描述符- addr:本地地址结构体- addrlen:地址长度 |
成功返回0,失败返回-1 | <sys/socket.h> |
int inet_aton(const char *cp, struct in_addr *inp); |
字符串IP转网络字节序整型 | - cp:点分十进制IP字符串- inp:存储结果的in_addr结构体指针 |
成功返回1,失败返回0 | <arpa/inet.h> |
in_addr_t inet_addr(const char *cp); |
字符串IP转网络字节序整型(简化版) | - cp:点分十进制IP字符串 |
成功返回网络字节序整型,失败返回INADDR_NONE | <arpa/inet.h> |
char *inet_ntoa(struct in_addr in); |
网络字节序整型转字符串IP | - in:in_addr结构体(含网络字节序IP) |
成功返回IP字符串指针(静态缓冲区) | <arpa/inet.h> |
uint32_t htonl(uint32_t hostlong); |
主机字节序32位整数转网络字节序 | - hostlong:主机字节序32位整数 |
返回网络字节序32位整数 | <arpa/inet.h> |
uint16_t htons(uint16_t hostshort); |
主机字节序16位整数转网络字节序 | - hostshort:主机字节序16位整数 |
返回网络字节序16位整数 | <arpa/inet.h> |
uint32_t ntohl(uint32_t netlong); |
网络字节序32位整数转主机字节序 | - netlong:网络字节序32位整数 |
返回主机字节序32位整数 | <arpa/inet.h> |
uint16_t ntohs(uint16_t netshort); |
网络字节序16位整数转主机字节序 | - netshort:网络字节序16位整数 |
返回主机字节序16位整数 | <arpa/inet.h> |
UDP 服务器端(接收端)示例
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define PORT 65000 // 绑定的端口号
#define BUF_SIZE 128 // 缓冲区大小int main() {// 1. 创建UDP套接字int udp_fd = socket(AF_INET, SOCK_DGRAM, 0);if (udp_fd < 0) {perror("socket create failed");return 1;}// 2. 配置本地地址(服务器端必须绑定端口)struct sockaddr_in local_addr = {.sin_family = AF_INET, // IPv4协议.sin_port = htons(PORT), // 主机字节序转网络字节序(端口).sin_addr.s_addr = INADDR_ANY // 绑定所有网卡IP(INADDR_ANY=0.0.0.0)};// 3. 绑定套接字与地址if (bind(udp_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {perror("bind failed");close(udp_fd);return 1;}printf("UDP服务器启动,监听端口%d...\n", PORT);// 4. 循环接收数据char buf[BUF_SIZE] = {0};struct sockaddr_in client_addr; // 存储客户端地址socklen_t client_addr_len = sizeof(client_addr);while (1) {bzero(buf, sizeof(buf)); // 清空缓冲区// 接收客户端数据(阻塞等待)ssize_t recv_len = recvfrom(udp_fd, buf, sizeof(buf)-1, 0,(struct sockaddr *)&client_addr, &client_addr_len);if (recv_len < 0) {perror("recvfrom failed");continue;}// 打印客户端信息和数据printf("收到客户端[%s:%d]的数据(%zd字节):%s\n",inet_ntoa(client_addr.sin_addr), // 网络字节序IP转字符串ntohs(client_addr.sin_port), // 网络字节序端口转主机字节序recv_len, buf);}// 5. 关闭套接字(实际中不会执行到)close(udp_fd);return 0;
}
UDP 客户端(发送端)示例
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define SERVER_IP "127.0.0.1" // 服务器IP(本地回环地址)
#define SERVER_PORT 65000 // 服务器端口(需与服务器一致)
#define BUF_SIZE 128int main() {// 1. 创建UDP套接字int udp_fd = socket(AF_INET, SOCK_DGRAM, 0);if (udp_fd < 0) {perror("socket create failed");return 1;}// 2. 配置服务器地址struct sockaddr_in server_addr = {.sin_family = AF_INET,.sin_port = htons(SERVER_PORT),.sin_addr.s_addr = inet_addr(SERVER_IP) // 字符串IP转网络字节序};// 3. 循环发送数据char buf[BUF_SIZE] = {0};while (1) {bzero(buf, sizeof(buf));printf("请输入要发送的数据(输入q退出):");fgets(buf, sizeof(buf)-1, stdin);// 退出条件if (strcmp(buf, "q\n") == 0) {printf("客户端退出\n");break;}// 发送数据到服务器ssize_t send_len = sendto(udp_fd, buf, strlen(buf), 0,(struct sockaddr *)&server_addr, sizeof(server_addr));if (send_len < 0) {perror("sendto failed");continue;}printf("发送成功(%zd字节)\n", send_len);}// 4. 关闭套接字close(udp_fd);return 0;
}
其他基础函数
| 函数原型 | 功能描述 | 参数说明 | 返回值说明 | 头文件 | 关键注意事项 |
|---|---|---|---|---|---|
int printf(const char *format, ...) |
按指定格式将可变参数数据输出到标准输出(屏幕),等价于fprintf(stdout, format, ...) |
- format:const char*,格式控制字符串- ...:可变参数,与format的格式符对应 |
成功:返回实际输出的字节数 失败:返回负数 |
<stdio.h> |
1. 等价于fprintf(stdout, format, ...)2. 标准输出默认行缓冲,加 \n或fflush(stdout)可刷新 |
int scanf(const char *format, ...) |
按指定格式从标准输入(键盘)读取数据,存储到指定变量,等价于fscanf(stdin, format, ...) |
- format:const char*,格式控制字符串- ...:可变参数,需传变量地址(如&a) |
成功:返回成功赋值的参数个数 失败/EOF:返回 EOF |
<stdio.h> |
1. 等价于fscanf(stdin, format, ...)2. 读取字符串用 %s,遇空白符停止;需确保缓冲区足够大 |
int getchar(void) |
从标准输入(键盘)读取单个字符,等价于fgetc(stdin),支持字符级输入 |
无参数 | 成功:返回读取的字符(ASCII值) 失败/EOF:返回 EOF |
<stdio.h> |
1. 等价于fgetc(stdin)2. 每次读取1个字符,包括换行符 \n |
int putchar(int c) |
向标准输出(屏幕)写入单个字符,等价于fputc(c, stdout),支持字符级输出 |
- c:int,要输出的字符(ASCII值) |
成功:返回输出的字符 失败:返回 EOF |
<stdio.h> |
1. 等价于fputc(c, stdout)2. 每次输出1个字符 |
int access(const char *pathname, int mode) |
检查指定文件是否存在,以及当前进程对文件的访问权限(读、写、执行) | - pathname:const char*,文件路径/名称- mode:int,检查的权限:- F_OK:检查文件是否存在- R_OK:检查是否可读- W_OK:检查是否可写- X_OK:检查是否可执行 |
成功:所有检查的权限都满足,返回0 失败:至少一个权限不满足,返回-1,并设置 errno |
<unistd.h> |
1. 检查当前进程对文件的权限(基于进程的UID/GID) 2. 示例:`access("test.txt", F_OK |