C语言标准库与常用工具链:string.h、stdio.h、stdlib.h深度解析与CMake、Makefile构建

C语言标准库与常用工具链:string.h、stdio.h、stdlib.h深度解析与CMake、Makefile构建

《C语言标准库与常用工具链:string.h、stdio.h、stdlib.h深度解析与CMake、Makefile构建》

在这里插入图片描述

一、前言:为什么C标准库与工具链是C语言开发的基石?

学习目标

  • 理解C标准库的本质:C语言的标准函数库,提供了大量常用函数
  • 理解工具链的本质:用于编译、链接和调试C语言程序的工具集合
  • 明确C标准库与工具链的重要性:提高开发效率、简化程序结构
  • 掌握本章学习重点:string.h、stdio.h、stdlib.h的常用函数、CMake与Makefile的构建方法、工具链的使用技巧、避坑指南、实战案例分析
  • 学会使用C标准库编写高效、简洁的程序,使用工具链构建项目

重点提示

💡 C标准库与工具链是C语言开发的基石!通过C标准库,你可以直接使用大量常用函数,避免重复造轮子;通过工具链,你可以简化项目的构建和调试过程。


二、模块1:string.h深度解析——字符串处理的常用函数

2.1 学习目标

  • 理解字符串处理的本质:对字符串进行操作,如复制、拼接、比较
  • 掌握string.h的常用函数:strcpy、strncpy、strcat、strncat、strcmp、strncmp、strlen、strchr、strstr
  • 掌握字符串处理的避坑指南:避免字符串溢出、避免空指针
  • 避开string.h使用的3大常见坑

2.2 string.h的常用函数

字符串复制

#include<string.h>char*strcpy(char*dest,constchar*src);char*strncpy(char*dest,constchar*src,size_t n);

字符串拼接

#include<string.h>char*strcat(char*dest,constchar*src);char*strncat(char*dest,constchar*src,size_t n);

字符串比较

#include<string.h>intstrcmp(constchar*s1,constchar*s2);intstrncmp(constchar*s1,constchar*s2,size_t n);

字符串长度

#include<string.h>size_tstrlen(constchar*s);

代码示例1:使用string.h的常用函数

#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){char str1[50]="Hello, ";char str2[]="World!";char str3[50];// 字符串复制strncpy(str3, str1,sizeof(str3)); str3[sizeof(str3)-1]='\0';printf("str3: %s\n", str3);// 字符串拼接strncat(str3, str2,sizeof(str3)-strlen(str3)-1);printf("str3: %s\n", str3);// 字符串比较int cmp =strcmp(str1, str2);if(cmp <0){printf("str1 < str2\n");}elseif(cmp >0){printf("str1 > str2\n");}else{printf("str1 == str2\n");}// 字符串长度printf("str1的长度:%lu\n",strlen(str1));// 查找字符char*ptr =strchr(str1,',');if(ptr !=NULL){printf("找到字符 ',' 在位置:%ld\n", ptr - str1);}// 查找子字符串 ptr =strstr(str3,"World");if(ptr !=NULL){printf("找到子字符串 \"World\" 在位置:%ld\n", ptr - str3);}return0;}

三、模块2:stdio.h深度解析——标准输入输出的常用函数

3.1 学习目标

  • 理解标准输入输出的本质:与用户或文件进行数据交换
  • 掌握stdio.h的常用函数:printf、scanf、fprintf、fscanf、fopen、fclose、fread、fwrite
  • 掌握标准输入输出的避坑指南:避免文件打开失败、避免文件读取失败、避免文件写入失败
  • 避开stdio.h使用的3大常见坑

3.2 stdio.h的常用函数

控制台输入输出

#include<stdio.h>intprintf(constchar*format,...);intscanf(constchar*format,...);

文件输入输出

#include<stdio.h> FILE *fopen(constchar*filename,constchar*mode);intfclose(FILE *stream);size_tfread(void*ptr,size_t size,size_t nmemb, FILE *stream);size_tfwrite(constvoid*ptr,size_t size,size_t nmemb, FILE *stream);intfprintf(FILE *stream,constchar*format,...);intfscanf(FILE *stream,constchar*format,...);

代码示例2:使用stdio.h的常用函数

