指针 变量是什么
程序中的变量只是一段存储空间的别名 ,那么是不是必须通过这个别名才能使用这段存储空间呢?
不是,我们还可以通过这段空间的地址或者说起始地址去使用这段空间;
*的意义
在指针声明时 ,* 号表示所声明的变量为指针
在指针使用时 ,* 号表示取指针所指向的内存空间的地址
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main () { int i = 0 ; int * pI; char * pC; float * pF; pI = &i; *pI = 10 ; printf ("%p, %p, %d\n" , pI, &i, i); printf ("%ld, %ld, %p\n" , sizeof (int *), sizeof (pI), &pI); printf ("%ld, %ld, %p\n" , sizeof (char *), sizeof (pC), &pC); printf ("%ld, %ld, %p\n" ,sizeof (float *), sizeof (pC), &pF); return 0 ; }
在C语言中,指针的大小取决于计算机操作系统 。在32位平台上,指针的大小是4个字节;在64位平台上,指针的大小是8个字节;
传值调用与传地址调用
指针是变量,因此可以声明指针参数
当一个函数体内部需要改变实参的值 ,则需要使用指针参数
函数调用时是将实参值将复制到形参
指针适用于复杂数据类型作为参数 的函数中
常量与指针
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> int main () { int i = 0 ; const int * p1 = &i; int const * p2 = &i; int * const p3 = &i; const int * const p4 = &i; *p1 = 1 ; p1 = NULL ; *p2 = 2 ; p2 = NULL ; *p3 = 3 ; p3 = NULL ; *p4 = 4 ; p4 = NULL return 0 ; }
小结
指针是 C 语言中一种特别的变量
指针所保存的值是内存的地址
可以通过指针修改内存中的任意地址内容
指针的阅读技巧 下列标识符代表什么含义?
右左法则
从最里面 的圆括号中未定义的标识符 看起
首先往右看,在往左看
遇到圆括号 或方括号 时可以确定部分类型 ,并调转方向
重复2,3步骤,
解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int (*p1)(int *, int (*f)(int *));==> p1 为指针,指向函数,指向的函数有两个参数,第一个是 int *,第二个是个函数指针 f,指向的函数是 int *,返回值是 int ; p1 的返回值为 int 类型 ==> 函数指针,其中有参数为领一个函数指针 int (*p2[5 ])(int *);==> p2 为数组,有五个元素,五个元素的内容是指针,指向函数,函数类型为 int (int *) ==> 数组中存放的是函数指针 int (*(*p3)[5 ])(int *);==> p3 是指针,数组指针,指向的数组元素有五个,这五个元素为指针,是函数指针,指向的函数类型为 int (int *) ==> 指针指向了一个数组,数组中的五个元素为函数指针 int *(*(*p4)(int *))(int *);==> p4 为指针,函数指针,参数为 int * 返回值为指针,是函数指针,指向的函数类型为 int * (int *) ==> 一个函数指针的返回值是个函数指针 int (*(*p5)(int *))[5 ];==> p5 为指针,函数指针,参数为 int *, 返回值为指针,指向的数组类型为 int [5 ] ==> 一个函数指针的返回值是个数组 以最后一个为例可以简化为: typedef int (ArrayType)[5 ];typedef ArrayType* (FuncType)(int *)FuncType* p5;
可通过 typedef 简化复杂指针的定义
数组 数组就是相同类型的变量的有序集合
数组的大小
数组在一片连续的内存空间 中存储元素
数组元素的个数可以显示或隐式指定
1 2 int a[5 ] = {1 , 2 };int b[] = {1 , 2 };
数组大小与数组名
数组名 代表数组元素的首地址
数组的地址 需要用取地址符 & 才能得到
数组首元素的地址值与数组的地址值相同
数组首元素的地址与数组的地址是两个不同的概念
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int main () { int a[5 ] = {0 }; printf ("a = %p\n" , a); printf ("&a = %p\n" , &a); printf ("&a[0] = %p\n" , &a[0 ]); return 0 ; }
数组的地址值和数组第一个元素的地址值,从地址值上看是完全一致的,但其实他们所代表的意义是不相同的,不相同在他们所占用的空间长度是不一样的
在 C 语言中,数组的地址值 与数组第一个元素的地址值 从表面上看是相同的,即 &array
和 &array[0]
的值是相同的,都是数组首地址。
不同点在于:
&array
:代表整个数组的地址,类型是 T (*)[N]
(指向数组的指针,T
是数组元素的类型,N
是数组的大小)
&array[0]
:代表数组第一个元素的地址,类型是 T*
(指向数组元素的指针)
&array
(整个数组的地址):代表整个数组所占用的内存块,这个块的大小是 sizeof(array)
,也就是数组中所有元素的总大小。
&array[0]
(第一个元素的地址):仅仅代表数组中第一个元素的地址,它指向的内存块大小是 sizeof(array[0])
,也就是单个元素的大小。
例如,假设你有一个 int array[10]
,在 32 位系统上:
sizeof(array)
是 40 字节(假设每个 int
是 4 字节)。
sizeof(&array)
是指针的大小,通常为 4 字节(32 位系统上)。
sizeof(array[0])
是单个元素的大小,4 字节。
数组名的盲点
数组名可以看做一个常量指针
数组名”指向“的是内存中数组首元素的起始地址
数组名不包含数组的长度信息
在表达式中数组名只能作为右值使用
只有在下列场合中数组名不能 看作常量指针
数组名作为 sizeof 操作符的参数
数组名作为 & 运算符的参数
常量指针 :指针指向的值是常量,不能通过该指针修改指向的值,但指针可以指向其他变量。
指针常量 :指针本身是常量,不能改变指针指向的地址,但可以通过该指针修改所指向的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <stdio.h> int main () { int a[5 ] = {0 }; int b[2 ]; int * p = NULL ; p = a; printf ("a = %p\n" , a); printf ("p = %p\n" , p); printf ("&p = %p\n" , &p); printf ("sizeof(a) = %ld\n" , sizeof (a)); printf ("sizeof(p) = %ld\n" , sizeof (p)); p = b; printf ("a = %p\n" , b); printf ("p = %p\n" , p); printf ("&p = %p\n" , &p); printf ("sizeof(b) = %ld\n" , sizeof (b)); printf ("sizeof(p) = %ld\n" , sizeof (p)); b = a; return 0 ; }
小结
数组是一片连续的内存空间
数组的地址和数组首元素的地址意义不同
数组名在大多数情况下被当成常量指针 处理
数组名其实并不是指针 ,不能将其等同于指针
概念的混淆是 BUG 的根源之一!
数组和指针 在数组中 a + 1 的意义是什么?结果是什么?
指针运算的意义是什么?结果又是什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main () { int a[5 ] = { 0 }; int * p = NULL ; printf ("a = 0x%x\n" , (unsigned int )(a)); printf ("a + 1 = 0x%x\n" , (unsigned int )(a + 1 )); printf ("p = 0x%x\n" , (unsigned int )(p)); printf ("p + 1 = 0x%x\n" , (unsigned int )(p + 1 )); }
一、a 是数组元素的首地址,首地址 + 1,它却增加了 4,因为数组的中元素的大小是 4 个字节;
二、a 是数组元素的首地址,首地址进行操作,他得到的结果也是个地址(指针s)值;
指针是一种特殊的变量,与整数的运算规则为:
指针和指针之间的运算
指针之间只支持减法运算
参与减法运算的指针类型必须相同
规则为:
指针之间也可以进行比较运算
指针也可以进行关系运算(<、<=、>、>=)
指针关系运算的前提是同时指向同一个数组的元素
任意两个指针之间的*比较运算(==,!=)无限制
参与比较运算的指针类型必须相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <stdio.h> int main () { int arr[] = {10 , 20 , 30 , 40 , 50 }; int *p1 = &arr[1 ]; int *p2 = &arr[3 ]; if (p1 == p2) { printf ("p1 and p2 point to the same location.\n" ); } else { printf ("p1 and p2 point to different locations.\n" ); } if (p1 < p2) { printf ("p1 points to an earlier element in the array than p2.\n" ); } else { printf ("p1 points to a later element in the array than p2.\n" ); } return 0 ; }
指针之间的比较是比较二者在数组中的位置,比较的是二者指向的地址,而不是地址中的数据;
数组的访问方式
下标形式 vs 数组形式
指针以固定增量在数组中移动时,效率高于下标形式
指针增量为 1 且硬件具有硬件模型时,效率更高
下标形式与指针形式的转换
1 a[n] <--> *(a + n) <--> *(n + a) <--> n[a]
注意:
现代编译器的生成代码优化率已大大提高,在固定增量时,下标形式的效率已经和指针形式相当;
但从可读性和代码的维护角度来看,下标形式更优;
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <stdio.h> int main () { int a[5 ] = {0 }; int * p = a; int i = 0 ; for (i = 0 ; i < 5 ; i++) { p[i] = i + 1 ; } for (i = 0 ; i < 5 ; i++) { printf ("a[%d] = %d\n" , i, *(a + i)); } printf ("\n" ); for (i = 0 ; i < 5 ; i++) { i[a] = i + 10 ; } for (i = 0 ; i < 5 ; i++) { printf ("p[%d] = %d\n" , i, p[i]); } return 0 ; }
数组名是不是指针?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int a[] = {1 , 2 , 3 , 4 };#include <stdio.h> int main () { extern int a[]; printf ("&a = %p\n" , &a); printf ("a = %p\n" , a); printf ("*a = %d\n" , *a); return 0 ; } =================================== #include <stdio.h> int main() { extern int * a; printf ("&a = %p\n" , &a); printf ("a = %p\n" , a); printf ("*a = %d\n" , *a); return 0 ; }
数组名不是指针,只不过在某些情况下可以看作指针,或者当作指针来进行使用;
**&a
**:这行代码打印的是变量 a
本身的地址。
**a
**:这里的 a
是被 extern int* a;
声明为一个指针。然而,由于指针 a
没有被正确初始化,它的值是未定义的,可能是垃圾值或无效的内存地址。这个地址是一个无效的或随机的内存地址,可能指向了程序无法访问的区域。
***a
**:这里试图访问 a
指针指向的内存位置,并打印出该位置的值。但是,由于 a
指针指向的是无效的内存地址(如上例中的 0x200000001
),所以试图访问该地址时会引发段错误。
a 和 &a 的区别
a 为数组首元素的地址
&a 为整个数组的地址
a 和 &a 的区别在于指针运算
面试示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main () { int a[5 ] = {1 , 2 , 3 , 4 , 5 }; int * p1 = (int *)(&a + 1 ); int * p2 = (int *)((int )a + 1 ); int * p3 = (int *)(a + 1 ); printf ("%d\n" , p1[-1 ]); printf ("%d\n" , p2[0 ]); printf ("%d\n" , p3[3 ]); }
&a 是数组的地址,数组地址加一,这时候就超出了数组的地址范围了,指向了 5 这个元素右边界的位置;
a 是元素的首地址,转为一个整数,就是将一个八位的地址值截取为一个四位的整数;加一,那就是简单的整数加一,这时候再将它转为一个指针,就是将一个四位的整数转为一个八位的指针地址,那么这个地址就会变成一个指向未知数据的一个地址,这时候我们想想获取一个字节的内容,这时候就是一个未知状态的访问,那么也就报错了;
a 是元素的首地址,首地址加一,就是指向数组的下一个元素,也就是数据为 2 的未知
数组参数
1 2 void f (int a[]) <==> void f(int * a)void f(int a[5 ]) <==> void f(int * a)
注意:
在传递数组时,数组的大小信息是不会传递过去的,因此当定义的函数中有数组参数时,需要定义另一个参数来表示数组的大小
虚幻的数组参数
数组参数会退化为指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <stdio.h> void func1 (char a[5 ]) { printf ("In func1: sizeof(a) = %lu\n" , sizeof (a)); *a = 'a' ; a = NULL ; } void func2 (char b[]) { printf ("In func2 sizeof(b) = %lu\n" , sizeof (b)); *b = 'b' ; b = NULL ; } int main () { char array [10 ] = {0 }; func1(array ); printf ("array[0] = %c\n" , array [0 ]); func2(array ); printf ("array[0] = %c\n" , array [0 ]); return 0 ; }
从上面的输出中可以看出将数组作为函数参数时,他会退化为指针:
否则 输出的 sizeof 大小应该是数组的长度,而不是指针的长度,且 a = NULL 也会报错,因为数组名不可以作为左值使用;
小结
数组名和指针使用方式相同
数组名并不是数组的地址 ,而是数组首元素的地址
函数的数组参数退化为指针
数组指针和指针数组 1 2 3 4 int array [5 ];int matrix[3 ][3 ];int * pa = array ;int * pm = matrix;
array 代表数组名,即数组元素的首地址
那么 matrix 代表什么?
&array 表示数组的地址,虽然与 array 二者在值上是一致的,但二者意义是不同的,二者所代表的类型相同么?
数组类型
C 语言中的数组有自己的类型
数组的类型由元素类型 和数组大小 共同决定
1 2 int array [5 ] 的类型为 int [5 ]
定义数组类型
1 2 3 4 5 6 7 8 9 10 typedef type (name) [size];typedef int (AINT5) [5];typedef float (AFLOAT10) [10];AINT5 iArray; AFLOAT10 fArray;
typedef 是重命名关键字,在 type(name)[size]; 表示要对数组重命名,重命名为 name;
AINT5 iArray; 中 iArray 就是个数组类型,元素类型为 int,数组长度为 5;
数组指针 数组指针是个指针用于指向一个数组(不是指向数组的首元素)
数组指针用于指向一个数组
数组名是数组首元素的起始地址 ,但并不是数组的起始地址
通过将取地址符 & 作用于数组名可以得到数组的起始地址
可通过数组类型定义数组的指针 :ArrayType* pointer;
也可以直接定义:type(*pointer)[n];
pointer 为数组指针变量名
type 为指向的数组中元素的类型
n 为指向的数组的大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include "stdio.h" typedef int (AINT5) [5];typedef float (AFLOAT10) [10];typedef char (ACHAR9) [9];int main () { AINT5 al; float fArray[10 ]; AFLOAT10* pf = &fArray; ACHAR9 cArray; char (*pc)[9 ] = &cArray; char (*pcw)[4 ] = cArray; int i = 0 ; printf ("%lu, %lu\n" , sizeof (AINT5), sizeof (al)); for (i = 0 ; i < 10 ; i++) { (*pf)[i] = i; } for (i = 0 ; i < 10 ; i++) { printf ("%f\n" , fArray[i]); } printf ("%p, %p, %p\n" , &cArray, pc + 1 , pcw + 1 ); } ======================================================= #include "stdio.h" typedef char (ACHAR9)[9 ];int main () { ACHAR9 cArray; char (*pc)[9 ] = &cArray; int a[] = {1 , 2 , 3 , 4 , 5 }; int * pa = (int *)(&a + 1 ); printf ("%p, %p, %p\n" , &a, (&a + 1 ), pa); printf ("%p, %p\n" , &cArray, pc + 1 ); }
pc 是个变量,指针指向的内容为 &cArray;pc + 1 也就是 &cArray 的地址 + 他数据类型的长短小,而 &cArray 的类型为 ACHAR9 它占了 9 个字节的大小;所以 pc + 1 在地址中的实际偏移量为 9;0x7ffc5f3943f8 = 0x7ffc5f3943ef + 9;
同理 pcw + 1 的地址 0x7ffc5f3943f3 = 0x7ffc5f3943ef + 4;
指针数组
指针数组是一个普通的数组
指针数组中每一个元素为一个指针
指针数组的定义:type* pArray[n];
小结
数组的类型由元素类型 和数组大小 共同决定
数组指针是一个指针 ,指向对应类型的数组
指针数组是一个数组 ,其中每个元素为指针
数组指针遵循 指针运算法则
指针数组拥有 C 语言数组的各种特性
多维数组与多维指针 指向指针的指针
指针的本质是变量
指针会占用 一定的内存空间
可以定义指针的指针 来保存指针变量的地址
1 2 3 4 5 6 7 8 9 10 11 int main () { int i = 0 ; int * p = NULL ; int ** pp = NULL ; pp = &p; *pp = &i; return 0 }
为什么需要指向指针的指针?
指针在本质上也是变量
对于指针 也同样存在传值调用 与传址调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <stdio.h> #include <malloc.h> int reset (char ** p, int size, int new_size) { int ret = 1 ; int i = 0 ; int len = 0 ; char * pt = NULL ; char * tmp = NULL ; char * pp = *p; if (p != NULL ) && (new_size > 0 ) { pt = (char *)malloc (new_size); tmp = pt; len = (size < new_size)? size : new_size; for (i = 0 ; i < len; i++) { *tmp++ = *pp++ } free (*p); *p = pt; }else { ret = 0 } return ret; } int main () { char * p = (char *)malloc (5 ); printf ("%p\n" , p); if (reset(&p, 5 , 3 )) { printf ("%p\n" , p); } free (p); return 0 ; }
二维数组与二级指针
二维数组在内存中以一维的方式排布
二维数组中的第一维四一维数组
二维数组中的第二维才是具体的值
二维数组的数组名可看做常量指针
二维数组是一个一维数组,其中的值是另一个一维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <stdio.h> int printArray (int a[], int size) { int i = 0 ; printf ("printArray: %ld\n" , sizeof (a)); for (i = 0 ; i < size; i++) { printf ("%d\n" , a[i]); } } int main () { int a[3 ][3 ] = {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 }; int * p = &a[0 ][0 ]; int i = 0 ; int j = 0 ; for (i = 0 ; i < 3 ; i++) { for (j = 0 ; j < 3 ; j++) { printf ("%d\n, " , *(*(a + i) + j)); } printf ("\n" ); } printf ("\n" ); printArray(p, 9 ); return 0 ; }
在 C 语言中,*(*(a + i) + j)
和 a[i][j]
之间的等价关系可以通过以下步骤理解:
数组名作为指针 :a
是一个指向数组的指针,a + i
计算的是指向第 i
行的指针。
解引用 :*(a + i)
取得第 i
行的首地址,实际上是一个指向该行一维数组的指针。
偏移量 :*(a + i) + j
计算的是第 i
行中第 j
列的地址。
解引用 :最后,通过 *(*(a + i) + j)
解引用这个地址,得到了 a[i][j]
的值。
因此,*(*(a + i) + j)
实际上是先获取第 i
行的指针,然后再访问该行的第 j
列的元素,这与 a[i][j]
的操作是等价的。
数组名
1 2 3 4 int a[5 ],a 的类型为 int *int m[2 ][5 ],m 的类型为 int (*)[5 ]
二维数组名可以看作是指向数组的常量指针
二维数组可以看作是一维数组
二维数组中的每个元素都是同类型的一维数组
如何动态的申请二维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <malloc.h> int ** malloc2d (int row, int col) { int ** ret = NULL ; if ((row >0 && (col > 0 ))) { int * p = NULL ; ret = (int **)malloc (row * sizeof (int *)); p = (int *)malloc (row * col); if ((ret != NULL ) && (p != NULL )) { int i = 0 ; for (i = 0 ; i < row; i++) { ret[i] = p + i * col; } }else { free (ret); free (p); ret = NULL ; } } return ret; } void free2d (int ** p) { if (*p != NULL ) { free (*p); } free (p); } int main () { int ** a = malloc2d(3 , 3 ); int i = 0 ; int j = 0 ; for (i = 0 ; i < 3 ; i++) { for (j = 0 ; j < 3 ; j++) { printf ("%d, " , a[i][j]); } printf ("\n" ); } free2d(a); return 0 ; }
malloc 这个函数不保证申请出来的空间所有值都为 0;
小结
C 语言中只支持 一维数组
C 语言中的数组大小必须在编译期就作为常数确定
C 语言中的数组元素可以是任何类型的数据
C 语言中数组的元素可以是另一个数组
数组参数和指针参数 数组作为函数参数时会退化为指针;
为什么 C 语言中的数组参数会退化为指针?
退化的意义:
C 语言中只会以值拷贝的方式 传递参数
当向函数传递数组时:
将整个数组拷贝一份传入函数(错误的,会造成空间上的浪费)
将数组名看作常量指针传递数组元素的首地址(正确的,显著的提高效率不会造成空间上的浪费)
C 语言以高效作为最初的设计目标:
a) 参数传递的时候如果拷贝整个数组执行效率将大大降低
b) 参数位于栈上,太大的数组拷贝将导致栈溢出
二维数组参数
二维数组参数同样存在退化 的问题
二维数组可以看作 是一维数组
二维数组中的每个元素都是一维数组
二维数组参数中第一维的参数可以省略
1 2 void f (int a[5 ]) <--> void f (int a[]) <--> void f (int * a) void g (int a[3 ][3 ]) <--> void g (int a[][3 ]) <--> void g (int (*a)[3 ])
一维数组 退化为 int*a,int 是数组中元素的类型
二维数组退化为 int(*a)[5],int[5] 是二维数组中元素的类型
可见,一维数组退化为指针,二维数组退化为数组指针
等价关系
注意
C 语言中无法 向一个函数传递任意的多维数组 (接收二维参数,你就只能传递二维的)
必须提供除第一维之外的所有维度的长度
第一维之外的维度信息用于完成指针运算
N 维数组的本质是一维数组 ,元素是 N-1 维的数组
对于多维数组的函数参数只有第一维是可变的 (其他的是参数类型不可变,这话好像有问题?)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <stdio.h> void access (int a[][3 ], int row) { int col = sizeof (*a) / sizeof (int ); int j = 0 ; int i = 0 ; printf ("sizeof(a) = %ld\n" , sizeof (a)); printf ("sizeof(*a) = %ld\n" , sizeof (*a)); for (i = 0 ; i < row; i++) { for (j = 0 ; j < col; j++) { printf ("%d\n" , a[i][j]); } } } void access_ex (int b[][2 ][3 ], int n) { int i = 0 ; int j = 0 ; int k = 0 ; printf ("sizeof(b) = %ld\n" , sizeof (b)); printf ("sizeof(*b) = %ld\n" , sizeof (*b)); for (i = 0 ; i < n; i++) { for (j = 0 ; j < 2 ; j++) { for (k = 0 ; k < 3 ; k++) { printf ("%d\n" , b[i][j][k]); } } } } int main () { int a[3 ][3 ] = {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 }; int aa[2 ][2 ] = {0 }; int b[1 ][2 ][3 ] = {0 }; access(a, 3 ); access(aa, 3 ); access_ex(b, 1 ); access_ex(aa, 1 ); return 0 ; }
小结
C 语言中只会以值拷贝的方式 传递参数
C 语言中的数组参数必然退化为指针
多维数组必须提供除第一维之外的所有维的长度
对于多维数组的函数参数只有第一维是可变的