函数速查表

函数速查表

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("%%"); %

示例

c 复制代码
#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)

示例

c 复制代码
#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,不足补0
    printf("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==bp==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`
12 &&(逻辑与) 逻辑与 2(布尔值/表达式) 左到右 a&&b 短路特性:左为假则右不执行,均为真返回1,否则0
13 ||(逻辑或) 逻辑或 2(布尔值/表达式) 左到右 `a
14 ?:(条件运算符) 三目运算 3(表达式1?表达式2:表达式3) 右到左 a>b?a:b 表达式1为真则取表达式2的值,否则取表达式3的值
15 = 赋值 2(变量=表达式) 右到左 a=3a=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`
16 ,(逗号运算符) 表达式分隔 多个(表达式1,表达式2,...) 左到右 a=3, b=4, a+b 从左到右依次执行,最终取最后一个表达式的值(结果为7)

字符串操作函数

函数原型 功能描述 参数说明 返回值说明 头文件 关键注意事项
char *strcpy(char *dest, const char *src) 将源字符串(含终止符\0)复制到目标缓冲区,覆盖目标缓冲区原有内容 - destchar*,目标缓冲区(可读写,需足够大) - srcconst char*,源字符串(只读,以\0结尾) 成功:返回dest(目标缓冲区首地址) 失败:无(但可能因缓冲区溢出导致程序崩溃) <string.h> 1. 不安全 :不检查dest长度,若src过长会溢出 2. 自动复制src\0dest
char *strncpy(char *dest, const char *src, size_t n) 将源字符串的前n个字符复制到目标缓冲区,是strcpy的安全版本,限制复制长度 - destchar*,目标缓冲区 - srcconst 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 - destchar*,目标字符串(可读写,以\0结尾,需足够大) - srcconst 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的安全版本 - destchar*,目标字符串 - src:const char*,源字符串 - n:size_t,最大拼接字节数 成功:返回dest 失败:无 <string.h> 1. 安全 :最多拼接n字节,自动在末尾加\0 2. 拼接长度 = min(strlen(src),n)
int strcmp(const char *s1, const char *s2) 按ASCII值逐字符比较两个字符串,直到遇到不同字符或\0,判断字符串的字典序关系 - s1const char*,第一个字符串 - s2const char*,第二个字符串 返回值: - <0s1字典序小于s2 - 0s1s2完全相同 - >0s1字典序大于s2 <string.h> 1. 按ASCII值逐字符比较,直到\0或不同字符 2. 区分大小写(如'A'(65)< 'a'(97))
size_t strlen(const char *s) 计算字符串的长度(字节数),从字符串起始位置到第一个\0为止,不包含\0 - sconst char*,字符串(以\0结尾) 成功:返回字符串长度(字节数,不含\0) 失败:无(sNULL会段错误) <string.h> 1. 时间复杂度O(n),需遍历到\0 2. 若字符串无\0,会越界访问(结果不确定)
char *strchr(const char *s, int c) 从字符串起始位置往后查找指定字符(含\0),返回首次出现该字符的指针 - sconst 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),返回最后一次出现该字符的指针 - sconst char*,字符串 - c:int,要查找的字符 成功:返回指向该字符最后一次出现位置的指针 失败:返回NULL <string.h> 1. 从字符串末尾往前查找(反向查找) 2. 其他特性同strchr
char *strstr(const char *haystack, const char *needle) 在主字符串中查找子字符串首次出现的位置,返回子字符串的起始指针;子字符串为空时返回主字符串 - haystackconst char*,主字符串(被查找的字符串) - needleconst char*,子字符串(要查找的字符串) 成功:返回指向子字符串第一次出现位置的指针 失败:返回NULL <string.h> 1. 若needle为空字符串,返回haystack(标准规定) 2. 区分大小写,不支持通配符
char *strtok(char *str, const char *delim) 按指定分隔符集合分割字符串,将分隔符替换为\0,返回当前分割的子字符串指针 - strchar*,要分割的字符串(首次调用传非NULL,后续传NULL) - delimconst char*,分隔符集合(如",. "表示逗号、句号、空格均为分隔符) 成功:返回当前分割出的子字符串指针 失败/分割完毕:返回NULL <string.h> 1. 修改原字符串 :将分隔符替换为\0 2. 非线程安全(同一进程多个线程调用会相互干扰) 3. 示例:char s[]="a,b.c"; strtok(s, ",.");返回"a",下次传NULL返回"b"
void *memcpy(void *dest, const void *src, size_t n) 按字节从源内存复制n字节数据到目标内存,不关心数据类型,支持任意内存块复制 - destvoid*,目标内存(可读写) - src:const void*,源内存(只读) - n:size_t,要复制的字节数 成功:返回dest 失败:无(src/destNULL会段错误) <string.h> 1. 按字节复制,不关心数据类型(可复制结构体、数组等) 2. 源/目标内存重叠时行为未定义 (需用memmove
void *memmove(void *dest, const void *src, size_t n) 按字节从源内存复制n字节数据到目标内存,内部处理内存重叠问题,是memcpy的安全版本 - destvoid*,目标内存 - src:const void*,源内存 - n:size_t,复制字节数 成功:返回dest 失败:无 <string.h> 1. 功能同memcpy,但内部会处理内存重叠(先将数据临时存储) 2. 比memcpy稍慢,但更安全
void *memset(void *s, int c, size_t n) 按字节将目标内存的前n字节设置为指定值c,常用于内存初始化(如清零) - svoid*,目标内存(可读写) - 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)