#include<stdio.h>#include<stdlib.h>#include<string.h>typedefstruct{char name[50];int age;float score;} Student;intmain(){ Student students[3]={{"张三",20,90.5},{"李四",21,85.5},{"王五",22,95.5}};// 写文件 FILE *fp =fopen("students.txt","w");if(fp ==NULL){printf("文件打开失败!\n");return0;}for(int i =0; i <3; i++){fprintf(fp,"姓名:%s,年龄:%d,成绩:%f\n", students[i].name, students[i].age, students[i].score);}fclose(fp);// 读文件char buffer[1024]; fp =fopen("students.txt","r");if(fp ==NULL){printf("文件打开失败!\n");return0;}while(fgets(buffer,sizeof(buffer), fp)!=NULL){printf("%s", buffer);}fclose(fp);// 二进制文件读写 fp =fopen("students.dat","wb");if(fp ==NULL){printf("文件打开失败!\n");return0;}fwrite(students,sizeof(Student),3, fp);fclose(fp); Student read_students[3]; fp =fopen("students.dat","rb");if(fp ==NULL){printf("文件打开失败!\n");return0;}fread(read_students,sizeof(Student),3, fp);fclose(fp);printf("二进制文件读取结果:\n");for(int i =0; i <3; i++){printf("姓名:%s,年龄:%d,成绩:%f\n", read_students[i].name, read_students[i].age, read_students[i].score);}return0;}

四、模块3:stdlib.h深度解析——动态内存管理与常用工具函数

4.1 学习目标

  • 理解动态内存管理的本质:在程序运行过程中动态分配和释放内存
  • 掌握stdlib.h的常用函数:malloc、calloc、realloc、free、atoi、atof、rand、srand
  • 掌握动态内存管理的避坑指南:避免内存泄漏、避免空指针、避免访问已释放的内存
  • 避开stdlib.h使用的3大常见坑

4.2 stdlib.h的常用函数

动态内存管理

#include<stdlib.h>void*malloc(size_t size);void*calloc(size_t nmemb,size_t size);void*realloc(void*ptr,size_t size);voidfree(void*ptr);

类型转换

#include<stdlib.h>intatoi(constchar*nptr);doubleatof(constchar*nptr);

随机数

#include<stdlib.h>intrand(void);voidsrand(unsignedint seed);

代码示例3:使用stdlib.h的常用函数

#include<stdio.h>#include<stdlib.h>#include<time.h>intmain(){int*ptr;int size =5;// 动态内存分配 ptr =(int*)malloc(size *sizeof(int));if(ptr ==NULL){printf("内存分配失败!\n");return0;}// 初始化数组srand(time(NULL));for(int i =0; i < size; i++){ ptr[i]=rand()%100;}// 打印数组printf("数组:");for(int i =0; i < size; i++){printf("%d ", ptr[i]);}printf("\n");// 重新分配内存 size =10; ptr =(int*)realloc(ptr, size *sizeof(int));if(ptr ==NULL){printf("内存分配失败!\n");return0;}// 初始化新分配的内存for(int i =5; i < size; i++){ ptr[i]=rand()%100;}// 打印数组printf("数组:");for(int i =0; i < size; i++){printf("%d ", ptr[i]);}printf("\n");// 释放内存free(ptr); ptr =NULL;// 类型转换char str1[]="123";char str2[]="3.14";int num =atoi(str1);double fnum =atof(str2);printf("num:%d,fnum:%f\n", num, fnum);return0;}

五、模块4:CMake与Makefile构建——简化项目的构建过程

5.1 学习目标

  • 理解构建工具的本质:用于管理项目的编译、链接和安装过程
  • 掌握Makefile的编写方法:规则、变量、函数
  • 掌握CMake的使用方法:CMakeLists.txt的编写、构建过程
  • 掌握构建工具的避坑指南:避免语法错误、避免依赖关系错误、避免链接错误
  • 避开构建工具使用的3大常见坑

5.2 Makefile的编写方法

代码示例4:Makefile示例

# 编译器 CC := gcc # 编译选项 CFLAGS := -Wall -Wextra -g # 链接选项 LDFLAGS := -lm # 源文件 SRCS := main.c utils.c # 目标文件 OBJS := $(SRCS:.c=.o) # 可执行文件 TARGET := app .PHONY: all clean all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(OBJS) $(TARGET) 

代码示例5:CMakeLists.txt示例

