news 2026/4/24 7:42:20

C语言高级编程技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言高级编程技巧

C语言高级编程技巧

简介

掌握了C语言的基础知识后,如何写出高效、安全、可维护的代码?本文从代码优化、函数指针、内存管理、回调函数、Windows编程等多个高级主题出发,结合实战经验,分享C语言进阶编程的核心技巧。

一、代码优化技巧

1.1 CPU与位运算

计算机对位操作的执行速度最快,因为CPU本身就是专门执行位运算的器件。在性能敏感场景中,应尽量将乘法和除法转换为位运算和加法。

// 性能对比:// 除法指令:约 50 个机器周期// 位运算/加法:约 1-2 个机器周期x<<n;// 等价于 x * 2^nx>>n;// 等价于 x / 2^n(正数)12*5;// 等价于 (12 << 2) + 12// 清零操作a^a;// 结果为 0

1.2 数学公式替代循环

// 优化前:循环求和intsum=0;for(inti=1;i<=n;i++)sum+=i;// 优化后:高斯定理intsum=n*(n+1)/2;

1.3 表达式优化

x=x+1;// 访问两次内存x+=1;// 访问一次内存,更高效

1.4 if-else 分支顺序优化

在多重 if-else 判断中,将概率最高的条件放在最前面,可以减少平均判断次数。

// 统计英文文档中字母数、空格数、数字数// 优化:字母出现概率最高,先判断字母if(isalpha(ch)){letterCount++;}elseif(isdigit(ch)){digitCount++;}elseif(isspace(ch)){spaceCount++;}

1.5 循环内不变量外提

// 优化前:每次循环都计算 strlenfor(i=0;i<strlen(buf);i++){/* ... */}// 优化后:constintlen=strlen(buf);for(i=0;i<len;i++){/* ... */}

1.6 switch 的空间换时间

switch语句会维护一张跳转表,是典型的空间换时间策略。当分支较多且为离散值时,switchif-else链效率更高。

注意:switch不能判断范围,此时只能使用if-else

二、函数指针

2.1 函数指针基础

函数名的本质是一个标号,其值是存储该函数代码的内存空间首地址——函数名是一个函数指针常量,类似于数组名。