示例

c 复制代码
#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"复制到str3
    printf("2. strcpy(str3, str2) → str3 = %s\n", str3); // 输出World

    // 3. strncpy:安全复制(限制复制长度,避免溢出)
    char str4[10];
    strncpy(str4, str1, 3); // 复制str1的前3个字符到str4
    str4[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决定是否创建 - pathnameconst char*,指定文件的完整路径(如"/home/test.txt")或当前目录文件名(如"test.txt") - modeconst char*,打开模式,常用值: - "r":只读,文件不存在则失败 - "w":只写,文件不存在则创建,存在则清空内容 - "a":追加写,文件不存在则创建,写入从文件尾开始 - "r+":读写,文件不存在则失败 - "w+":读写,文件不存在则创建,存在则清空 - "a+":读写,文件不存在则创建,写入从文件尾开始 成功:返回指向FILE结构体的指针(文件指针),用于后续IO操作 失败:返回NULL,并设置errno(可通过perror打印错误) <stdio.h> 1. 文本文件与二进制文件打开模式一致(Linux下无区别) 2. 打开后必须调用fclose关闭,避免资源泄漏
int fclose(FILE *stream) 关闭已打开的文件指针,释放文件相关资源,并自动刷新缓冲区数据到文件 - streamFILE*fopen返回的文件指针 成功:返回0 失败:返回-1,并设置errno <stdio.h> 1. 关闭前会自动刷新缓冲区(全缓冲/行缓冲) 2. 关闭后stream指针变为野指针,不可再使用
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从指定文件中按数据块读取数据,存储到用户缓冲区,适用于二进制文件或批量数据读取 - ptrvoid*,用户缓冲区指针(用于存储读取到的数据,需提前分配内存) - size:size_t,单个数据块的字节数(如sizeof(int)) - nmemb:size_t,要读取的数据块数量 - streamFILE*,文件指针 成功:返回实际读取的数据块数量 (不是字节数) 失败/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) 将用户缓冲区中的数据按数据块写入指定文件,适用于二进制文件或批量数据写入 - ptrconst void*,用户缓冲区指针(存储要写入的数据) - size:size_t,单个数据块的字节数 - nmemb:size_t,要写入的数据块数量 - streamFILE*,文件指针 成功:返回实际写入的数据块数量 失败:返回0,并设置errno <stdio.h> 1. 写入字节数 = 返回值 × size 2. 全缓冲模式下,数据先存缓冲区,满了才写入文件;需fflush强制刷新
int fgetc(FILE *stream) 从指定文件中读取单个字符,文件指针自动向后移动,支持文本文件的字符级读取 - streamFILE*,文件指针(如stdin表示标准输入) 成功:返回读取到的字符(ASCII值,范围0-255) 失败/EOF:返回EOF(宏定义为-1) <stdio.h> 1. 每次读取1个字符,文件指针自动后移 2. 读取标准输入时,streamstdin(如fgetc(stdin)等价于getchar()
int fputc(int c, FILE *stream) 向指定文件中写入单个字符,文件指针自动向后移动,支持文本文件的字符级写入 - c:int,要写入的字符(ASCII值,低8位有效) - streamFILE*,文件指针(如stdout表示标准输出) 成功:返回写入的字符(ASCII值) 失败:返回EOF <stdio.h> 1. 每次写入1个字符,文件指针自动后移 2. 写入标准输出时,streamstdout(如fputc('A', stdout)等价于putchar('A')
char *fgets(char *s, int size, FILE *stream) 从指定文件中按行读取文本数据,存储到用户字符数组,自动添加字符串终止符\0 - schar*,用户字符数组(存储读取的行数据) - size:int,数组最大长度(需预留1字节存\0) - streamFILE*,文件指针 成功:返回s(数组首地址) 失败/EOF:返回NULL <stdio.h> 1. 读取到\nsize-1字节后停止,自动在末尾加\0 2. 会读取\n并存入数组(区别于gets) 3. 避免缓冲区溢出(size需小于数组长度)
int fputs(const char *s, FILE *stream) 将指定字符串写入目标文件,写入到字符串终止符\0为止,不自动添加换行符 - sconst char*,要写入的字符串(需以\0结尾) - streamFILE*,文件指针 成功:返回非负整数(具体值依赖系统) 失败:返回EOF <stdio.h> 1. 不自动添加\n(区别于puts) 2. 写入到\0停止,\0不写入文件
int fprintf(FILE *stream, const char *format, ...) 按指定格式将可变参数数据写入目标文件,支持格式化输出(如整数、字符串、浮点数) - streamFILE*,文件指针(如stdout表示屏幕输出) - format:const char*,格式控制字符串(含%d%s等) - ...:可变参数,与format中的格式符一一对应 成功:返回实际写入的字节数(不含\0) 失败:返回负数 <stdio.h> 1. 格式符需与参数类型匹配(如%d对应int) 2. 标准输出用fprintf(stdout, ...),等价于printf(...)
int fscanf(FILE *stream, const char *format, ...) 按指定格式从目标文件中读取数据,存储到指定变量,支持格式化输入 - streamFILE*,文件指针(如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) 调整文件指针的位置,实现文件的随机访问,支持从文件头、当前位置或文件尾偏移 - streamFILE*,文件指针 - 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) 获取当前文件指针相对于文件头的偏移量(字节数),用于记录文件位置或计算文件长度 - streamFILE*,文件指针 成功:返回当前位置到文件头的字节数 失败:返回-1L,并设置errno <stdio.h> 1. 常与fseek配合使用(如记录当前位置,后续恢复) 2. 文本文件中,换行符可能被转换(如Windows下\n存为\r\n),影响字节数计算
int fflush(FILE *stream) 强制刷新文件的缓冲区,将缓冲区中的数据立即写入文件,避免数据滞留丢失 - streamFILE*,文件指针;若为NULL,刷新所有打开的流 成功:返回0 失败:返回EOF,并设置errno <stdio.h> 1. 强制将缓冲区数据写入文件(适用于全缓冲/行缓冲) 2. 标准输出stdout默认行缓冲,加\nfflush(stdout)可刷新 3. 只读流(如"r"模式)调用fflush行为未定义

示例

c 复制代码
#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) { // 读到文件末尾返回NULL
        printf("%s", buffer); // 打印读取的内容
    }

    // 6. 再次关闭文件
    fclose(fp);
    fp = NULL;

    return 0;
}

