目录
一、数组名到底是什么?
✅ 例外一:sizeof(单独数组名)
✅ 例外二:&数组名
二、如何通过指针对数组进行访问?
1、标准实操写法:指针遍历赋值+打印
2、指针访问数组的其他方式:
写法一:arr[i] 等价于 *(arr+i)
写法二:p[i] 等价于 *(p+i)
写法三:*(arr+i) 等价于 *(i+arr)(加法交换律延伸写法)
写法四:arr[i] 等价于 i[arr]
三、一维数组传参的本质
✅ 先看现象:同一个 sizeof,内外结果完全不一样
✅ 运行结果
✅ 底层原理一句话讲死
✅ 正确规范写法
💡 补充拓展:数组为什么没有值传递?值传递 vs 指针传递核心区别(带代码)
1、先搞懂两种传递核心规则
2、重点核心结论
3、顺带解答疑惑:为什么形参 arr[] 里面可以不写数字?
4、对照实测代码:一眼看懂指针传递会修改原数组
5、总结
四、数组与指针到底有什么本质区别?
1、核心底层区别:内存空间完全不一样
2、关键区别一:sizeof 计算结果天差地别(前面学过的例外直接复用)
3、关键区别二:能不能修改指向(常量 VS 变量)
4、关键区别三:传参后身份直接互换(对应前面数组传参)
5、为什么写法还能混用?
✅ 最后一句终极总结
五、指针数组是什么?
1、先记一句话定义
2、内存逻辑一眼看懂
3、基础入门代码:直接定义+打印地址,秒懂结构
4、模拟实现一个二维数组
5、重点补充:和真实二维数组的区别
六、数组指针是什么?
1、数组指针核心定义
2、标准写法一定要写对(易错点)
3、底层原理 + 配套代码
4、数组指针最关键:+1 跨步演示
5、数组指针真正实战用途:遍历二维数组(唯一正经用处)
7、终极对标:指针数组 VS 数组指针
七、全文总结
1、数组名核心铁律
2、指针访问数组:所有写法底层全互通
3、一维数组传参:没有数组,只有指针
4、数组 VS 指针:看着像,本质完全不一样
5、指针数组:数组里面装指针
6、数组指针:指针指着整片数组
一、数组名到底是什么?
先抛一句核心定论:绝大多数场景下,数组名 = 数组首元素的起始地址。
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; // 打印首元素地址、数组名对应地址 printf("&arr[0] = %p\n", &arr[0]); printf("arr = %p\n", arr); return 0; }运行结果,两个地址数值完全一模一样。这就实锤了:常规写法里,数组名就是拎出来首元素的地址,本质就是个地址常量。
但是,也有例外:数组名不是随时随地都代表首元素地址,只有两个专属例外情况,除此之外全是首元素地址:
✅ 例外一:sizeof(单独数组名)
先说第一个核心例外:只要 sizeof 里面只写数组名、不写下标、不写指针,此时数组名不代表首元素地址,而是代表整个数组,计算的是数组整体总字节数。很多同学这里会卡住疑惑:我们前面明明说过,常规情况下数组名就是首元素地址,而地址的大小是由运行环境决定的,32位环境下指针地址固定占4字节,64位环境下指针地址固定占8字节。按理来说,如果arr真的只是单纯的首元素地址,那sizeof(arr)运算结果,要么是4字节、要么是8字节才对。但实际运行代码后会发现,结果根本不是4也不是8,而是数组整体的总字节数。原因非常简单:C语言语法规则单独规定了特例,当数组名直接放在sizeof内部单独参与运算时,就会触发专属例外机制,不再把arr当成普通地址处理,直接识别成一整片数组本身,直接统计全部内存占用。
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; // 正常情况下,arr是首元素地址 printf("arr 地址:%p\n", arr); // 例外场景:sizeof(数组名) 算的是整个数组大小 printf("sizeof(arr) = %zu 字节\n", sizeof(arr)); return 0; }关键点:10个int元素,每个占4字节,最终输出结果固定为40; 如果arr只是普通指针,结果只会是4/8字节,一眼就能区分。
✅ 例外二:&数组名
第二个例外:对数组名直接取地址&arr,这里我们重点把三组写法彻底掰明白,分别是:&arr[0]、arr、&arr。先说共同点:三者直接打印出来的内存地址数值,看起来是完全一模一样的,肉眼看不出任何区别。但核心差距不在数值,而在类型,最直观的区分方式就是看「三者分别+1之后的跳转跨度」。arr 和 &arr[0] 逻辑完全等价,都是数组首元素地址,执行+1操作时,只会向后跳过一个int元素,固定偏移4个字节,正常遍历数组全靠它。而 &arr 完全不一样,它虽然地址数值没变,但执行+1操作时,会直接一次性跳过一整个完整数组,直接偏移数组全部占用的字节空间。简单汇总区分:前两者是「元素级别地址」,加减只跨单个元素;&arr是「数组整体级别地址」,加减直接跨整片数组。这里提前记一个关键词:此时的 &arr 本质就是一个数组指针,专门用来指向一整个数组,后续我们会单独重点拆解讲解数组指针用法。
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; printf("普通首元素地址 arr = %p\n", arr); printf("数组整体地址 &arr = %p\n", &arr); // 核心差距在 +1 跨步 printf("arr + 1 只跨1个int = %p\n", arr + 1); printf("&arr + 1 跨整个数组 = %p\n", &arr + 1); return 0; }核心结论秒记:arr+1:跳过1个元素,偏移4字节; &arr+1:跳过整段数组,直接偏移40字节; 数值相同,类型不同,跨步天差地别。
二、如何通过指针对数组进行访问?
搞懂了数组名的底层本质,用指针访问数组就是顺水推舟,完全没有难度。核心逻辑很直白:既然数组名是首元素地址,那咱们直接用指针变量接住这个地址,后续靠指针偏移就能随便读写数组所有元素。
1、标准实操写法:指针遍历赋值+打印
#include <stdio.h> int main() { int arr[10] = {0}; int sz = sizeof(arr) / sizeof(arr[0]); int* p = arr; // 指针p直接接收数组首元素地址,等价于p = &arr[0] // 指针偏移批量录入数据 for(int i = 0; i < sz; i++) { scanf("%d", p + i); } // 指针偏移批量读取打印数据 for(int i = 0; i < sz; i++) { printf("%d ", *(p + i)); } return 0; }2、指针访问数组的其他方式:
前面我们已经完成核心绑定:int* p = arr;,指针 p 完全等价于数组名 arr,二者都是数组首元素的起始地址。C语言中数组下标访问不是特殊语法,底层核心公式就一句:下标访问 = 地址 + 偏移量 + 解引用。同时加法自带交换律,[]只是普通运算符号,没有严格前后顺序,所以所有写法全部互通成对、等价通用,下面两两联动拆解,每组绑定对照,搭配可直接编译运行的代码,一眼吃透底层关联。
写法一:arr[i] 等价于 *(arr+i)
这是最基础、工程开发最常用的一组联动写法。编译器编译代码时,看到 arr[i] 不会单独识别下标,会自动底层翻译成 *(arr+i):先拿数组名arr取出首元素地址,加上下标i算出偏移跨度,最后解引用拿到对应位置的真实元素,两组写法运行效果、底层逻辑完全一模一样。
#include <stdio.h> int main() { int arr[5] = {10,20,30,40,50}; int i = 2; // 成对联动使用,运行结果完全一致 printf("arr[i] 访问结果:%d\n", arr[i]); printf("*(arr+i)访问结果:%d\n", *(arr + i)); return 0; }写法二:p[i] 等价于 *(p+i)
既然 int* p = arr,指针p和数组名arr身份完全对等,那数组能用的访问规则,指针全部通用。我们既可以用纯指针偏移解引用的 *(p+i) 硬核写法,也可以直接给指针搭配下标写成 p[i],不用刻意切换思维,两组写法互通互换,是指针遍历数组的核心常用联动组合。
#include <stdio.h> int main() { int arr[5] = {10,20,30,40,50}; int* p = arr; int i = 2; // 指针适配成对写法,贴合指针遍历逻辑 printf("p[i] 访问结果:%d\n", p[i]); printf("*(p+i) 访问结果:%d\n", *(p + i)); return 0; }写法三:*(arr+i) 等价于 *(i+arr)(加法交换律延伸写法)
地址加偏移量本质是普通加法运算,数学加法天然满足交换律,不分先后顺序。所以 *(arr+i) 可以直接互换位置写成 *(i+arr),编译器不会报错,正常识别地址偏移逻辑。日常不写,但一定要认识。
#include <stdio.h> int main() { int arr[5] = {10,20,30,40,50}; int i = 2; // 利用交换律联动互换,语法完全合规 printf("*(arr+i)访问结果:%d\n", *(arr + i)); printf("*(i+arr)访问结果:%d\n", *(i + arr)); return 0; }写法四:arr[i] 等价于 i[arr]
重点记住核心前提:[] 只是运算符,不强制要求前面必须是数组名/指针、后面必须是下标。结合前面所有等价逻辑+交换律,直接把数组名和下标互换位置,写出 i[arr] 也能正常编译运行,和 arr[i] 结果完全一致。但可读性极差,建议写代码时不要使用这种写法,认识即可。
#include <stdio.h> int main() { int arr[5] = {10,20,30,40,50}; int i = 2; // 语法联动等价,仅限学习理解,禁止工程使用 printf("arr[i] 访问结果:%d\n", arr[i]); printf("i[arr] 访问结果:%d\n", i[arr]); return 0; }编译器底层做好了适配,不用纠结写法,几组等价关系直接套用:
arr[i] ↔ *(arr + i) ↔ *(p + i) ↔ p[i] ↔ *(i + arr) ↔ i[arr] ↔ *(i + p) ↔ i[p]
简单说:下标访问和指针解引用访问,底层原理完全一样,只是写法不一样。日常开发想用哪个用哪个,灵活切换就行,但尽量不要用后4种,认识即可。
三、一维数组传参的本质
前面我们已经死磕明白了一句话:数组名绝大多数时候,都是首元素的地址。
那把数组传给函数,传的是什么?传的不是整片数组,传的只是一个地址(首元素地址)。
这就直接导致一个大坑:在子函数内部,再也求不出数组真实元素个数。
✅ 先看现象:同一个 sizeof,内外结果完全不一样
#include <stdio.h> // x64环境运行,指针长度为8字节 // 数组传参:看似是数组,本质是指针 void test(int arr[]) //int* arr { // 这里算的是:指针变量的大小,不是数组大小 //此时的arr不在是数组名而是一个指针 int sz2 = sizeof(arr) / sizeof(arr[0]); printf("子函数内部 sz2 = %d\n", sz2); } int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; // 这里 arr 是真实数组,sizeof 算整个数组 int sz1 = sizeof(arr) / sizeof(arr[0]); printf("主函数内部 sz1 = %d\n", sz1); // 本质:只把 arr首元素地址 传进去了 test(arr); return 0; }✅ 运行结果
主函数里:sz1 = 10 ✅ 正常算出真实个数 子函数里:sz2 = 2 ❌ 永远算不对
✅ 底层原理一句话讲死
1)数组传参写void test(int arr[]),只是语法好看,编译器底层直接翻译成void test(int* arr); 2)形参本质就是一个指针变量,只存地址,不存数组; 3)子函数里 sizeof(arr) 测的是指针大小(4字节 / 8字节),跟数组没关系。
✅ 正确规范写法
数组本身只传地址,数组长度单独当参数传进去。
#include <stdio.h> // 标准写法:数组 + 长度一起传 void bubble_sort(int arr[], int sz) { // 直接用外面传进来的 sz,不要自己算 sizeof for(int i = 0; i < sz; i++) { printf("%d ", arr[i]); } } int main() { int arr[] = {2,5,1,3,4}; int sz = sizeof(arr) / sizeof(arr[0]); // 老老实实把长度一起带过去 bubble_sort(arr, sz); return 0; }本节终极口诀:数组传参不传数,只传地址不传物; 子函数里别求长,长度外面带进来。
一维数组传参,本质不是传整片数组,只传了数组名,也就是只传了首元素地址。
所以子函数里的数组形参,不管写成int arr[],还是直接写成int* arr,本质都是一个指针变量。这时候再用sizeof(arr),算的是指针本身的字节大小,根本不是数组总长度,自然算不对元素个数。解决方案也很简单:数组传参时,额外把数组长度单独当成参数传进去即可。
💡 补充拓展:数组为什么没有值传递?值传递 vs 指针传递核心区别(带代码)
很多同学学完数组传参后都会疑惑:普通变量传参有值传递,那数组能不能用值传递?这里直接把底层逻辑掰开揉碎讲透,结合指针传递对照,看完彻底吃透数组传参底层逻辑。
1、先搞懂两种传递核心规则
✅ 值传递:会把原数据完整拷贝一份,给函数开辟全新的独立内存空间,形参修改数据,完全不会影响外面的原数据,开销大、占用额外内存。
✅ 指针传递(地址传递):不拷贝整个数组,只拷贝数组首元素的地址传给函数,不用额外开辟大片数组内存,直接通过地址操作原数组的真实数据,开销极小、效率极高。
2、重点核心结论
C语言中:数组不存在值传递,数组传参只有且只能是指针传递!
原因很现实:如果数组支持值传递,每次传大数组都要完整拷贝几十、上百个元素,额外开辟同等大小内存,程序运行效率直接崩盘,内存严重浪费。所以C语言直接底层规则限制,一刀切优化:数组只传地址、不传本体。
3、顺带解答疑惑:为什么形参 arr[] 里面可以不写数字?
因为数组传参本质是指针传递,形参根本不是真实数组,只是一个伪装成数组样式的指针变量,不需要为数组开辟完整连续内存空间,自然不用标注数组长度,写不写数字都不影响使用,编译器直接忽略括号内的数字。
4、对照实测代码:一眼看懂指针传递会修改原数组
#include <stdio.h> // 数组传参:本质是指针传递,只拷贝首地址,不拷贝数组 void change_arr(int arr[]) { // 直接通过地址,修改外面原始数组的真实数据 arr[0] = 999; } // 普通变量值传递做对比:拷贝变量本身,不影响原值 void change_num(int num) { num = 999; } int main() { int arr[5] = {1,2,3,4,5}; int num = 10; // 数组指针传递:会改原数据 change_arr(arr); // 普通变量值传递:不改原数据 change_num(num); printf("数组第一个元素:%d\n", arr[0]); // 变成999,原数组被修改 printf("普通变量数值:%d\n", num); // 还是10,原值不受影响 return 0; }5、总结
数组元素被成功修改,普通变量原值不变,差距一目了然。
一句话收口:
数组没有值传递,全程只传首地址;不开新空间,直接改原数据;形参空括号随便写,全因本质是指针!
四、数组与指针到底有什么本质区别?
前面我们把数组名、指针访问数组、数组传参全部啃完了,很多人越学越迷糊:既然数组名是首元素地址,又能赋值给指针变量,写法还能互通混用,那数组和指针不就是一样的吗?
答案:完全不一样,只是“用法看起来像”,底层内存、属性、规则完全不同!
下面结合前面所有知识点,直接对比 + 代码验证,一次性彻底分清。
1、核心底层区别:内存空间完全不一样
✅ 数组:是一块真实连续的内存空间
定义 int arr[10];系统会直接在内存中开辟10个连续int空间,实打实占用内存,存的是真实数据。 数组名代表这块空间,是地址常量,不能随便改指向。
✅ 指针:只是一个单独的变量,只存地址,不存数据
定义 int *p;只会开辟一块指针大小的空间(4/8字节),里面只存一个地址号码,不存数组数据。 指针是变量,想指向谁就指向谁,可以随意修改指向。
2、关键区别一:sizeof 计算结果天差地别(前面学过的例外直接复用)
这是最容易肉眼区分的判断标准。
数组名单独放sizeof里:算整个数组总字节数。指针放sizeof里:只算指针本身大小,4字节或8字节。
#include <stdio.h> int main() { int arr[10]; int* p = arr; printf("sizeof(arr) = %zu\n", sizeof(arr)); // 40 实打实整片数组 printf("sizeof(p) = %zu\n", sizeof(p)); // 8 只是一个地址变量 return 0; }结论:能算出大空间的是数组,永远只有4/8字节的是指针。
3、关键区别二:能不能修改指向(常量 VS 变量)
数组名是地址常量,不可修改指向
数组名永远指向数组开头,不能++、不能赋值,写 arr++ 直接报错。
指针是变量,随意移动、随意改指向
p++、p = &num,随便写,灵活切换指向不同内存。
#include <stdio.h> int main() { int arr[10]; int* p = arr; p++; // 合法!指针可以移动指向 // arr++; // 非法!数组名是常量,不能修改 return 0; }4、关键区别三:传参后身份直接互换(对应前面数组传参)
在主函数里:数组是数组,实打实连续空间。传到子函数里:数组直接退化,变成指针。
这也就是为什么子函数算不出数组长度,因为数组身份没了,只剩一个指针地址。
5、为什么写法还能混用?
很多人疑惑:既然不一样,为什么 arr[i] 和 p[i] 都能用?
很简单:访问元素只需要“地址+偏移”,不管你是数组名还是指针,只要是地址,就能下标访问。
只是:长相互通,本质不同。
✅ 最后一句终极总结
数组:真实连续内存空间,地址常量,空间大,不能改指向,sizeof能算出真实长度。
指针:单独存地址的变量,空间小,能随意改指向,永远算不出数组长度。
写法可以混用,底层绝对不是一回事!
五、指针数组是什么?
前面我们把「数组名、指针、数组传参、数组与指针区别」全部啃完,现在顺势进阶,来讲第一个高频易混考点:指针数组。
1、先记一句话定义
指针数组,本质是数组。
整型数组 → 存放整型数据的数组 字符数组 → 存放字符数据的数组指针数组 → 专门存放「指针(地址)」的数组
核心语法:int* arr[num];
含义:数组里的每一个元素,类型都是 int* 类型的指针,每个格子里存的都是地址,不是普通数字。
2、内存逻辑一眼看懂
普通数组:格子里放的是实实在在的数据; 指针数组:格子里不放普通数据,全部放地址,每个地址可以独立指向一块内存空间。
3、基础入门代码:直接定义+打印地址,秒懂结构
#include <stdio.h> int main() { // 定义:arr是数组,一共5个元素,每个元素都是int*指针 int* arr[5]; int a = 10; int b = 20; int c = 30; // 给指针数组的每个元素,存入不同变量的地址 arr[0] = &a; arr[1] = &b; arr[2] = &c; // 打印地址,再解引用取出真实数据 printf("arr[0]存的地址:%p,对应值:%d\n", arr[0], *arr[0]); printf("arr[1]存的地址:%p,对应值:%d\n", arr[1], *arr[1]); printf("arr[2]存的地址:%p,对应值:%d\n", arr[2], *arr[2]); return 0; }运行结果看得明明白白:数组里存的全是地址,解引用就能拿到外面变量的值。
4、模拟实现一个二维数组
用指针数组,把好几个独立一维数组“捆在一起”,模拟出二维数组的访问效果。
#include <stdio.h> int main() { // 三个独立的一维数组,内存不连续 int arr1[] = {1,2,3,4,5}; int arr2[] = {2,3,4,5,6}; int arr3[] = {3,4,5,6,7}; // 指针数组:存放三个一维数组的数组名(首元素地址) int* parr[3] = {arr1, arr2, arr3}; // 双层循环,模拟二维数组遍历访问 for(int i = 0; i < 3; i++) { for(int j = 0; j < 5; j++) { // parr[i] 取出对应一维数组的首地址 // parr[i][j] 等价于 *(parr[i] + j) 访问元素 printf("%d ", parr[i][j]); } printf("\n"); } return 0; }5、重点补充:和真实二维数组的区别
✅ 指针数组模拟二维数组:每一行内存不连续,只是地址挂靠在一起,灵活度高; ✅ 真实二维数组:整块内存完全连续,固定行列,灵活性差一点。
六、数组指针是什么?
前面我们刚学完指针数组,现在趁热打铁,把它的“死对头”——数组指针一次性讲透。
这两个名词90%的人都会搞混,其实只要记住一句话,终身不会错:
指针数组:本质是数组,里面存指针。
数组指针:本质是指针,指向一整个数组。
1、数组指针核心定义
数组指针,是一个指针变量。
普通指针 int* p:指向的是一个整型变量。
数组指针 int (*p)[10]:指向的是一整个整型数组,一步跨越整片数组。
2、标准写法一定要写对(易错点)
✅ 正确数组指针写法:int (*p)[10];
❌ 错误写法:int *p[10]; (这是上一节讲的指针数组!)
小括号不能丢!
因为 [ ] 优先级比 * 高,不加括号,就先和 p 结合变成数组,直接跑偏成指针数组。
3、底层原理 + 配套代码
前面我们在数组名例外二里面提过一嘴:&arr 就是数组指针类型,现在正式兑现讲解。
数组指针专门接收 &arr(整个数组的地址),不接收普通首元素地址。
#include <stdio.h> int main() { // 定义一个真实一维数组,10个int元素 int arr[10] = {1,2,3,4,5,6,7,8,9,10}; // 定义数组指针:p 专门指向「有10个int元素的数组」 int (*p)[10] = &arr; // 打印地址对比 printf("arr 首元素地址:%p\n", arr); printf("&arr 整个数组地址:%p\n", &arr); printf("p 数组指针存放:%p\n", p); return 0; }运行一眼看懂:p 完美接住了整个数组的地址 &arr。
4、数组指针最关键:+1 跨步演示
前面例外二学过跨步,这里直接联动复用,无缝衔接:
普通指针 +1:走 4 字节,跨 1 个元素。
数组指针 +1:直接跳过一整行数组,一次性走 40 字节!
#include <stdio.h> int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int (*p)[10] = &arr; printf("p = %p\n", p); // +1 直接跳过整个数组 printf("p+1 = %p\n", p + 1); return 0; }<- 跨越40个字节
这就是数组指针和普通指针最大的区别:跨步超级大。
5、数组指针真正实战用途:遍历二维数组(唯一正经用处)
数组指针平时不用,只有一个核心用处:二维数组传参 + 遍历二维数组。
二维数组传参,本质就是用数组指针接收每一行。
#include <stdio.h> // 二维数组传参,本质就是数组指针接收 void print_arr(int (*p)[5], int row) { // 逐行遍历 for(int i = 0; i < row; i++) { for(int j = 0; j < 5; j++) { // *(p+i) 定位到每一行 printf("%d ", *( *(p + i) + j )); // 等价与 p[i][j] } printf("\n"); } } int main() { // 真实二维数组,内存连续 int two_arr[3][5] = { {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15} }; // 传二维数组名,本质传每一行的起始地址,适配数组指针 print_arr(two_arr, 3); return 0; }7、终极对标:指针数组 VS 数组指针
① 指针数组 int* p[]
✅本质:数组
✅ 里面存:一堆指针地址
✅ 用处:挂靠多个一维数组,模拟二维数组(内存不连续)
✅ 口诀:数组里面装指针
② 数组指针 int (*p)[]
✅ 本质:指针
✅ 指向:一整片完整数组
✅ 用处:专门接收二维数组,遍历二维数组(内存连续)
✅ 口诀:指针指着一整片数组
七、全文总结
到这里,数组、指针、指针数组、数组指针四大核心考点,我们全部从零啃完。最后不废话,把整篇博客所有重点,按顺序闭环总结一遍,看完直接融会贯通,刷题、写代码、面试全够用。
1、数组名核心铁律
✅ 常态规则:数组名就是首元素地址,可以直接赋值给指针;
✅ 唯一两个例外:
1.sizeof(数组名):代表整个数组,计算整片数组总字节大小,不再是地址;
2.&数组名:代表一整个数组的地址,类型是数组指针,+1直接跨整片数组;
✅ 关键区别:arr 和 &arr[0] 只跨元素,&arr 直接跨数组,数值一样,类型完全不一样。
2、指针访问数组:所有写法底层全互通
1、int* p = arr;指针直接接管数组,等价互换;
2、六大等价写法底层通用:arr[i] ↔ *(arr+i) ↔ p[i] ↔ *(p+i) ↔ *(i+arr) ↔ i[arr];
3、工作只写前四种,后两种认识即可,工程绝对别写;
4、本质逻辑:下标只是运算符,访问数组全靠「地址 + 偏移量」。
3、一维数组传参:没有数组,只有指针
1、数组传参不传整片数据,只传首元素地址;
2、形参 int arr[] 只是伪装,底层直接翻译成 int* 指针;
3、子函数内部无法用sizeof求数组长度,永远算不对;
4、数组没有值传递,只有指针传递,不拷贝数据,直接改原数组;
5、形参括号里可以不写数字,因为本质不是数组,不用开空间;
6、规范写法:数组 + 数组长度,两个参数一起传。
4、数组 VS 指针:看着像,本质完全不一样
1、数组:真实连续内存空间,地址常量,不能自增、不能改指向,sizeof能算出真实数组大小; 2、指针:单独4/8字节小变量,只存地址,不存数据,能随意改指向,永远算不出数组长度;
3、混用原因:只要是地址就能下标访问,表面互通,底层独立。
5、指针数组:数组里面装指针
1、写法:int* arr[];
2、本质:是数组,每个元素都是指针地址;
3、作用:挂靠多个独立一维数组,模拟二维数组;
4、特点:每行内存不连续,灵活性高,纯地址挂靠。
6、数组指针:指针指着整片数组
1、写法:int (*p)[10];括号绝对不能丢;
2、本质:是指针,专门指向一整个数组;
3、接收:只能接收 &arr 数组整体地址;
4、特点:+1直接跳过一整片数组,跨步超大;
5、用处:唯一核心用途——二维数组传参、遍历二维数组。