intadd(inta,intb){returna+b;}intmain(void){int(*fPtr)(int,int);// 定义函数指针fPtr=add;// 赋值,函数名就是地址printf("%d\n",fPtr(2,5));// 调用方式1printf("%d\n",(*fPtr)(2,5));// 调用方式2return0;}

2.2 函数指针的用途

  • 回调函数:将函数指针作为参数传递
  • 策略模式:运行时切换不同的处理逻辑
  • 函数表:用数组存储函数指针,通过索引调用
// 函数表实现命令分发typedefvoid(*CommandFunc)(void);CommandFunc commands[]={cmd_open,cmd_close,cmd_save,cmd_exit};voidexecute(intcmd_id){if(cmd_id>=0&&cmd_id<4){commands[cmd_id]();}}

三、内存拷贝与重叠问题

3.1 自实现 memcpy

实现memcpy时必须考虑源地址和目标地址重叠的情况:

void*my_memcpy(void*dest,constvoid*src,size_tcount){char*pdest=(char*)dest;constchar*psrc=(constchar*)src;if(pdest>psrc&&pdest<psrc+count){// 地址重叠,从后向前拷贝for(size_ti=count-1;i!=(size_t)-1;--i)pdest[i]=psrc[i];}else{// 正常情况,从前向后拷贝for(size_ti=0;i<count;++i)pdest[i]=psrc[i];}returndest;}

3.2 memmove vs memcpy

// memmove:能正确处理重叠区域// memcpy:不保证重叠区域的正确性(参数加 restrict 限定)// 原则:不确定是否重叠时,用 memmove

3.3 memmove_s(安全版本)

// memmove_s 增加了目标缓冲区大小参数,防止越界errno_tmemmove_s(void*dest,size_tdestCount,constvoid*src,size_tcount);

四、可变参数函数

4.1 可变参数函数的编写

使用<stdarg.h>中提供的宏来实现:

#include<stdarg.h>#include<stdio.h>intmon_log(char*format,...){charstr_tmp[128];va_list vArgList;va_start(vArgList,format);inti=vsnprintf(str_tmp,sizeof(str_tmp),format,vArgList);va_end(vArgList);printf("%s\n",str_tmp);returni;}// 调用intmain(void){inti=mon_log("%s,%d,%d,%d","test",2,3,4);printf("写入字符数:%d\n",i);return0;}

4.2 四个关键宏

功能
va_list定义可变参数列表的指针
va_start初始化可变参数列表
va_arg逐个获取可变参数
va_end清理可变参数列表

4.3 格式化函数族

sprintf(char*buf,constchar*fmt,...);// 输出到字符串fprintf(FILE*fp,constchar*fmt,...);// 输出到文件vsnprintf(char*buf,size_tn,constchar*fmt,va_list ap);// 可变参数版本

五、NULL指针与野指针

5.1 NULL 指针

// C 中的定义#defineNULL((void*)0)// C++ 中的定义#defineNULL0// 操作 NULL 指针会导致段错误// 但 free(NULL) 是安全的,不会出错int*p=NULL;free(p);// 安全

5.2 野指针的产生

野指针的产生有两种主要途径:

1. 指针未初始化

int*p;// 未初始化,指向随机地址*p=10;// 危险!未定义行为// 正确做法int*p=NULL;// 初始化为 NULL

2. free/delete 后未置 NULL

int*p=(int*)malloc(sizeof(int)*10);free(p);// p 此时成为野指针,指向的内存已被释放// 但 p 的值并不为 NULL// 正确做法free(p);p=NULL;

5.3 malloc 与 calloc 的区别

// calloc = malloc + memset(将内存初始化为零)int*p=(int*)calloc(10,sizeof(int));// 分配并清零int*q=(int*)malloc(10*sizeof(int));// 仅分配,内容不确定// 注意:calloc 不保证内存中字符串长度为 0// char *p = calloc(1, 0); strlen(p) 的结果不确定

六、宏定义与 do-while(0) 模式

6.1 宏的注意事项

  • 宏只是简单的文本替换,不做类型检查
  • 宏参数要加括号,防止优先级问题
  • 多行宏使用do { ... } while(0)包裹

6.2 do-while(0) 的妙用

在C语言函数中,中途return容易忘记释放资源。而嵌套if-else又显得累赘。do-while(0)提供了优雅的解决方案:

// 问题代码:嵌套过深ret=func1();if(ret==0){ret=func2();if(ret==0){ret=func3();// ...}}// 优化:使用 do-while(0)do{ret=func1();if(ret!=0)break;ret=func2();if(ret!=0)break;ret=func3();if(ret!=0)break;// 成功逻辑}while(0);// 统一的资源清理代码在这里执行

6.3 调试宏

#defineDEBUG1#ifdefDEBUG#defineLOG(fmt,...)printf("[DEBUG] "fmt"\n",##__VA_ARGS__)#else#defineLOG(fmt,...)#endif

七、回调函数

7.1 回调函数的原理

回调函数是通过函数指针调用的函数,将函数作为参数传递给另一个函数,在适当的时候被调用。

7.2 异步场景中的回调

在网络编程中,异步发送消息后需要得到对方的响应。由于发送函数内部无法直接获取结果,可以传入回调函数:

// 定义回调函数类型typedefvoid(*CallbackFunc)(void*context,intresult);// 注册回调voidasync_send(constchar*msg,CallbackFunc callback,void*context){// 发送消息...// 异步接收响应后调用回调callback(context,0);// 0 表示成功}// 使用示例voidon_response(void*ctx,intresult){printf("收到响应,结果:%d\n",result);}async_send("Hello",on_response,my_context);

八、用C语言实现面向对象

8.1 隐藏数据接口

利用前向声明实现封装:

// conceal_data_type.h(头文件)typedefstructconceal_data_typeconceal_data_type_t;externintconceal_data_type_get_a(concel_data_type_t*obj);externvoidconceal_data_type_set_a(concel_data_type_t*obj,intval);
// conceal_data_type.c(源文件)structconceal_data_type{inta;// 其他私有成员...};intconceal_data_type_get_a(concel_data_type_t*obj){returnobj->a;}

外部文件只能通过接口函数访问结构体成员,无法直接操作成员变量。

8.2 用C语言实现继承与多态

// 父类structparent{intdat1;intdat2;};// 子类"继承"父类(将父类作为第一个成员)structchild{structparentpar;// 必须是第一个成员intdat3;intdat4;};// 多态:父类指针可以指向子类对象structchild*ch=calloc(1,sizeof(structchild));structparent*par=(structparent*)ch;// par->dat1 等价于 ch->par.dat1

8.3 零长数组(柔性数组)

structmutable_array{intlen;chardata[0];// GNU 扩展:零长数组};// sizeof(struct mutable_array) == 4(32位系统)structmutable_array*p=malloc(sizeof(structmutable_array)+1024);p->len=1024;memcpy(p->data,"Hello",6);

九、C语言中的奇特但有用的语法

9.1 字符串字面值拼接

constchar*str="My""Name""is Zhouwy";// 编译器自动拼接,str = "MyNameis Zhouwy"

9.2 结构体指定成员初始化

structstructTest{inta;intb;intc;};structstructTestvar={.a=10,.b=24,.c=56};

9.3 数组指定下标初始化

intarr[10]={[4]=67,[5]=34};// arr[4] = 67, arr[5] = 34, 其余为 0

十、Windows C编程要点

10.1 临界区(CRITICAL_SECTION)

CRITICAL_SECTION cs;InitializeCriticalSection(&cs);// 初始化EnterCriticalSection(&cs);// 进入临界区// ... 访问临界资源 ...LeaveCriticalSection(&cs);// 离开临界区DeleteCriticalSection(&cs);// 删除临界区

10.2 事件对象(Event)

HANDLE hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);// 创建事件SetEvent(hEvent);// 设置为有信号WaitForSingleObject(hEvent,INFINITE);// 等待信号ResetEvent(hEvent);// 重置为无信号

10.3 互斥对象(Mutex)

HANDLE hMutex=CreateMutex(NULL,FALSE,NULL);// 创建互斥锁WaitForSingleObject(hMutex,INFINITE);// 等待锁ReleaseMutex(hMutex);// 释放锁

10.4 Windows 进程间通信

方式特点
邮槽(Mailslot)半双工,类似匿名管道
命名管道面向连接,可靠传输,支持跨机器通信
匿名管道仅限父子进程间通信

10.5 Windows 异常处理

__try{// 可能出错的代码int*p=NULL;*p=10;}__except(EXCEPTION_EXECUTE_HANDLER){// 异常处理printf("捕获到异常\n");}

__except参数的含义:

  • EXCEPTION_CONTINUE_EXECUTION (-1):忽略异常,继续执行
  • EXCEPTION_CONTINUE_SEARCH (0):不处理,继续寻找上层处理
  • EXCEPTION_EXECUTE_HANDLER (1):识别异常,执行处理代码

10.6 端口复用

intopt=1;// 在 bind 之前调用setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(constvoid*)&opt,sizeof(opt));

端口复用的主要用途:防止服务器重启时之前绑定的端口还未释放,或程序异常退出时系统未释放端口。

注意:多个套接字绑定同一端口时,只有最后一个套接字能正常接收数据。

十一、内存安全编程原则

11.1 核心原则

  1. malloc 分配的内存需要手动释放,函数结束也不会自动释放
  2. free 后的内存内容不变,但操作它会导致未定义行为
  3. 堆内存越界可能在free时才报错,难以定位
  4. 访问非法内存不会立即报错,可能在后续某条指令才崩溃

11.2 内存调试建议

当程序出现莫名其妙的崩溃时:

  1. 检查所有涉及内存操作的代码
  2. 排查指针是否越界、是否使用已释放的内存
  3. 使用调试工具(如 Valgrind)检测内存问题

总结

C语言的高级编程技巧涵盖了性能优化、内存安全、代码架构等多个层面。掌握这些技巧不仅能让你的代码更加高效和安全,还能帮助你更好地理解计算机系统的底层运作机制。

在实际开发中,请牢记以下原则:

  • 性能优化要有理有据,优先优化热点代码
  • 内存管理要严格,malloc 与 free 配对使用,free 后置 NULL
  • 善用回调函数和函数指针实现灵活的代码架构
  • 利用do-while(0)等模式简化错误处理流程
  • 重视跨平台兼容性,编写可移植的代码

原始笔记来源:frasight/王者归来笔记.c, jdah/StudyC.c

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 7:42:17

硕士生/博士生必看的高录用率EI会议——2026国际学术会议清单:EI稳定检索+ 人工智能/ 通信遥感 / 计算机工程 / 大数据 / 控制 / 电子电力 / 交通运输等多主题会议速查(5-6月最新)

对于广大硕博研究生而言&#xff0c;EI、Scopus 国际会议论文是毕业达标、评奖评优、申博深造、职称打底的核心成果渠道。 2026 年 5-6 月 EI 、Scopus检索会议汇总&#xff5c;硕博毕业评职首选&#xff01; 会议名称 会议时间 地点 第六届中国膜计算论坛暨2026年人工智能、…

作者头像 李华
网站建设 2026/4/24 7:41:18

WaveTools鸣潮工具箱:3分钟解锁120FPS游戏体验的终极指南

WaveTools鸣潮工具箱&#xff1a;3分钟解锁120FPS游戏体验的终极指南 【免费下载链接】WaveTools &#x1f9f0;鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools WaveTools鸣潮工具箱是一款专为《鸣潮》玩家设计的开源性能优化工具&#xff0c;能够安…

作者头像 李华
网站建设 2026/4/24 7:39:31

C语言代码复现(1)

目录 1.C语言常见概念 2.C语言数据类型和变量 3.C语言的分支和循环 4.C语言数组 5.C语言函数 6.C语言函数递归 7.C语言操作符 注&#xff1a;当vs出现scanf报错时&#xff0c;因为 Visual Studio 编译器&#xff08;MSVC&#xff09;把 scanf 标记成了【不安全、有风险】…

作者头像 李华
网站建设 2026/4/24 7:36:58

“红帽系统管理二”知识点问答题:第1章 提高命令行运行效率

1. #!/bin/bash 是什么意思?指定脚本的解释器为 bash&#xff0c;告知操作系统使用 /bin/bash 程序执行当前 shell 脚本&#xff0c;是 shell 脚本的固定起始标识。2. PATH 变量有什么重要作用?PATH 是系统环境变量&#xff0c;存储可执行文件的搜索路径列表。执行命令时&…

作者头像 李华
网站建设 2026/4/24 7:35:57

论文排版神器Paperidea,一键搞定格式烦恼

Paperidea 论文自动改格式工具重磅登场&#xff0c;全程免费、高效便捷、格式精准&#xff0c;以创新的“范文复刻”逻辑&#xff0c;帮你一键搞定论文排版&#xff0c;实现 100%“范文化”。毕业季最让人头疼的事&#xff0c;莫过于论文内容过关&#xff0c;却栽在格式上——熬…

作者头像 李华
网站建设 2026/4/24 7:35:33

小批量梯度下降(MBGD)手算教程:以房价预测为例

本教程将通过一个完整的、逐步手算的例子,带你彻底理解小批量梯度下降(Mini-Batch Gradient Descent, MBGD)算法的工作原理。我们将使用一个简化的房价预测模型,并展示模型参数是如何在一轮训练中被更新的。 一、问题定义与场景设定 我们的目标是训练一个线性回归模型来预…

作者头像 李华