☰
char、char*、char**本质都是整数?一文彻底搞懂C指针与内存模型
2026/10/1 9:10:07 网站建设 项目流程

先说结论:char、char*、char** 这三个东西,本质上都是“整数”,只是它们被赋予了不同的解读方式。char 是 1 字节的整数,char* 是存放内存地址的整数(指针),char** 是存放“存放地址的地址”的整数(二级指针)。搞不懂它们,C 语言的字符串、函数传参、指针数组就永远隔着一层纱。这篇从底层内存模型讲起,带你一步步把这三个概念彻底打通。

这篇内容适合三类人:刚学 C/C++ 被指针折磨的新生、自学编程卡在字符串处理的初学者、以及用 C 写嵌入式或 GIS 二次开发但总是对指针“知其然而不知其所以然”的开发者。读完你能明白:char 到底存什么、char* 为什么能指向字符串、char** 到底在什么场景下非用不可,以及面试里常考的“指针作为函数参数为什么不能改变实参”这类问题。

1. 内容整体设计与思路拆解

1.1 先搞清楚一个事实:char 其实是个整数

很多教材一上来就教“char 是字符类型”,这其实是误人子弟的简化说法。C 语言标准明确规定:char 是占用 1 字节(8 位)的整数类型,它要么是有符号的(取值范围 -128 到 127),要么是无符号的(取值范围 0 到 255),具体取决于编译器实现。所谓的“字符”,本质上就是用一个整数编号去对应某个符号,这个编号规则就是 ASCII 码表。

举个例子,大写字母 A 对应的 ASCII 十进制值是 65,二进制是 01000001。当你在代码里写char c = 'A';时,编译器做的事情就是把整数 65 放进变量 c 的内存单元里。当你用printf("%c", c)输出时,printf 按照“把这个整数当作 ASCII 码对应的符号”来显示;当你用printf("%d", c)输出时,它直接显示整数 65。同一个值,两种解读方式,这就是 C 语言的灵活性所在。

理解这一点特别重要,因为 char 的“字符”属性是后天赋予的,先天上它就是 1 字节整数。所以 char 完全可以当小型整数用,比如存储 0 到 255 的计数器、标志位、RGB 颜色分量等。在 GIS 开发中,栅格数据的每个像元值如果是 8 位无符号整型,底层用的就是 unsigned char——它只是恰好被用来表示地理空间数据,跟“字符”没半点关系。

1.2 指针的本质:内存地址就是“房间号”

理解了 char 是整数后,再来看 char*。指针也是一个整数,只不过这个整数的“含义”是内存地址。你可以把内存想象成一栋巨大的公寓楼,每个房间(字节)都有一个编号,这个编号就是地址。指针变量里存的就是这个房间号。

char* 表示“这个地址指向的房间里,存放的是一个 char 类型的数据”。关键在于:指针本身有固定的字节数(32 位系统上是 4 字节,64 位系统上是 8 字节),它和“指向的 char”是两码事。打个比方:char* 是一张写着门牌号的纸条,纸条本身是 8 字节(64 位系统),而门牌号指向的房间里只有 1 字节的数据。

char** 就更有意思了。它表示“这个地址指向的房间里,存放的是一个 char* 类型的指针”。换句话说,char** 是一张写着“另一张纸条所在位置”的纸条。第一次接触确实容易晕,但只要抓住“指针就是地址,地址就是整数”这个核心,一切都能拆解。

1.3 为什么要分这么多层?——直接从代码场景反推

也许你会问:直接用 char 不就行了吗,为什么要搞出 char* 和 char** 这么绕的东西?

答案很简单:因为实际编程中,数据往往不是孤立存在的。

  • 如果你要处理一个字符,用 char;
  • 如果你要处理一串字符(字符串),C 语言没有原生字符串类型,只能用一个连续的内存区域存放多个 char,然后用首地址来标识这个区域——这就是 char* 存在的意义;
  • 如果有一串字符串呢?比如命令行参数char *argv[]、一个函数指针数组、或者要在函数里修改调用者的指针变量——就需要存放“字符串首地址”的数组,这个数组名本身又是个地址,于是出现了 char**。

所以三者的关系是层层递进的:char 是数据本身,char* 是数据的地址,char** 是“存放地址的地址”。理解了“需求驱动设计”,就不会觉得这是人为制造出来的复杂度了。

1.4 与 Java、GIS 中 char 的横向对比

