news 2026/8/4 6:28:46

从预处理到指针:C语言进阶之路的今日份复盘

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从预处理到指针:C语言进阶之路的今日份复盘

周末花了一整天啃C语言的进阶内容,说实话,脑子有点胀,但理清楚之后还是很有成就感的。今天的主线其实就两条:预处理命令和指针。前者是编译之前的准备工作,后者是C语言最硬核的内存操作能力。趁热打铁,把今天的收获写下来,既是梳理,也算是给自己的一份笔记。
先说说预处理。平时我们写gcc main.c,感觉一键就生成了可执行文件,但其实背后分了四个步骤。预处理只是第一步,执行的是gcc -E main.c -o main.i,做的事情就是把代码里所有带#的指令展开。比如#include <stdio.h>会把stdio.h的整个内容拷贝进来,#define PI 3.1415会把代码里所有的PI替换成3.1415,还有#if 0 ... #endif这种条件编译会把中间的代码屏蔽掉,不参与后续编译。预处理完之后,才是真正的编译(转成汇编)、汇编(转成二进制)和链接(把库函数实现挂上去)。这个流程让我对编译器的理解深了一层。
宏定义这块,以前我总觉得宏就是简单替换,今天才真正意识到它和函数的区别有多大。宏在预处理阶段就完成了展开,本质上就是文本替换,所以它没有函数调用时的现场保护和恢复开销,效率高,但也因为不做运算,很容易出问题。比如你写#define SQUARE(x) x*x,调用SQUARE(3+2),展开后是3+2*3+2,结果是11而不是25。所以老师反复强调,能加括号就加括号,写成#define SQUARE(x) ((x)*(x))。另外宏名最好大写,结尾不能加分号,这些都是容易踩的坑。相比之下,函数是在运行时才调用的,代码体积小,逻辑清晰,但每次调用都有开销。什么时候用宏,什么时候用函数,得看具体场景。
头文件包含也挺有意思。#include <stdio.h>用尖括号,编译器会去系统的路径找,比如Linux下的/usr/include。而#include "head.h"用双引号,编译器会先从当前目录找。这个区别之前一直没在意,今天才知道是为了区分系统头文件和自定义头文件。

条件编译这块,最实用的就是防止头文件重复包含。写#ifndef HEAD_H、#define HEAD_H、#endif这三行包裹住头文件内容,就能确保无论被#include多少次,实际只展开一次。另外#if 0 ... #endif用来注释大段代码比/* */更安全,因为可以嵌套,而/* */遇到嵌套就乱了。

当然,今天花时间最多的还是指针部分。说实话,指针确实是C语言最让人头疼但又最让人着迷的地方。
指针的本质就是地址。计算机给每个内存字节编了号,这个编号就是地址,而指针就是地址的别名。指针变量就是用来保存这个编号的变量。在64位系统下,任何指针变量都占8个字节,32位下占4个字节,这个和它指向的数据类型无关。定义的时候要写明基类型,比如int *p,意思是p指向一个int型的数据,解引用*p拿到的就是一个int。

指针的初始化特别重要。不初始化的指针就是野指针,指向哪里完全不可控,程序随时可能崩溃。老师给了两个建议:要么初始化为NULL,要么直接赋一个已有变量的地址,比如int *p = &num。
指针的算术运算一开始容易混淆。p+1不是简单加1个字节,而是偏移sizeof(基类型)个字节。int *p的话,p+1就向后移动4个字节,char *p只移动1个字节。两个指针相减的结果是它们之间间隔的元素个数,而不是字节差。这个设计其实很巧妙,方便对数组元素进行遍历。
指针作为函数参数,是我今天觉得最有用的技能。以前写函数,只能返回一个值,想返回多个结果就不知道怎么弄了。现在知道了,把变量的地址传进去,子函数通过解引用直接修改主函数里的变量,这不就相当于返回了多个结果嘛。经典的交换两个数就是最好的例子,传递&x和&y,子函数里*a和*b直接操作的就是x和y本身。
指针和数组的关系也很紧密。数组名就是数组首元素的地址,a和&a[0]完全等价。所以a[2]本质上就是*(a+2)。但数组名是地址常量,不能被赋值,a = &num是会报错的。当数组作为参数传递给函数时,实际上传递的就是首元素的地址,所以void print_array(int a[], int len)和void print_array(int *pa, int len)这两种写法完全等价。在函数内部,a就是一个指针变量,可以随意移动。

