C语言数组名与指针的区别:从sizeof到函数传参的完整解析 之前在项目里排查一个内存越界问题最后定位到某位同学把数组名当指针用还总觉得两者是一回事。这种误区在 C 语言初学者中实在太常见了甚至不少工作几年的开发者在笔试里遇到sizeof(arr)和sizeof(ptr)的对比题依然会犹豫一下。这篇文章就把“数组名”和“指针”这两个概念彻底拆开讲一遍。先讲它们为什么容易被混淆再从sizeof、、函数传参、下标访问、二维数组等多个维度做对比最后给出一批笔试和面试中经常出现的坑题并附上完整的代码示例和运行结果。如果你是正在学习 C 语言的新手或者准备校招笔试、考研复试这篇文章值得完整看一遍。1. 数组名到底是什么我们先从最基础的问题开始数组名真的只是一个“装地址的变量”吗在 C 语言中数组名并不是一个普通的指针变量。它代表的是整个数组对象本身也就是一段连续的、已经分配好内存空间的数据集合。int arr[5];上面这条语句会向系统申请一块可容纳 5 个int的连续内存空间。而arr这个标识符指的是这块连续内存本身它不是一个单独的变量也没有为它额外分配一块专门存储地址的空间。说得再直白一点数组名是这块内存区域的一个“名字”而不是“存放地址的盒子”。那为什么很多人会把数组名当作指针呢因为 C 语言在设计时规定了一个非常重要的规则在绝大多数表达式中数组名会“退化为”指向其首元素的指针。这里的关键词是“绝大多数”。也就是说这个退化规则是有例外情况的而这些例外恰恰是笔试中最爱挖坑的地方。先看下面这段代码#include stdio.h int main(void) { int arr[5] {1, 2, 3, 4, 5}; int *p arr; printf(sizeof(arr) %zu\n, sizeof(arr)); printf(sizeof(p) %zu\n, sizeof(p)); return 0; }在 64 位 Linux 环境下输出结果为sizeof(arr) 20 sizeof(p) 8在 32 位环境下输出的可能是sizeof(arr) 20 sizeof(p) 4int占 4 字节arr[5]总大小是 20 字节所以sizeof(arr)计算的是整个数组的大小。而p只是一个指向int的指针变量无论它指向哪里指针变量本身只占一个指针大小的空间。这就是“数组名和指针不是一回事”的第一个铁证sizeof对数组名求出的是整个数组的字节数对指针求出的是指针变量自身的字节数。2. 环境准备与运行说明在正式展开代码对比之前先把本文示例的运行环境说明一下方便你复现运行结果。本文所有示例都基于以下环境如果你本机环境不同可能需要把输出结果的细节做相应调整项目说明操作系统Ubuntu 20.04 x86_64 / Windows 10 / 11编译器GCC 9.4 或更高版本Windows 下可用 MinGWC 标准C99 / C11IDEVS Code C/C 插件或 CLion、Visual Studio如果电脑里还没有 C 语言编译环境推荐先安装 GCC# Ubuntu / Debian sudo apt update sudo apt install gcc # CentOS / RHEL sudo yum install gcc # Windows # 推荐安装 MinGW-w64然后把 bin 目录加入 PATH安装完成后可以用下面命令确认版本gcc --version编译运行本文代码的方式也很简单gcc -stdc11 -Wall test.c -o test ./test在 Windows 下则是gcc -stdc11 -Wall test.c -o test.exe test.exe加上-Wall可以输出更多编译警告这在学习数组名和指针的坑时非常有帮助很多隐患在编译阶段其实就能看到。3. 数组名与指针的核心区别这一节是整篇文章的核心我把数组名和指针在 C 语言中的关键行为差异逐条拆开讲解。3.1 本质不同数组名是对象指针是变量用一句话概括数组名代表一块连续内存区域的名字指针是一个用来保存地址的变量。这意味着数组名不是一个变量所以不能对数组名执行自增、自减、赋值操作。指针是变量所以指针可以重新指向其他地方可以执行p、p--等操作。数组名在表达式求值时通常会被隐式转换成指向首元素的指针但这个转换发生在“使用时”并不会改变数组名本身的属性。下面这段代码能很好地说明问题#include stdio.h int main(void) { int arr[3] {10, 20, 30}; int *p arr; printf(p %p\n, (void *)p); p; // 合法指针可以自增 printf(after p: p %p, *p %d\n, (void *)p, *p); // arr; // 编译错误数组名不是可修改的左值 int *q arr; q arr 1; // 合法arr 1 会退化为指针参与运算 printf(*(arr 1) %d\n, *(arr 1)); return 0; }这段代码里如果打开被注释掉的arrGCC 会直接报错error: lvalue required as increment operand这个错误的含义是数组名不是可修改的左值不能对它做自增运算。但p完全没问题因为p是一个真正的指针变量。再看一个隐藏得更深的地方。有人会觉得“既然数组名会退化为指针那我把数组名赋值给指针不就行了”这确实可以但要注意这种赋值只复制了“首元素地址”并没有复制整个数组。int arr[5] {1, 2, 3, 4, 5}; int *p arr; p[2] 100; printf(%d\n, arr[2]); // 输出 100因为p和arr操作的是同一块内存p[2] 100等价于*(p 2) 100也就是直接修改arr[2]所在的内存。这是因为p保存了数组首元素的地址所以两者操作的是同一块内存区域。这并不表示“数组名就是指针”只表示“指针可以保存数组首元素的地址”。3.2 sizeof 操作最直观的区别sizeof是笔试中出现频率最高、区分度最大的一个考察点。先看这个经典的对比示例#include stdio.h int main(void) { char a1[10]; char *p1 a1; printf(sizeof(a1) %zu\n, sizeof(a1)); // 10 printf(sizeof(p1) %zu\n, sizeof(p1)); // 8 或 4 short a2[8]; short *p2 a2; printf(sizeof(a2) %zu\n, sizeof(a2)); // 16 printf(sizeof(p2) %zu\n, sizeof(p2)); // 8 或 4 return 0; }输出结果sizeof(a1) 10 sizeof(p1) 8 sizeof(a2) 16 sizeof(p2) 8解释如下sizeof(a1)在编译期就能确定等于char大小乘以元素个数也就是1 * 10 10。sizeof(a2)等于short大小乘以元素个数即2 * 8 16。sizeof(p1)和sizeof(p2)都是指针变量本身的大小和它们指向的目标类型无关只与系统寻址位数有关。这里有一个关键知识点sizeof是编译期运算符而不是函数。它不会去访问内存而是在编译期间直接计算出来。当操作数是数组名时sizeof计算整个数组的大小当操作数是指针变量时sizeof计算该指针变量自身的存储大小。还有一个容易被忽略的细节在 C 语言中sizeof作用于数组名时数组名不会退化为指针。这正是“数组名在某些上下文中不退化”的典型例外。3.3 数组名与指针类型完全不同再看另一个经典考点arr和p。#include stdio.h int main(void) { int arr[5] {0}; int *p arr; printf(arr %p\n, (void *)arr); printf(arr %p\n, (void *)arr); printf(p %p\n, (void *)p); printf(p %p\n, (void *)p); printf(arr 1 %p\n, (void *)(arr 1)); printf(arr 1 %p\n, (void *)(arr 1)); printf(p 1 %p\n, (void *)(p 1)); return 0; }运行结果示例arr 0x7ffd2b5a6e30 arr 0x7ffd2b5a6e30 p 0x7ffd2b5a6e30 p 0x7ffd2b5a6e28 arr 1 0x7ffd2b5a6e34 arr 1 0x7ffd2b5a6e44 p 1 0x7ffd2b5a6e34我们来一步步分析这个结果arr的值和arr的值在实际地址数值上是相同的都是数组首元素的地址。但它们的类型完全不同。arr的类型是int *指向int的指针。arr的类型是int (*)[5]指向含 5 个元素的数组的指针。arr 1的地址值比arr大sizeof(int)即 4 字节因为它指向下一个int元素。arr 1的地址值比arr大sizeof(int[5])即 20 字节因为它指向整个数组的下一块内存。写成代码理解起来更清晰#include stdio.h int main(void) { int arr[5] {1, 2, 3, 4, 5}; printf(arr %p\n, (void *)arr); printf(arr 1 %p\n, (void *)(arr 1)); // 差 4 字节 int (*pArr)[5] arr; // pArr 指向整个数组 printf(arr %p\n, (void *)arr); printf(arr 1 %p\n, (void *)(arr 1)); // 差 20 字节 return 0; }这个差异非常关键。笔试中如果问“arr 1和arr 1有什么区别”答案是arr 1移动到下一个元素地址。arr 1直接跳过整个数组指向数组末尾的下一个地址。为了验证arr 1是否真的跳过了 20 字节可以打印出(char *)(arr 1) - (char *)arr结果为 20。在指针算术中指针加减的步长取决于指针指向的类型arr指向int步长为sizeof(int)。arr指向int[5]步长为sizeof(int[5])。3.4 函数传参数组名退化为指针函数传参是数组名和指针“最容易打架”的场景。先看下面这段代码#include stdio.h void test(int arr[]) { printf(in function: sizeof(arr) %zu\n, sizeof(arr)); } int main(void) { int arr[10] {0}; printf(in main: sizeof(arr) %zu\n, sizeof(arr)); test(arr); return 0; }输出结果in main: sizeof(arr) 40 in function: sizeof(arr) 8在main里arr是真正的数组sizeof(arr)是 4010 个int每个 4 字节。但在函数test中sizeof(arr)变成了 8指针大小。这是因为函数形参int arr[]会被编译器改写为int *arr。在 C 语言标准中这条规则非常明确当数组名作为函数参数时数组名退化为指向其首元素的指针。所以test函数内部实际上拿到的是一个指针变量它不知道原始数组有多少个元素。这也是为什么在 C 语言中如果你需要函数处理一个数组必须显式传递数组长度#include stdio.h void print_array(int arr[], int len) { for (int i 0; i len; i) { printf(%d , arr[i]); } printf(\n); printf(indide print_array, sizeof(arr) %zu\n, sizeof(arr)); } int main(void) { int arr[6] {1, 3, 5, 7, 9, 11}; int len sizeof(arr) / sizeof(arr[0]); printf(in main, len %d\n, len); print_array(arr, len); return 0; }输出in main, len 6 1 3 5 7 9 11 indide print_array, sizeof(arr) 8在main中sizeof(arr) / sizeof(arr[0])可以正确计算出数组长度因为这里的sizeof(arr)是真正的数组大小。但是一旦数组名传进函数函数内部就无法通过sizeof拿到原始长度必须借助外部传入的len参数。这也是很多初学者在使用void test(int arr[])时遇到的经典陷阱在函数内用sizeof(arr) / sizeof(arr[0])计算长度结果往往得到一个错误值。正确做法是在数组所在作用域内计算好长度再作为参数传入函数。3.5 下标访问的本质C 语言中a[i]本质上就是*(a i)的语法糖。这说明下标运算符[]并不只适用于数组它同样适用于指针。这句话反过来也成立数组名能做的事指针通常也能做但指针能做的事数组名不一定能做。#include stdio.h int main(void) { int arr[5] {10, 20, 30, 40, 50}; int *p arr; printf(arr[2] %d\n, arr[2]); printf(*(arr 2) %d\n, *(arr 2)); printf(p[2] %d\n, p[2]); printf(2[arr] %d\n, 2[arr]); // 等价于 *(2 arr) return 0; }输出arr[2] 30 *(arr 2) 30 p[2] 30 2[arr] 30最后一行2[arr]可能看起来很奇怪但它完全合法。因为2[arr]被转换成语法*(2 arr)也就是*(arr 2)所以结果仍然是 30。这类写法在实际工程中不推荐使用但笔试中偶尔会出现你需要理解其原理。虽然数组名和指针都支持下标访问但有一个重要区别数组名不能自增自减指针可以。int arr[5] {1, 2, 3, 4, 5}; int *p arr; p; // 合法 // arr; // 非法编译错误 int *p2 p 3; // 合法原因前面已经说过数组名不是一个可修改的左值而指针是变量可以修改。指针的移动本质上是改变指针变量里保存的地址值。3.6 字符数组与字符串指针的区别在 C 语言中字符数组和字符串指针的混淆是另一个高发雷区。先看两种定义方式char str1[] hello; char *str2 hello;这两行代码表面上看都能用str1和str2表示字符串hello但底层机制完全不同str1是一个字符数组大小自动确定为 65 个字符加上结尾的\0。str1拥有自己的一块内存内容存放在栈上可以被修改。str2是一个指针变量指向一个字符串字面量。字符串字面量在 C 语言中存储在只读区尝试修改它属于未定义行为通常会导致程序崩溃。下面这段代码就能验证差异#include stdio.h #include string.h int main(void) { char str1[] hello; char *str2 hello; printf(sizeof(str1) %zu\n, sizeof(str1)); // 6 printf(sizeof(str2) %zu\n, sizeof(str2)); // 8 // 修改 str1 是安全的 str1[0] H; printf(str1 %s\n, str1); // 下面这行会导致段错误因为字符串字面量存储在只读区 // str2[0] H; // printf(str2 %s\n, str2); return 0; }输出sizeof(str1) 6 sizeof(str2) 8 str1 Hello如果把注释掉的str2[0] H放开程序可能直接崩溃出现段错误Segmentation fault。这可不是闹着玩的这是笔试和面试中高频出现的知识点字符数组可以修改内容字符串指针所指内容为只读。所以当你在函数中需要修改或拼接一个字符串时优先选择字符数组如果只是想读取字符串使用const char *是更安全的做法。3.7 二维数组与指针的对应关系多维数组的情况更复杂但原理仍然相同数组名退化后指向其首元素首元素的类型决定了指针的类型。int a[3][4]可以看作“含 3 个元素的一维数组每个元素是一个含 4 个int的数组”。因此a退化为指针时它的类型是int (*)[4]即“指向含 4 个int元素的数组的指针”。#include stdio.h int main(void) { int a[3][4] { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; printf(a %p\n, (void *)a); printf(a 1 %p\n, (void *)(a 1)); printf(*a %p\n, (void *)*a); printf(*a 1 %p\n, (void *)(*a 1)); printf(a[1][2] %d\n, a[1][2]); // 7 printf(*(*(a1)2) %d\n, *(*(a 1) 2)); // 7 return 0; }输出示例a 0x7ffd3f2a5a20 a 1 0x7ffd3f2a5a30 *a 0x7ffd3f2a5a20 *a 1 0x7ffd3f2a5a24 a[1][2] 7 *(*(a1)2) 7注意观察a和a 1的地址差是 16 字节即4 * sizeof(int)因为a的类型是int (*)[4]步长为一行。*a的类型是int **a 1地址差是 4 字节指向第一行的第二个元素。对二维数组取元素可以写成*(*(a i) j)它等价于a[i][j]。这里有一个易错点很多人把a当作int **这是完全不正确的。int **是指向指针的指针和int (*)[4]是完全不同的类型两者不能混用。如果函数需要接收二维数组正确写法是#include stdio.h void print_matrix(int a[][4], int rows) { for (int i 0; i rows; i) { for (int j 0; j 4; j) { printf(%d , a[i][j]); } printf(\n); } } int main(void) { int a[3][4] { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; print_matrix(a, 3); return 0; }这里的形参int a[][4]会被编译器调整为int (*a)[4]所以第二维必须写明否则编译器无法知道每一行的长度。4. 实战笔试经典坑题完整解析下面直接从笔试/面试题角度给大家整理一组高频题目附上完整分析和代码输出。这些题目非常典型值得反复研究。4.1 题目一sizeof 对比#include stdio.h int main(void) { int a[] {1, 2, 3, 4, 5}; int *p a; printf(%zu\n, sizeof(a)); printf(%zu\n, sizeof(p)); printf(%zu\n, sizeof(*p)); printf(%zu\n, sizeof(a[0])); return 0; }在 64 位系统上输出是20 8 4 4解析sizeof(a)是整个数组大小5 个int每个 4 字节共 20。sizeof(p)是指针变量大小64 位系统为 8 字节。sizeof(*p)是p指向的目标类型大小即int大小4 字节。sizeof(a[0])和sizeof(*p)相同都是int大小4 字节。这种题目考察的就是“数组名在sizeof中不退化”这条规则。4.2 题目二数组名 1的移动步长#include stdio.h int main(void) { int arr[10] {0}; int *p arr; printf(p %p\n, (void *)p); printf(p 1 %p\n, (void *)(p 1)); printf(arr %p\n, (void *)arr); printf(arr 1 %p\n, (void *)(arr 1)); return 0; }p 1比p大 4 字节arr 1比arr大 40 字节10 个int的大小。如果笔试题问二者差值答案就是指针类型的步长不同。这里可以进一步打印差值来验证#include stdio.h int main(void) { int arr[10] {0}; int *p arr; printf(p 1 的偏移: %ld\n, (p 1) - p); // 1 printf(arr 1 的偏移: %ld\n, (arr 1) - arr); // 指针差值为 1单位为 int[10] // 按字节差值计算 printf(字节差: %zu\n, (char *)(arr 1) - (char *)arr); // 40 return 0; }运行结果p 1 的偏移: 1 arr 1 的偏移: 1 字节差: 40这里需要特别注意直接打印(arr 1) - arr结果是 1因为指针减法的单位是指针指向类型的字节大小。所以如果想得到字节数的差异必须先把两个地址都转换成char *。4.3 题目三数组名作函数参数后 sizeof 丢失#include stdio.h void fun(int a[]) { int len sizeof(a) / sizeof(a[0]); printf(inside fun, len %d\n, len); } int main(void) { int a[10] {0}; int len sizeof(a) / sizeof(a[0]); printf(in main, len %d\n, len); fun(a); return 0; }输出in main, len 10 inside fun, len 2在fun内部sizeof(a)变成了指针大小所以sizeof(a) / sizeof(a[0])计算出来的结果不再等于 10。在 64 位系统上8 / 4 2在 32 位系统上4 / 4 1。这个题目非常经典几乎每年笔试题都会出现类似变体。解决办法就是额外传递一个长度参数千万不要在函数内部再用sizeof计算数组长度。4.4 题目四字符数组与字符串指针的修改权限#include stdio.h int main(void) { char s1[] hello; char *s2 hello; s1[0] H; printf(s1 %s\n, s1); // s2[0] H; // 未定义行为可能段错误 printf(s2 %s\n, s2); return 0; }在多数现代编译器/操作系统上s2[0] H会导致段错误即使没有崩溃也属于未定义行为。笔试题目常常这样考char *s2 hello; s2[0] H;是否合法。准确答案是不合法属于未定义行为字符串字面量不能通过指针修改。在某些老旧教材或编译器里这个操作可能碰巧能运行但这是极端危险的习惯任何现代开发中都不应使用。4.5 题目五指针数组和数组指针的理解有一类题目比较常考区分“指针数组”和“数组指针”。int *p[4]; // 指针数组p 是一个数组数组的每个元素都是 int * int (*q)[4]; // 数组指针q 是一个指针指向含 4 个 int 元素的数组很多初学者把这两行搞混尤其是第二行。判断方法很简单先看标识符p先和谁结合。p先和[4]结合说明它是一个数组叫做“指针数组”。q先和*结合因为有括号说明它是一个指针指向的类型是int [4]叫做“数组指针”。一个经典的笔试题如下#include stdio.h int main(void) { int a[4] {1, 2, 3, 4}; int *p[4]; // 指针数组 int (*q)[4]; // 数组指针 // 指针数组每个元素保存一个 int 的地址 for (int i 0; i 4; i) { p[i] a[i]; } for (int i 0; i 4; i) { printf(%d , *p[i]); } printf(\n); // 数组指针q 指向整个数组 a q a; for (int i 0; i 4; i) { printf(%d , (*q)[i]); } printf(\n); return 0; }输出1 2 3 4 1 2 3 4前者是通过“指针数组的元素指向数组 a 的各个元素”来访问后者是“数组指针直接指向整个数组”来访问。两者都能打印出 1 2 3 4但底层结构完全不同。5. 常见问题与排查思路在学习和实际开发中关于数组名和指针的报错和问题非常多。下面整理几个典型问题以及对应的排查思路。问题现象常见原因解决思路sizeof(arr)在函数内不是数组长度数组形参退化为指针额外传入长度参数或在调用处计算长度arr编译报错数组名不是可修改左值使用临时指针变量修改char *s hello后崩溃字符串字面量在只读区改用char s[] helloarr 1不是指向最后一个元素指针步长由类型决定理解int (*)[N]类型按需使用arr N二维数组传入函数后编译失败形参写错类型形参写作int a[][N]或int (*a)[N]函数内用sizeof(a)/sizeof(a[0])得到错误长度形参退化为指针把长度作为参数传入把int a[3][4]当作int **使用混淆了int **与int (*)[4]明确类型不要强行转换逐个说明一下常见坑的排查思路问题一在函数内计算数组长度失败这是最普遍的问题。只要看到函数参数写成int a[]就要提醒自己这里的a已经被改写成了int *a。在函数内部无法通过sizeof获取数组长度正确做法是使用len参数。问题二数组名自增报错arr无法编译的原因不是编译器有问题而是数组名不是变量。如果确实需要遍历数组可以用一个指针变量int arr[5] {1, 2, 3, 4, 5}; int *p arr; while (p ! arr 5) { printf(%d , *p); p; }这样既保留了数组本身又通过指针完成遍历。问题三字符串修改导致崩溃优先使用字符数组来保存需要修改的字符串char s[] hello; s[0] H; // 安全如果需要读取字符串推荐使用const char *s hello;这种方式让编译器帮你检查是否有非法修改。问题四arr 1偏移量过大如果你只是想到达数组的最后一个元素应该用arr 4或arr[4]而不是arr 1。arr 1的用途通常是“跳过整个数组”在某些缓冲区操作或动态数组迭代时会用到日常访问元素时很少直接使用。问题五二维数组形参错误编译错误信息可能会像这样error: cannot convert int (*)[4] to int **这说明类型不匹配。二维数组名退化为int (*)[4]而不是int **。正确形参应该是void print_matrix(int a[][4], int rows); // 等价于 void print_matrix(int (*a)[4], int rows);如果列数不固定C99 之后可以使用变长数组void print_matrix(int rows, int cols, int a[rows][cols]);但这种写法在 C 中不可用需要注意区分。6. 最佳实践与工程建议理解数组名和指针的区别之后在实际编码中还有一些值得养成的习惯可以有效减少踩坑概率。6.1 明确数组名和指针的语义在写代码时始终问自己这个地方你是想要“一整块连续内存”还是只想“保存一个地址”如果需要定义一块连续存储、内容可变的数据集合优先声明为数组int scores[100];如果只是需要指向某些数据、在函数间传递地址使用指针void process(int *data, int len);6.2 不要在函数内部用 sizeof 计算数组长度这是一个硬性建议。不管形参写的是int a[]还是int *a在函数体内它都是一个指针不要写出这样的代码// 错误示范 void bad_func(int arr[]) { int len sizeof(arr) / sizeof(arr[0]); // 结果不是数组长度 }正确写法// 正确示范 void good_func(int arr[], int len) { for (int i 0; i len; i) { arr[i] * 2; } }在调用处int a[10] {0}; int len sizeof(a) / sizeof(a[0]); good_func(a, len);6.3 字符串常量一律用 const char * 指向除非你非常确定要在原地修改字符串内容否则不要用非 const 指针指向字符串字面量// 推荐 const char *name CSDN; // 不推荐字符串字面量被修改时可能导致崩溃 char *name CSDN;使用const char *的好处是编译器会在你试图修改字符串内容时直接报错从源头上避免段错误风险。6.4 尽量用指针运算代替下标访问遍历时在遍历数组时下标写法arr[i]没有错但有时候函数内部用的是指针用指针运算更直观且性能更高一点int sum_array(const int *arr, int len) { int sum 0; for (const int *p arr; p arr len; p) { sum *p; } return sum; }注意这里用了const int *声明我们不会通过指针修改数组内容语义更清晰。6.5 使用 typedef 提高可读性对于复杂的数组指针或指针数组类型用typedef能显著提高可读性typedef int MatrixRow[4]; void print_row(MatrixRow row, int len) { for (int i 0; i len; i) { printf(%d , row[i]); } printf(\n); }这里的MatrixRow就是int [4]的类型别名row退化为指向int的指针。对于数组指针的类型别名typedef int (*Array4Ptr)[4]; void print_all(int m[][4], int rows) { for (int i 0; i rows; i) { Array4Ptr rowPtr m[i]; for (int j 0; j 4; j) { printf(%d , (*rowPtr)[j]); } printf(\n); } }6.6 编译时打开警告选项在开发 C 代码时一定要开启编译警告往往能提前发现类型不匹配等问题gcc -stdc11 -Wall -Wextra -pedantic test.c -o test-Wall和-Wextra会输出大多数常见警告-pedantic会提示不符合标准的行为。6.7 注意 const 的位置const放在类型前和类型后的含义对指针来说有一点差别const int *p; // p 指向 const int不能通过 p 修改目标值 int *const p; // p 本身是 const 指针不能改指向但可以通过 p 修改目标值 const int *const p; // 两者都不能修改结合数组传参场景推荐在只读场景下使用const int *arr这样既能避免误修改原始数组也便于编译器做优化。7. 进一步学习建议数组名和指针是 C 语言中非常基础但又极容易混淆的知识点掌握它们的关键不在于背结论而在于多做对比实验、多写代码验证。建议你亲手跑一遍下面这个“全家桶”验证程序把本节所有结论一次性看全#include stdio.h void show(int a[], int len) { printf(inside show: sizeof(a) %zu\n, sizeof(a)); for (int i 0; i len; i) { printf(%d , a[i]); } printf(\n); } int main(void) { int arr[5] {1, 2, 3, 4, 5}; int *p arr; printf(sizeof(arr) %zu\n, sizeof(arr)); printf(sizeof(p) %zu\n, sizeof(p)); printf(arr %p, arr %p\n, (void *)arr, (void *)arr); printf(arr 1 %p, arr 1 %p\n, (void *)(arr 1), (void *)(arr 1)); show(arr, 5); char s[] hello; const char *s2 hello; printf(sizeof(s) %zu\n, sizeof(s)); s[0] H; printf(s %s\n, s); printf(s2 %s\n, s2); return 0; }写完之后仔细观察每一行的输出再对照本文每一节的解释你就能把数组名和指针的关系真正理清。后面如果有时间可以继续深入这样几个主题C 语言函数指针与回调机制理解复杂声明解读方法。C 语言动态内存分配与指针的配合掌握malloc、free与数组的关系。C 语言多级指针和指针数组的更多应用场景。数据结构中的链表、树等结构如何使用指针实现。学习指针没有捷径只能靠多写、多调试、多看底层行为。希望这篇文章能帮你把“数组名和指针”这个经典坑彻底填平。动手运行一下文中的代码对比输出结果相信你会有更直观的收获。