既然热词里反复出现“Java char 是什么数据类型”“GIS 里面 char float int time”,这里也顺手理清。Java 里的 char 是 16 位 Unicode 字符类型,取值范围 0 到 65535,它和 C 的 char 最大的区别在于:Java 的 char 明确就是字符,不是用来当整数用的,且占用 2 字节。而 GIS(地理信息系统)开发中常见的 char 则有两种语境:一种是像 C/C++ 或 Python 的 ctypes 里那样表示 1 字节整数,另一种是在文件格式(如 Shapefile)中定义字段类型时,char 表示定长字符串(比如char[10]表示最多 10 个字符的文本)。float 是单精度浮点数,int 是 4 字节整数,time 在 GIS 里通常指日期时间字段,它们和 char 是“数据类型家族”里的不同成员,各自解决不同的问题。

这种横向对比有助于建立完整的知识框架:同样是 “char” 这个单词,在不同语言不同场景下含义完全不同,千万别死记硬背一个定义走天下。

2. 核心细节解析与实操要点

2.1 char 的内存模型与常见操作

先写一段代码,看看 char 在内存中到底长什么样:

#include <stdio.h> int main() { char a = 'A'; char b = 65; char c = -1; unsigned char d = 255; printf("a = %c, 整数值 = %d\n", a, a); printf("b = %c, 整数值 = %d\n", b, b); printf("c = %d\n", c); printf("d = %u\n", d); return 0; }

这段代码在绝大多数编译器上输出如下:

a = A, 整数值 = 65 b = A, 整数值 = 65 c = -1 d = 255

看到没:a = 'A'和b = 65在内存里就是一模一样的二进制01000001。c 在有符号 char 下是 -1,在无符号 char 下则是 255,同一个字节11111111,两种解释。这正是 char 作为“整数”的核心特点:字节不变,解释权归你。

实操中要注意:如果你用 char 存中文,那注定要出问题,因为一个汉字在 UTF-8 编码下占 3 个字节,GBK 编码占 2 个字节,char 根本存不下。存中文字符串应该用char[]数组,而不是单个 char。

另外一个常见坑:有些编译器默认 char 是无符号的(比如某些 ARM 嵌入式编译器),有些默认是有符号的(比如 x86 上的 GCC)。跨平台写代码时,如果你依赖 char 的符号性做判断,就埋了雷。例如:

