ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

完整且详细的C语言教程,从入门到精通(5.3)--C语言常用函数(2)

完整且详细的C语言教程,从入门到精通(5.3)--C语言常用函数(2) 你们好我是一名嵌入式工程师。我正在持续更新《嵌入式工程师的C语言实战》系列教程目前已更新7篇。如果你也在学习嵌入式欢迎点个关注我们一起从零到一。文章目录前言一、strcpy —— 字符串复制头文件函数原型功能参数返回值重要警告示例安全替代方案二、strlen —— 获取字符串长度头文件函数原型功能参数返回值示例重要提醒三、malloc —— 动态内存分配头文件函数原型功能参数返回值重要注意事项示例常见应用场景四、free —— 释放动态内存头文件函数原型功能参数返回值重要注意事项示例五、rand —— 生成随机数头文件函数原型功能参数返回值重要注意事项示例生成特定范围随机数的公式六、memset —— 设置内存块的值头文件函数原型功能参数返回值常见用途示例警告七、memcpy —— 内存复制头文件函数原型功能参数返回值重要注意事项示例memcpy vs strcpy综合示例实战应用总结对比表最佳实践总结如果这篇文章对你有帮助欢迎点赞、收藏、关注主播是一个马上粉丝量超1个的嵌入式内容主播。谢谢前言strcpy、strlen、malloc、free、rand、memset、memcpy这7个函数是C语言中字符串操作、内存管理和工具函数的核心成员。一、strcpy —— 字符串复制头文件#includestring.h函数原型char*strcpy(char*dest,constchar*src);功能将源字符串src包括结尾的\0复制到目标字符串dest中。参数dest目标字符串的指针必须有足够的空间容纳src。src源字符串的指针必须以\0结尾。返回值返回dest的指针便于链式调用。重要警告strcpy不检查目标缓冲区大小如果src比dest的空间大会导致缓冲区溢出严重安全漏洞。示例#includestdio.h#includestring.hintmain(){charsrc[]Hello, World!;chardest[20];// 必须足够大strcpy(dest,src);printf(复制后: %s\n,dest);// 输出: Hello, World!// 链式调用charbuffer[50];strcpy(buffer,strcpy(dest,C语言));printf(buffer: %s\n,buffer);// 输出: C语言printf(dest: %s\n,dest);// 输出: C语言return0;}安全替代方案建议使用strncpy指定最大复制长度chardest[10];strncpy(dest,Hello, World!,sizeof(dest)-1);dest[sizeof(dest)-1]\0;// 确保结尾有 \0二、strlen —— 获取字符串长度头文件#includestring.h函数原型size_tstrlen(constchar*s);功能计算字符串的长度不包含结尾的\0即从起始地址到第一个\0之前的字符个数。参数s以\0结尾的字符串。返回值返回字符串的长度类型为size_t无符号整数。示例#includestdio.h#includestring.hintmain(){charstr1[]Hello;charstr2[]中文测试;// 中文字符在 UTF-8 下占多个字节printf(%s 的长度: %zu\n,str1,strlen(str1));// 输出 5printf(%s 的长度: %zu\n,str2,strlen(str2));// 输出 12 (UTF-8)// sizeof vs strlenchararr[20]Hello;printf(sizeof(arr) %zu\n,sizeof(arr));// 20 (整个数组大小)printf(strlen(arr) %zu\n,strlen(arr));// 5 (字符串长度)// 手动实现 strlen 的思路constchar*pTest;size_tlen0;while(*p!\0){len;p;}printf(手动计算: %zu\n,len);// 输出 4return0;}重要提醒strlen返回的是字符数不是字节数对多字节字符集需注意。传入的字符串必须保证以\0结尾否则结果不可预知。三、malloc —— 动态内存分配头文件#includestdlib.h函数原型void*malloc(size_tsize);功能在**堆heap**上分配size个字节的连续内存空间并返回指向该内存块的指针。参数size要分配的字节数。返回值成功时返回指向已分配内存的指针void*可强制转换为任意类型。失败时内存不足返回NULL。重要注意事项分配的内存内容未初始化包含垃圾值。必须检查返回值是否为NULL。使用完后必须调用free释放否则造成内存泄漏。分配的大小要用sizeof计算避免硬编码。示例#includestdio.h#includestdlib.hintmain(){int*arr;intn5;// 分配 n 个 int 类型的内存arr(int*)malloc(n*sizeof(int));if(arrNULL){printf(内存分配失败\n);return1;}// 使用内存注意此时值未初始化for(inti0;in;i){arr[i]i*10;printf(%d ,arr[i]);}printf(\n);// 释放内存free(arr);arrNULL;// 避免野指针return0;}常见应用场景在运行时才知道需要多大的数组。创建链表、树等动态数据结构。处理可变长度的数据如从文件读取内容。四、free —— 释放动态内存头文件#includestdlib.h函数原型voidfree(void*ptr);功能释放之前由malloc、calloc或realloc分配的内存将其归还给系统。参数ptr指向要释放的内存块的指针必须是之前动态分配的。返回值无。重要注意事项只能释放动态分配的内存不能释放栈上的变量。不能重复释放double free同一块内存会导致未定义行为。释放后最好将指针置为NULL避免野指针。释放NULL是安全的不会产生错误。示例#includestdio.h#includestdlib.hintmain(){char*buffer;// 分配 100 字节buffer(char*)malloc(100*sizeof(char));if(bufferNULL){printf(分配失败\n);return1;}// 使用内存sprintf(buffer,动态分配的字符串);printf(%s\n,buffer);// 释放内存free(buffer);bufferNULL;// 安全实践// free(NULL) 是安全的free(NULL);// 什么也不做return0;}五、rand —— 生成随机数头文件#includestdlib.h函数原型intrand(void);功能生成一个0到RAND_MAX之间的伪随机整数。RAND_MAX通常定义为32767。参数无。返回值返回一个伪随机整数。重要注意事项默认种子是 1每次运行程序生成的随机数序列相同。需要用srand设置不同的种子通常用当前时间。rand的随机质量有限对安全性要求高的场景需用专用库。示例#includestdio.h#includestdlib.h#includetime.h// 用于 time()intmain(){// 设置随机种子使用当前时间srand((unsignedint)time(NULL));// 生成 10 个随机数printf(10 个随机数:\n);for(inti0;i10;i){printf(%d ,rand());}printf(\n);// 生成 1~100 之间的随机数printf(\n1~100 的随机数:\n);for(inti0;i10;i){intnumrand()%1001;printf(%d ,num);}printf(\n);// 生成 0.0~1.0 之间的随机浮点数printf(\n0.0~1.0 随机浮点数:\n);for(inti0;i5;i){doublef(double)rand()/RAND_MAX;printf(%.3f ,f);}printf(\n);return0;}生成特定范围随机数的公式范围公式0 ~ N-1rand() % N1 ~ Nrand() % N 1min ~ maxrand() % (max - min 1) min0.0 ~ 1.0(double)rand() / RAND_MAX六、memset —— 设置内存块的值头文件#includestring.h函数原型void*memset(void*s,intc,size_tn);功能将内存块s的前n个字节全部设置为c按字节填充。参数s指向要填充的内存块的指针。c要填充的值会被转换为unsigned char。n要填充的字节数。返回值返回s的指针。常见用途初始化数组或结构体为 0。清空敏感数据如密码。重置缓冲区。示例#includestdio.h#includestring.hintmain(){// 1. 初始化数组为 0intarr[10];memset(arr,0,sizeof(arr));printf(arr[0] %d\n,arr[0]);// 输出 0// 2. 初始化数组为特定值注意按字节填充charbuffer[20];memset(buffer,A,10);// 前10个字节设为 Abuffer[10]\0;printf(buffer: %s\n,buffer);// 输出: AAAAAAAAAA// 3. 初始化结构体typedefstruct{intid;charname[50];floatscore;}Student;Student s;memset(s,0,sizeof(s));// 所有成员清零printf(s.id %d, s.score %.2f\n,s.id,s.score);// 4. 清空字符串charstr[100]Hello, World!;printf(清空前: %s\n,str);memset(str,0,sizeof(str));printf(清空后: %s (长度: %zu)\n,str,strlen(str));// 长度 0return0;}警告memset按字节填充对于int数组memset(arr, 1, sizeof(arr))不会将每个元素设为 1而是每个字节设为 1结果每个int的值是0x01010101即 16843009。只有填充0或-1时才适用于整数数组。七、memcpy —— 内存复制头文件#includestring.h函数原型void*memcpy(void*dest,constvoid*src,size_tn);功能将内存块src的前n个字节复制到dest中。参数dest目标内存指针。src源内存指针。n要复制的字节数。返回值返回dest的指针。重要注意事项不检查\0直接按字节复制n个字节。源和目标不能重叠重叠请用memmove。目标内存必须有足够的空间。示例#includestdio.h#includestring.hintmain(){// 1. 复制数组intsrc_arr[]{1,2,3,4,5};intdest_arr[5];memcpy(dest_arr,src_arr,sizeof(src_arr));printf(复制后的数组: );for(inti0;i5;i){printf(%d ,dest_arr[i]);}printf(\n);// 2. 复制字符串包括 \0charsrc_str[]Hello, World!;chardest_str[50];memcpy(dest_str,src_str,strlen(src_str)1);// 1 复制 \0printf(复制后的字符串: %s\n,dest_str);// 3. 复制结构体typedefstruct{intx;inty;}Point;Point p1{10,20};Point p2;memcpy(p2,p1,sizeof(Point));printf(p2: (%d, %d)\n,p2.x,p2.y);// 4. strcpy 与 memcpy 的区别chartext[]ABCDEFG;charbuf1[10],buf2[10];strcpy(buf1,text);// 复制直到遇到 \0memcpy(buf2,text,3);// 只复制前3个字节不自动加 \0buf2[3]\0;// 手动添加结束符printf(strcpy: %s\n,buf1);printf(memcpy: %s\n,buf2);// 输出: ABCreturn0;}memcpy vs strcpy对比项memcpystrcpy复制方式按字节数复制复制到\0为止是否检查\0否是能否复制二进制数据✅ 可以❌ 不行遇\0停止性能通常更快略慢需扫描\0安全性需手动控制长度不检查目标大小危险综合示例实战应用#includestdio.h#includestdlib.h#includestring.h#includetime.hintmain(){// 1. 动态分配内存intn10;int*numbers(int*)malloc(n*sizeof(int));if(numbersNULL){printf(内存分配失败\n);return1;}// 2. 初始化内存为 0memset(numbers,0,n*sizeof(int));// 3. 生成随机数填充srand((unsignedint)time(NULL));for(inti0;in;i){numbers[i]rand()%1001;// 1~100}// 4. 复制数组int*copy(int*)malloc(n*sizeof(int));if(copyNULL){printf(内存分配失败\n);free(numbers);return1;}memcpy(copy,numbers,n*sizeof(int));// 5. 输出结果printf(原始数组: );for(inti0;in;i){printf(%d ,numbers[i]);}printf(\n);printf(复制数组: );for(inti0;in;i){printf(%d ,copy[i]);}printf(\n);// 6. 统计数组长度类比 strlencharmsg[]Dynamic memory management;printf(消息: %s\n,msg);printf(消息长度: %zu\n,strlen(msg));// 7. 释放内存free(numbers);free(copy);numbersNULL;copyNULL;return0;}总结对比表函数头文件核心功能是否检查\0是否检查大小返回值strcpystring.h复制字符串✅ 是❌ 否目标指针strlenstring.h计算字符串长度✅ 是-size_tmallocstdlib.h分配内存--void*freestdlib.h释放内存--无randstdlib.h生成随机数--intmemsetstring.h按字节填充内存❌ 否✅ 需手动指定目标指针memcpystring.h按字节复制内存❌ 否✅ 需手动指定目标指针最佳实践总结字符串操作优先使用strncpy、strnlen等带n的安全版本。动态内存malloc后必须检查NULL使用后必须free释放后置NULL。随机数程序开始时调用一次srand(time(NULL))避免重复种子。内存操作memset用于清零memcpy用于复制二进制数据注意不要重叠。内存泄漏检测Linux 下可用valgrind工具检测。如果这篇文章对你有帮助欢迎点赞、收藏、关注主播是一个马上粉丝量超1个的嵌入式内容主播。谢谢
返回列表