系统IO函数

函数原型 功能描述 参数说明 返回值说明 头文件 关键注意事项
int open(const char *pathname, int flags, ...) 打开或创建指定文件,返回文件描述符用于底层IO操作;支持细粒度的打开选项(如非阻塞、追加) - pathnameconst char*,文件路径/名称(同fopen) - flags:int,打开选项(必选+可选组合,用` 连接): - 必选(三选一):O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写) - 可选:O_CREAT(文件不存在则创建,需传第3参数mode)、O_APPEND(追加写,每次写前移到文件尾)、O_TRUNC(文件存在则清空)、O_NONBLOCK(非阻塞模式) - ...:可变参数,仅当flagsO_CREAT时有效,为mode_t mode`(文件权限,如0644) 成功:返回文件描述符 (非负整数,0=标准输入,1=标准输出,2=标准错误) 失败:返回-1,并设置errno <fcntl.h>
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,文件描述符 - bufvoid*,用户缓冲区(存储读取的数据,需提前分配) - 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,文件描述符 - bufconst 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,偏移基准(同fseekSEEK_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)
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,再复制oldfdnewfd 2. 常用于重定向(如dup2(fd, 1)将标准输出重定向到fd对应的文件)

示例

c 复制代码
#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) { // 打开失败返回-1
        perror("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字节的堆内存,并初始化为0 2. 等价于malloc(nmemb×size) + memset(..., 0, ...) 3. 适用于创建数组(如calloc(5, sizeof(int))创建5个int的堆数组)
void *realloc(void *ptr, size_t size) 调整已有的堆内存块大小,可扩大或缩小;扩大时可能分配新内存并复制数据,缩小则截断内存 - ptrvoid*,已有的堆内存指针(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申请的堆内存,将内存归还给系统,避免内存泄漏 - ptrvoid*malloc/calloc/realloc返回的堆内存指针 无返回值 <stdlib.h> 1. 仅释放堆内存,不修改指针值(ptr变为野指针,需手动设为NULL) 2. 不可重复释放(同一指针释放多次会崩溃) 3. 不可释放栈内存(如局部变量地址)
void bzero(void *s, size_t n) 将目标内存的前n字节清零,功能等价于memset(s, 0, n),是BSD扩展函数 - svoid*,目标内存(可读写) - n:size_t,要清零的字节数 无返回值 <strings.h> 1. 功能同memset(s, 0, n),仅清零 2. 是BSD扩展函数,部分系统可能不支持(建议用memset保证可移植性)

示例

c 复制代码
#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,初始化为0
    if (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结尾 - pathconst char*,要执行的程序路径(如"/bin/ls") - argconst 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结尾,无需手动指定完整路径 - fileconst 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) 创建子进程执行指定的系统命令,父进程阻塞等待命令执行完毕;命令输出默认到标准输出 - commandconst char*,要执行的系统命令字符串(如"ls -l" 成功:返回命令的退出状态(需用WEXITSTATUS解析) 失败:返回-1,并设置errno <stdlib.h> 1. 内部创建子进程执行命令,父进程阻塞等待 2. 命令执行结果输出到标准输出(屏幕) 3. 不适用于需交互的命令(如"vi"
FILE *popen(const char *command, const char *type) 创建管道和子进程,执行系统命令;通过管道实现父进程与子进程的通信(读命令输出或写命令输入) - commandconst char*,系统命令字符串 - typeconst 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创建的管道文件指针,等待子进程(命令)执行完毕,回收子进程资源 - streamFILE*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. 常用于子进程(避免刷新父进程的缓冲区)

示例

c 复制代码
#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) 创建新线程,指定线程执行函数和参数;新线程与主线程并发执行,共享进程资源 - threadpthread_t*,存储新线程的TID(线程ID) - attrconst pthread_attr_t*,线程属性(NULL表示默认属性) - start_routinevoid*(*)(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传递给回收线程 - retvalvoid*,线程返回值(传递给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*,互斥锁变量 - attrconst 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. 临界区代码需放在lockunlock之间,避免竞争
int pthread_mutex_unlock(pthread_mutex_t *mutex) 释放互斥锁,允许其他等待锁的线程竞争获取;仅持有锁的线程可释放 - mutex:pthread_mutex_t*,互斥锁变量 成功:返回0(释放锁) 失败:返回错误码 <pthread.h> 1. 仅持有锁的线程可释放(其他线程释放会失败) 2. 释放后,等待锁的线程竞争获取

示例

c 复制代码
#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. 非阻塞模式下,管道为空返回-1errno=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:管道文件路径(mkfifopathname) - 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); 关闭具名管道的文件描述符 - fdopen返回的管道描述符 成功返回0; 失败返回-1 <unistd.h> 1. 所有进程关闭后,管道文件仍存在(需手动rm删除); 2. 关闭后描述符不可再用

匿名管道(父子进程通信)示例

c 复制代码
#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)

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)

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)

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:信号阻塞与解除阻塞

c 复制代码
#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. 初始化信号集并添加SIGINT
    sigemptyset(&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>

互斥锁示例(多线程安全访问共享变量)

c 复制代码
#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;
}

读写锁示例(多读少写场景优化)

c 复制代码
#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>

匿名信号量示例(线程间同步)

c 复制代码
#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),初始值=0
    sem_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;
}

具名信号量示例(进程间同步)

c 复制代码
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    // 创建具名信号量:名称"/my_sem",权限0666,初始值0
    sem_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>

条件量示例(生产者 - 消费者模型)

c 复制代码
#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 服务器端(接收端)示例

c 复制代码
#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 客户端(发送端)示例

c 复制代码
#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 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 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, ...) - formatconst char*,格式控制字符串 - ...:可变参数,与format的格式符对应 成功:返回实际输出的字节数 失败:返回负数 <stdio.h> 1. 等价于fprintf(stdout, format, ...) 2. 标准输出默认行缓冲,加\nfflush(stdout)可刷新
int scanf(const char *format, ...) 按指定格式从标准输入(键盘)读取数据,存储到指定变量,等价于fscanf(stdin, format, ...) - formatconst 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) 检查指定文件是否存在,以及当前进程对文件的访问权限(读、写、执行) - pathnameconst 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