char x = 0x80; if (x < 0) { // 在有符号 char 的平台上会进来 // 在无符号 char 的平台上不会进来 }

这种代码就是典型的“看编译器心情”。要跨平台稳定,直接用signed char或unsigned char明确声明。

2.2 char* 的三种存在形态:变量、数组、字符串常量

char* 之所以让新手头疼,是因为它有三种完全不同的使用场景,表面看起来却长得一样。

第一种:指向单个 char 变量的指针。

char ch = 'Z'; char *p = &ch; printf("%c\n", *p); // 输出 Z

这里 p 存放的是 ch 的地址,*p是解引用,取出 ch 的值。这种用法在实际项目中最少,主要用来理解指针的基本操作。

第二种:指向 char 数组首元素,用来遍历字符串。

char str[] = "hello"; char *p = str; // 数组名 str 自动转换为指向首元素的指针 while (*p != '\0') { printf("%c", *p); p++; }

这是最核心的用途。注意:char str[] = "hello"会在栈上分配 6 个字节(5 个字符加一个结尾的\0),而char *p = str只是把首地址给了 p。此时 p 和 str 用法几乎一样,都能用下标访问:str[1] == p[1]。

这里有一个重要的知识点:数组名不是指针变量,它是一个“常量地址”,不能做str++这样的自增操作,但指针变量可以。很多人以为数组名就是指针,其实数组名在表达式中会“退化”为指针,但是sizeof(str)返回整个数组大小(比如 6),而sizeof(p)返回指针大小(64 位系统上 8),这就暴露了两者的本质区别。

第三种:指向字符串常量。

char *p = "hello"; p[0] = 'H'; // 危险!多数平台会崩溃

这里的"hello"是字符串字面量,存储在只读数据段。p 指向的是一块只读内存,试图修改它属于未定义行为。很多初学者在这里踩坑:用char *接收字符串常量然后想原地改内容,结果段错误。正确的做法是:需要修改字符串内容就用char str[],不需要修改就用const char *。

2.3 char**:什么时候非用不可?

二级指针最常见的使用场景有三个,逐一说明。

场景一:在函数内部修改外部指针变量的值。

#include <stdio.h> #include <stdlib.h> #include <string.h> void allocate_memory_bad(char *p) { p = (char *)malloc(100); strcpy(p, "hello"); } void allocate_memory_good(char **p) { *p = (char *)malloc(100); strcpy(*p, "world"); } int main() { char *ptr = NULL; allocate_memory_bad(ptr); printf("bad 结果: %s\n", ptr ? ptr : "NULL"); // 输出 NULL allocate_memory_good(&ptr); printf("good 结果: %s\n", ptr ? ptr : "NULL"); // 输出 world free(ptr); return 0; }

原理很简单:C 语言函数参数是值传递,allocate_memory_bad(ptr)传入的是 ptr 的“复印件”,函数内部修改复印件,原件纹丝不动。而allocate_memory_good(&ptr)传入的是 ptr 的地址(也就是 char**),函数内部*p修改的是原件。要想改一个指针的值,必须把指针的地址传过去——这个“多一层间接”思想就是二级指针的核心价值。

场景二:处理指针数组,比如命令行参数。

#include <stdio.h> int main(int argc, char *argv[]) { // 这里的 argv 类型等价于 char **argv for (int i = 0; i < argc; i++) { printf("参数 %d: %s\n", i, argv[i]); } return 0; }

argv是一个数组,每个元素都是char *,指向一个字符串。在函数参数列表中,char *argv[]和char **argv完全等价。这种结构在 main 函数、各种 CLI 工具解析里是标配。

场景三:二维字符数组的动态分配。注意区分两种东西:char array[3][20]是真正的二维数组,内存在栈上连续分配;而char **p可以通过malloc逐行分配,每行的内存不一定连续。

#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char **p = (char **)malloc(3 * sizeof(char *)); for (int i = 0; i < 3; i++) { p[i] = (char *)malloc(20); sprintf(p[i], "第%d行", i); printf("%s\n", p[i]); } for (int i = 0; i < 3; i++) { free(p[i]); } free(p); return 0; }

这种“指针数组”的内存模型相对复杂:p 自己是一个变量,它指向一块连续的内存区域,这块区域里存了 3 个 char*,每个 char* 又各自指向一块存有 20 字节的内存。画个草图就是:

p (char**) ──▶ [p[0]] ──▶ "第0行\0..." [p[1]] ──▶ "第1行\0..." [p[2]] ──▶ "第2行\0..."

理解这个图,char** 就彻底拿下了:第一层指向“指针数组”,第二层指向“具体字符串”。正是因为多了一层,才能在二维结构里自由控制每一个字串的长度,这也是动态二维数组比 C 原生二维数组更适合处理“每行长度不一”的场景的原因。

2.4 char、char*、char** 在 GIS 场景的具体应用

结合热词里的 GIS,展开说几个贴切的例子,帮助理解这些概念在真实业务中的位置。

GIS 栅格数据(遥感影像、DEM 数字高程模型)的像元类型通常包括:1 字节无符号整型(unsigned char,适合存储 0-255 的灰度值或分类编码)、2 字节整型(short)、4 字节整型(int)、4 字节浮点型(float)、8 字节双精度型(double)。如果你用 C 读取 GeoTIFF 或 IMG 格式的栅格,底层缓冲区经常是unsigned char *buffer,然后根据数据类型把这块内存按不同方式解析。比如 DEM 的每个像元是 4 字节 float,你就可以把unsigned char*强转为float*来访问,因为内存本质上就是字节序列,类型只是解读方式。这个思路和 char 是“可重新解读的整数”一脉相承。

矢量数据方面,Shapefile 的属性表字段类型常见的有C(字符型,对应 C 语言的 char 数组)、N(数值型)、F(浮点型)、D(日期型)。读取 DBF 文件时,一个字段可能是一个char[10]数组,要提取里面内容,就需要用char*指向这个数组然后做字符串处理。而在 GIS 二次开发中,很多 SDK 的 API 接口定义都是char**,用于返回一个字符串列表(比如图层字段名列表)。这时候如果不理解二级指针,调接口就只会“抄示例”,换个场景就抓瞎。

3. 实操过程与核心环节实现

3.1 从零搭建一套完整演示代码

纸上谈兵没意思,直接写一套可以完整运行的代码,覆盖 char、char*、char** 的全部核心操作。在任意 C 编译器(GCC、Clang、MSVC)下都能直接跑。

#include <stdio.h> #include <stdlib.h> #include <string.h> // 函数:通过二级指针修改外部指针的指向 void safe_assign(char **dest, const char *src) { // 先把原来的内存释放掉,避免泄漏 if (*dest != NULL) { free(*dest); *dest = NULL; } *dest = (char *)malloc(strlen(src) + 1); if (*dest == NULL) { fprintf(stderr, "内存分配失败\n"); exit(1); } strcpy(*dest, src); } int main() { // ---------- 第一部分:char ---------- printf("========== char 基础 ==========\n"); char a = 'A'; char b = 66; printf("a = %c, 整数表示 = %d\n", a, a); printf("b = %c, 整数表示 = %d\n", b, b); printf("char 大小 = %zu 字节\n", sizeof(char)); printf("char* 大小 = %zu 字节\n", sizeof(char *)); printf("char** 大小 = %zu 字节\n", sizeof(char **)); // ---------- 第二部分:char* ---------- printf("\n========== char* 基础 ==========\n"); char str[] = "hello"; char *p = str; printf("数组方式访问: %c%c%c%c%c\n", str[0], str[1], str[2], str[3], str[4]); printf("指针方式访问: %c%c%c%c%c\n", p[0], p[1], p[2], p[3], p[4]); while (*p != '\0') { printf("%c", *p); p++; } printf("\n"); p = str; // 复位指针 // ---------- 第三部分:char** ---------- printf("\n========== char** 基础 ==========\n"); char *name1 = "Alice"; char *name2 = "Bob"; char *name3 = "Charlie"; // 方式一:用指针数组 + 二级指针遍历 char *name_array[] = {name1, name2, name3}; char **pp = name_array; for (int i = 0; i < 3; i++) { printf("name[%d] = %s, 字符串长度 = %zu\n", i, pp[i], strlen(pp[i])); } // 方式二:在函数里通过二级指针修改指针 char *dynamic_str = NULL; safe_assign(&dynamic_str, "动态分配的第一版"); printf("\n第一次分配: %s\n", dynamic_str); safe_assign(&dynamic_str, "动态分配的第二版"); printf("第二次分配: %s\n", dynamic_str); free(dynamic_str); dynamic_str = NULL; // 方式三:模拟二维字符串数组(每行长度不固定) printf("\n========== 二级指针动态二维数组 ==========\n"); char **grid = (char **)malloc(4 * sizeof(char *)); const char *cities[] = {"北京", "Shanghai", "NYC", "Tokyo"}; for (int i = 0; i < 4; i++) { grid[i] = (char *)malloc(strlen(cities[i]) + 1); strcpy(grid[i], cities[i]); printf("城市 %d: %s\n", i, grid[i]); } for (int i = 0; i < 4; i++) { free(grid[i]); } free(grid); return 0; }

这段代码覆盖了:指针大小的验证(32 位下三者是 4/4/4,64 位下是 1/8/8)、数组与指针的等价访问、通过二级指针在函数内修改外部指针、动态分配二维数组。建议实际敲一遍,在断点处查看变量的内存地址,比看十篇文章都管用。

3.2 图解内存布局:一张图讲透三级跳

理解这三个概念,最关键的是画内存图。自己在纸上画“房间号 + 房间内容”即可,不需要花哨工具。我描述一遍,你跟着画:

画三条横线代表三块内存区域。

区域一是变量 a(char 类型):房间地址假设是 0x100,房间内容是01000001,对应的 ASCII 是 A。

区域二是变量 p(char* 类型):房间地址假设是 0x200,房间里存的内容是0x100,即指向区域一。注意 p 自己有地址 0x200 这件事非常重要。

区域三是变量 pp(char** 类型):房间地址假设是 0x300,房间里存的内容是0x200,即指向 p。

画完后你会发现:无论嵌套多少层指针,内存模型都只是“房间里放地址”这个模式的重复。所谓二级指针,就是“房间里的内容,指向的是另一个放地址的房间”。真正的数据存在最底层的那间房里。

用代码验证地址关系:

char value = 'X'; char *ptr = &value; char **pptr = &ptr; printf("value 的地址: %p\n", &value); printf("ptr 里存的地址: %p\n", (void *)ptr); printf("ptr 自己的地址: %p\n", &ptr); printf("pptr 里存的地址: %p\n", (void *)pptr);

运行结果中,第 1 行和第 2 行是同一个地址,第 3 行和第 4 行是同一个地址。这就直接证明了:ptr里面存的是value的地址,pptr里面存的是ptr的地址。

3.3 sizeof、strlen、数组名与指针的细节对照

写码时最容易混淆的就是 sizeof 和 strlen 的区别、数组名和指针的区别。整理一个对照表,方便查阅:

表达式含义64 位系统结果
sizeof(char)单个 char 占的字节数1
sizeof(char*)指针变量占的字节数8
sizeof(char**)二级指针变量占的字节数8
char s[] = "hello"; sizeof(s)数组总字节数(含结尾 \0)6
char s[] = "hello"; strlen(s)字符串有效字符数(不含 \0)5
char *p = "hello"; sizeof(p)指针类型大小,与字符串长度无关8
char *p = "hello"; strlen(p)从 p 指向的位置开始数到 \0 的字符数5

关键点:sizeof 是“编译期算子”,它看的是类型定义,不关心运行时内容;strlen 是“运行期函数”,它必须逐个字节扫描到\0。所以char*的sizeof永远是 8(64 位),而 strlen 才给出字符串长度。这个区分在写代码时非常关键,因为很多人错误地用 sizeof 去算字符串长度,导致缓冲区分配过大或过小。

另外注意char s[] = "hello"和char *s = "hello"虽然打印出来都是 hello,但前者是数组,修改 s[0] 合法;后者是指向常量区的指针,修改 s[0] 属于未定义行为。这也是面试官百问不厌的考点。

3.4 简化记忆法:把指针层级和“快递代收点”类比

如果上面的技术讲解还觉得绕,用生活场景再捋一遍。

char 是“一封写了字的信”。信纸上有 1 个字符的内容。

char* 是“写着信的存放地址的纸条”。纸条本身是一个指针,按照纸条上的地址找到信箱,打开信箱才能看到信。也可以理解成快递单号:你不需要关心包裹在哪个仓库,只需要凭单号就能找到它。

char** 是“写着快递单号存放位置的纸条”。你先按照地址找到一个抽屉,抽屉里有一张写着快递单号的纸条,再凭这个单号才能找到真正的包裹。

所以操作层级就是:char 直接操作内容,char* 先解引用一次再操作内容,char** 先解引用两次再操作内容。解引用次数等于 * 的个数。

这个类比在调试时特别好用。看到代码报Segmentation Fault,先想想是不是解引用次数多了或少了:如果char**只用了一次*就去访问数据,拿到的其实是个地址,硬当作内容打印自然会崩。

4. 常见问题与排查技巧实录

4.1 经典面试题:为什么函数里修改指针实参不生效

这道题考了无数人。代码长这样:

void change(char *s) { s = "new"; } int main() { char *str = "old"; change(str); printf("%s\n", str); // 输出 old return 0; }

原因还是那句话:C 语言值传递。change 函数收到的参数是 str 的拷贝,这个拷贝和 str 各占各的内存。函数里修改拷贝,对原变量 str 没有影响。

要修改实参本身,必须传入实参的地址:change(&str),此时形参类型是char**,函数里通过*s = "new"来修改原指针的指向。这就是为什么“想通过函数修改一个指针变量本身的值,必须用二级指针”。

把这个问题再延伸一层:如果想在函数里修改一个 int 变量呢?用int*;想修改一个char*变量呢?用char**;想修改一个char**变量呢?用char***。规律非常明显——想改什么就传什么的地址。

4.2 常见崩溃原因:野指针、越界、重复释放

用 char 系列指针最容易踩的坑,集中在下面几个方面:

第一,未初始化的指针(野指针)。char *p;然后直接strcpy(p, "hello"),p 指向哪里没谁知道,大概率崩溃。正确做法是初始化指针时要么赋值一个有效地址,要么置为 NULL。

第二,缓冲区越界。分配了 5 个字节,往里写了 10 个字符。char buf[5]; strcpy(buf, "hello world");看似编译没报错,运行可能也没报错,但已经破坏了栈上相邻的数据,只为时报一个莫名其妙的错。解决方法是优先用snprintf、strncpy等带长度限制的函数。

第三,重复 free。多次调用free(p)同一指针,或者 free 一个还在使用的指针,都会导致难以定位的问题。建议 free 后立即把指针置 NULL,养成习惯。

第四,内存泄漏。malloc 了忘记 free,尤其在函数里 malloc 一串指针数组后只 free 了第一层。二级指针的动态分配一定要“逆着分配顺序释放”:先释放每个子串,再释放指针数组本身。

写一个小总结表,方便排查:

症状可能原因排查方向
段错误(Segmentation Fault)野指针、越界、空指针解引用查指针是否初始化、是否越界
输出乱码char 按有符号/无符号误读、编码不匹配查类型声明是否匹配、数据源编码
函数内修改后外部没变化值传递修改了拷贝改用指针或二级指针
内存越写越崩缓冲区分配过小改成 strlen+1 分配
程序无缘无故多占内存malloc 没有对应的 free用 valgrind 检测
字符串修改时崩溃指向了字符串常量区改用数组存储

4.3 调试技巧分享:用 printf 和 gdb 快速定位指针问题

碰到指针相关问题,第一件事不是读代码,而是打印地址。我调试指针问题时最喜欢干的是用 printf 把每一层地址打出来:

printf("data 地址: %p\n", (void *)data); printf("ptr 值: %p\n", (void *)ptr); printf("ptr 地址: %p\n", (void *)&ptr); printf("pptr 值: %p\n", (void *)pptr);

三层地址一打印,立刻能看出哪一层是空的、哪一层指向了不该指向的位置。如果是 NULL(0x0),说明这一层还没赋值。如果地址很怪(比如 0x1、0xffffffff),可能是野指针。

gdb 里也常用p *ptr打印指针指向的内容,p &ptr打印指针自己的地址,bt看崩溃时的调用栈。配合watch监视某个地址内容的变化,能快速定位是谁改坏了一块内存。

4.4 关于“char 在 GIS 里和 float、int、time 的关系”的补充说明

有些读者可能是因为 GIS 开发摸到这篇文章的,这里再补充几句。在 GIS 开发中,数据类型的核心逻辑是“这个字段的数据在计算机里怎么存、怎么算”。int 存整数(比如人口数、楼层数),float 存带小数的数值(比如经纬度、面积),char 存字符或短字符串(比如行政区代码、地类代码),time 存时间(比如采集时间、更新时间)。

它们之间的选择原则也很直白:能存整数就别用 float,因为浮点有精度问题;定长短字符串用 char 数组,变长文本就该用字符串类型;时间字段在底层数据库或文件格式里往往存的是整数或字符串,但读取到代码里要用专门的时间结构体去解析。理解了 char 是 1 字节整数之后,再看这些字段类型的本质就通了:所有类型在内存里都是字节序列,类型只是告诉编译器怎么解读和操作这些字节。

5. 写在最后的几点实操心得

先说一个每次带新人都会强调的习惯:对指针做任何操作前,先问自己“这个指针指向的内存在哪里、生命周期到什么时候”。很多人写出悬挂指针(dangling pointer)就是因为只想着“怎么赋值”,没想“内存是谁的、谁负责释放”。如果字符串是从函数里 malloc 出来的,调用者用完必须 free;如果字符串是某个静态区的常量,绝不能在函数里试图修改它。把内存所有权理清楚,指针错误能减少八成。

再说一个小技巧:写代码时给变量名加上类型语义。我自己习惯这样命名:char 类型变量用 c 开头(如 cGrade),char* 用 p 开头(如 pName),char** 用 pp 开头(如 ppArgv)。虽然很多人觉得麻烦,但当你面对一个几百行的函数时,光看变量名就知道它是数据还是地址还是地址的地址,排查问题的速度会快很多。

另外,入门阶段不要害怕写测试代码。刚开始学指针,多写几个故意崩溃的程序反而学得更快。比如故意对 NULL 指针解引用,故意越界写内存,故意重复 free,观察控制台输出的错误信息。这种“反向验证”的学习方法,比背概念有用得多。等你把错误信息都混眼熟了,以后遇到问题基本一眼就能判断是哪类错误。

最后,这篇只是入门级的梳理。实际工程里还会遇到函数指针、指向多维数组的指针、还有各种 const 修饰组合(const char*、char* const、const char**等),复杂程度会几何级增长。但万变不离其宗:你只要记住“指针变量存的是地址,地址指向的东西是什么类型就用对应的指针类型,想改谁就传谁的地址”,不管多复杂的指针声明,都能一层层剥开。先收藏着,遇到问题再回来翻,比一口气死记硬背有效得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询