回想这一天,最大的感触是,预处理解决的是代码组织和编译控制的问题,指针解决的是内存访问和数据传递的问题。前者让代码更灵活,后者让程序更高效。当然,指针也是最容易出错的地方,野指针、悬空指针、越界访问,每一个都是大坑。但正是这些特性,让C语言保持着无可替代的地位。

今天学的东西够消化一阵子了,接下来找几个指针相关的练习题做做,把理论变成手感。学习编程嘛,光看笔记是不够的,多敲代码才是硬道理。

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

Ubuntu虚拟机网络故障排查指南

1. 虚拟机Ubuntu网络问题概述在VMware或VirtualBox等虚拟化环境中安装Ubuntu系统时&#xff0c;网络连接异常是最常见的故障之一。作为一名长期使用虚拟机的开发者&#xff0c;我遇到过各种千奇百怪的网络问题——从简单的NAT配置错误到复杂的桥接模式冲突。当Ubuntu虚拟机突然…

作者头像 李华
网站建设 2026/8/4 6:24:49

企业云盘 Kubernetes 私有化部署:StorageClass 与 CSI 驱动选型清单

企业云盘 Kubernetes 私有化部署&#xff1a;StorageClass 与 CSI 驱动选型清单 企业云盘私有化部署到 Kubernetes 集群&#xff0c;已经成为中大型组织的标准选择。相比传统物理机部署&#xff0c;Kubernetes 带来的弹性伸缩、滚动升级和多租户隔离能力&#xff0c;让文件存储…

作者头像 李华
网站建设 2026/8/4 6:23:15

VMware CentOS虚拟机分辨率问题:open-vm-tools安装与配置全攻略

1. 项目概述&#xff1a;为什么虚拟机里的屏幕总是不听话&#xff1f;如果你也曾在VMware Workstation里安装CentOS&#xff0c;然后对着那个要么小得可怜、要么无法固定、一拖动窗口就变形的屏幕分辨率发愁&#xff0c;那你来对地方了。这几乎是每个刚接触Linux虚拟化环境的朋…

作者头像 李华
网站建设 2026/8/4 6:22:18

Vibe Coding重构:提升AI协作开发效率的关键策略

1. 项目概述&#xff1a;重构Vibe Coding的核心价值Vibe Coding作为一种新兴的编码范式&#xff0c;正在改变开发者与AI协作的方式。不同于传统编程中严格的语法约束&#xff0c;Vibe Coding更强调通过自然语言表达设计意图&#xff0c;让AI助手&#xff08;如Cursor/Claude Co…

作者头像 李华
网站建设 2026/8/4 6:20:44

SQL注入实战:布尔、时间与报错盲注技术深度解析

1. 项目概述与核心挑战 最近在复现和总结一些经典的CTF&#xff08;Capture The Flag&#xff09;Web题目&#xff0c;发现“[极客大挑战 2019]FinalSQL”这道题非常有意思&#xff0c;它几乎把SQL注入中几种最考验耐心和技巧的“盲注”手法都融合在了一起。题目本身没有复杂的…

作者头像 李华
网站建设 2026/8/4 6:20:34

灰狼优化算法在柔性作业车间调度中的应用与MATLAB实现

1. 柔性作业车间调度问题的现实挑战在制造业数字化转型的浪潮中&#xff0c;车间调度问题一直是制约生产效率提升的关键瓶颈。传统作业车间调度&#xff08;JSP&#xff09;假设每道工序只能在特定机器上加工&#xff0c;而柔性作业车间调度&#xff08;FJSP&#xff09;则打破…

作者头像 李华