相关新闻

最新新闻

Linux 磁盘与文件系统管理

Linux 磁盘与文件系统管理

文章目录 1. 核心概念:从物理磁盘到文件系统2. 磁盘使用率与健康度查看2.1 查看磁盘空间占用:df (Disk Free)2.2 统计目录/文件大小:du (Disk Usage)2.3 查看磁盘块设备与分区列表:lsblk & fdisk 3. 实战:给服务器…

2026/8/31 9:04:51
Python实现福彩3D和值分析:从统计分布到蒙特卡罗模拟实战

Python实现福彩3D和值分析:从统计分布到蒙特卡罗模拟实战

先说明一点:福彩 3D 的开奖本质是随机事件,任何算法都不能“精准预测”下一期的具体号码。本文想聊的重点,是如何用 Python 构建一套“和值范围分析工具”,通过对历史数据进行频次统计、均值分布、遗漏分析和蒙特卡罗模拟&#xf…

2026/8/31 9:04:51
overlay图像叠加实战:从Pillow到ComfyUI的轻量合成方案

overlay图像叠加实战:从Pillow到ComfyUI的轻量合成方案

这次我们不谈又大又重的AI模型,而是看一个很实用的图像处理关键词:overlay。标题里的“绵绵很好_overlay”,可以理解为一套用于图像叠加合成的素材包或工作流,实际解决的问题是:如何把光效、纹理、贴纸、边框这些叠加层…