cmake_minimum_required(VERSION 3.10) project(MyProject) # 设置C标准 set(CMAKE_C_STANDARD 11) # 源文件 set(SRCS main.c utils.c) # 目标可执行文件 add_executable(app ${SRCS}) # 链接库 target_link_libraries(app m) # 安装目标 install(TARGETS app RUNTIME DESTINATION bin) 

六、模块5:工具链的使用技巧——提高开发效率

6.1 学习目标

  • 理解工具链的本质:用于编译、链接和调试C语言程序的工具集合
  • 掌握编译器的使用技巧:gcc的常用选项、优化选项
  • 掌握调试器的使用技巧:gdb的常用命令、断点设置
  • 掌握代码检查工具的使用技巧:valgrind、cppcheck
  • 避开工具链使用的3大常见坑

6.2 工具链的使用技巧

6.2.1 编译器的使用技巧

gcc的常用选项

  • -Wall:显示所有警告
  • -Wextra:显示额外的警告
  • -g:生成调试信息
  • -O2:优化等级2

代码示例6:使用gcc编译程序

gcc -Wall -Wextra -g main.c utils.c -o app -lm 
6.2.2 调试器的使用技巧

gdb的常用命令

  • run:运行程序
  • break:设置断点
  • continue:继续运行
  • next:执行下一行
  • step:进入函数
  • print:打印变量的值

代码示例7:使用gdb调试程序

gdb app 
6.2.3 代码检查工具的使用技巧

valgrind的使用

valgrind --leak-check=yes ./app 

cppcheck的使用

cppcheck main.c utils.c 

七、模块6:实战案例分析——使用C标准库与工具链构建项目

7.1 学习目标

  • 掌握使用C标准库与工具链构建项目:使用string.h、stdio.h、stdlib.h编写程序,使用CMake与Makefile构建项目
  • 学会使用C标准库与工具链解决实际问题
  • 避开实战案例使用的3大常见坑

7.2 使用C标准库与工具链构建项目

项目结构

myproject/ ├── CMakeLists.txt ├── Makefile ├── include/ │ └── utils.h └── src/ ├── main.c └── utils.c 

utils.h

#ifndefUTILS_H#defineUTILS_Hintsum(int*arr,int size);intmax(int*arr,int size);intmin(int*arr,int size);#endif

utils.c

#include"utils.h"intsum(int*arr,int size){int total =0;for(int i =0; i < size; i++){ total += arr[i];}return total;}intmax(int*arr,int size){int maximum = arr[0];for(int i =1; i < size; i++){if(arr[i]> maximum){ maximum = arr[i];}}return maximum;}intmin(int*arr,int size){int minimum = arr[0];for(int i =1; i < size; i++){if(arr[i]< minimum){ minimum = arr[i];}}return minimum;}

main.c

#include<stdio.h>#include<stdlib.h>#include<time.h>#include"utils.h"#defineSIZE10intmain(){int*arr =(int*)malloc(SIZE *sizeof(int));if(arr ==NULL){printf("内存分配失败!\n");return0;}srand(time(NULL));for(int i =0; i < SIZE; i++){ arr[i]=rand()%100;}printf("数组:");for(int i =0; i < SIZE; i++){printf("%d ", arr[i]);}printf("\n");printf("和:%d\n",sum(arr, SIZE));printf("最大值:%d\n",max(arr, SIZE));printf("最小值:%d\n",min(arr, SIZE));free(arr); arr =NULL;return0;}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10) project(MyProject) set(CMAKE_C_STANDARD 11) include_directories(include) set(SRCS src/main.c src/utils.c) add_executable(app ${SRCS}) install(TARGETS app RUNTIME DESTINATION bin) 

构建过程

mkdir build cd build cmake ..make ./app 

八、本章总结与课后练习

8.1 总结

string.h深度解析:字符串处理的常用函数,包括复制、拼接、比较、查找
stdio.h深度解析:标准输入输出的常用函数,包括控制台输入输出和文件输入输出
stdlib.h深度解析:动态内存管理与常用工具函数,包括动态内存分配、类型转换、随机数
CMake与Makefile构建:简化项目的构建过程,提高开发效率
工具链的使用技巧:提高开发效率,包括编译器、调试器、代码检查工具
实战案例分析:使用C标准库与工具链构建项目,实现数组的求和、求最大值、求最小值

8.2 课后练习

  1. 编写程序:使用string.h的函数实现字符串反转
  2. 编写程序:使用stdio.h的函数实现文件的读写
  3. 编写程序:使用stdlib.h的函数实现动态数组的扩容
  4. 编写Makefile:构建一个包含多个源文件的项目
  5. 编写CMakeLists.txt:构建一个包含多个源文件的项目

Read more

初阶数据结构之栈的实现

初阶数据结构之栈的实现

前言:实现栈之前,先来了解一下什么是栈。 1. 栈的概念 栈是一种特殊的线性表,只允许在固定一端插入和删除操作,进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守先进后出,后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈(压栈,入栈),入数据在栈顶。 出栈:栈的删除操作叫做出栈,出数据也在栈顶。 2. 栈的底层结构如何选择 现在我们已经了解了栈的结构特性了。那么我们该如何实现栈呢?先来看一个问题,顺序表和链表我们已经学习过了,那么栈的底层结构应该选择哪一个呢? 如果底层结构采用数组实现,在插入元素时只需要在指定的位置插入元素即可,删除元素时,- -top就可以了。唯一的缺点就是会存在空间浪费。 如果采用链表来实现栈,每一次插入数据元素都要开辟空间并且需要遍历链表,使新节点成为链表的尾节点;删除数据元素时也需要遍历链表,将尾节点的空间还给操作系统,还要保证尾节点的前驱节点的next保存NULL,避免成为野指针。优点是按需申请和释放空间,不存在空间浪费。缺点是时间复杂度为O(N),空间复杂度也为O(N)

By Ne0inhk
【优选算法】滑动窗口算法:专题一

【优选算法】滑动窗口算法:专题一

目录 引言:  【209. 长度最小的子数组】 题目描述: 实现核心及思路: 思路可视化: 代码实现: 【无重复字符的最长子串】 题目描述: 实现核心及思路: 思路可视化: 代码实现: 【最大连续1的个数III】 题目描述: 实现核心及思路: 代码实现: 【1658.将x减到0的最小操作数】 题目描述: 实现核心即思路: 代码实现: 引言: 滑动窗口?用两个指针维护一个动态的 “窗口” 区间,通过移动指针来扩大或缩小窗口,在一次遍历中完成计算,时间复杂度通常为 O (n)。 典型应用:寻找最长无重复字符的子串找到和为目标值的最短子数组字符串的排列匹配 一般步骤(模板): (1)定义left 和 right 指针同时指向数组首元素; (2)当符合要求时,right++,模拟进窗口; (3)不满足要求时,left++,模拟出窗口; (4)

By Ne0inhk
《算法闯关指南:优选算法--前缀和》--29.和为k的子数组,30.和可被k整除的子数组

《算法闯关指南:优选算法--前缀和》--29.和为k的子数组,30.和可被k整除的子数组

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 29. 和为k的子数组 * 解法(前缀和+哈希表): * 算法思路: * C++算法代码: * 算法总结&&笔记展示: * 30. 和可被k整除的子数组 * 解法(前缀和+哈希表): * 前置知识补充: * 算法思路: * C++算法代码: * 算法总结&&笔记展示: * 结尾: 前言: 聚焦算法题实战,系统讲解三大核心板块:优选算法:剖析动态规划、二分法等高效策略,学会寻找“最优解”。 递归与回溯:掌握问题分解与状态回退,攻克组合、

By Ne0inhk
【Linux系统】解明进程优先级与切换调度O(1)算法

【Linux系统】解明进程优先级与切换调度O(1)算法

各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。 如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步! 也欢迎关注我的blog主页:落羽的落羽 文章目录 * 一、进程优先级的概念 * 二、查看优先级信息 * 1. PRI 与 NI 的理解 * 2. 修改nice值 * 三、进程调度切换 * 1. list_head 与 prio_array 结构 * 2. 活跃140队列与过期140队列 * 四、补充概念:竞争、独立、并行、并发 一、进程优先级的概念 CPU的资源是有限的,所以CPU的运行队列中的所有进程是不可能同时得到资源的。这就是为什么运行队列是一个“队列”,而CPU分配资源的先后顺序,就是指进程的优先级。 二、查看优先级信息 使用ps -l命令,可以查看系统中更详细的进程信息:

By Ne0inhk