2026/8/31 9:04:51
MATLAB优化算法工具箱全攻略:从fmincon到混合求解

MATLAB优化算法工具箱全攻略:从fmincon到混合求解

之前写过一篇 MATLAB 遗传算法工具箱的入门教程,很多读者在评论区留言问:“全局优化算法有了,那 MATLAB 自带的优化算法工具箱到底能干什么?”、“fmincon 和 ga 到底什么区别?”、“有没有一套系统的函数清单&#xf…

2026/8/31 9:04:51
Exo 分布式 AI 集群容错完全指南:一台设备掉线时,集群如何继续运转

Exo 分布式 AI 集群容错完全指南:一台设备掉线时,集群如何继续运转

Exo 分布式 AI 集群容错完全指南:一台设备掉线时,集群如何继续运转 【免费下载链接】exo Run frontier AI locally. 项目地址: https://gitcode.com/GitHub_Trending/exo8/exo 半夜,集群里某台机器突然断电,其余设备照常运…

2026/8/31 9:04:51
Academic Research Skills许可证指南:CC BY-NC 4.0下个人与团队使用边界

Academic Research Skills许可证指南:CC BY-NC 4.0下个人与团队使用边界

Academic Research Skills许可证指南:CC BY-NC 4.0下个人与团队使用边界 【免费下载链接】academic-research-skills Academic Research Skills for Claude Code: research → write → review → revise → finalize 项目地址: https://gitcode.com/GitHub_Trend…

2026/8/31 8:59:51