网站开发设计哪家好,做网站6个月心得,和县建设局网站,做书一般在哪个网站下载素材C语言中指针和数组的关系似乎很“纠结”#xff0c;让人爱恨交织。本文试图帮助读者理清它们之间的复杂关系#xff01;
数组名的理解
数组元素在内存中是连续存放的#xff0c;在C语言中#xff0c;数组名有特殊的含义#xff0c;它表示数组首元素的地址。因此#xf…C语言中指针和数组的关系似乎很“纠结”让人爱恨交织。本文试图帮助读者理清它们之间的复杂关系
数组名的理解
数组元素在内存中是连续存放的在C语言中数组名有特殊的含义它表示数组首元素的地址。因此数组元素既可以用下标来访问也可以用指针来访问
#includestdio.h
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,0 };int i 4;//访问数组下标为4的元素printf(%d\n, arr[i]);printf(%d\n, *(arr i));return 0;
}上面代码中arr[i]和*(arr i)的效果是等价的都是访问数组下标为i的元素。
arr i表示arr数组下标为i的元素的地址而*(arr i)表示对arr数组下标为i的地址进行解引用操作
但是也有两个例外
sizeof(数组名)sizeof中单独放数组名那这个数组名表示的是整个数组所以计算的是整个数组的大小单位是字节
数组名这里的数组名也表示的是整个数组所以数组名取出的是整个数组的地址。要注意的是整个数组的地址和数组首元素的地址在数值上是相同的但它们是有区别的只是因为它们的起始空间是一样的而取地址时取出的是空间中地址较小的地址而已
那整个数组的地址数组名和数组首元素的地址数组名到底有什么区别呢
#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[0]1 %p\n, arr[0]1);printf(arr %p\n, arr);printf(arr1 %p\n, arr1);printf(arr %p\n, arr);printf(arr1 %p\n, arr1);return 0;
}
这段代码的运行结果为 arr[0]和arr都是数组首元素int类型的地址所以对它们加1的结果是向后跳过了一个整型即4个字节但arr表示的是整个数组取出的是整个数组的地址所以对它加1的结果是跳过了整个数组的大小即40个字节
这是因为整个数组的地址和数组首元素的地址的类型是不同的而指针的类型决定了对指针进行解引用时候的权限
使用指针访问数组
由于在上一篇文章中已经弄清楚了指针的一些基本用法了这里不在过多阐述对于使用指针访问数组其实在讲数组名时已经提到了这里直接上代码
#includestdio.h
int main()
{int arr[5] { 0 };int i 0;//输入for (i 0; i 5; i){scanf(%d, arr i);}//输出for (i 0; i 5; i){printf(%d , *(arr i));}printf(\n);return 0;
}
运行结果 数组元素之所以能通过这种方法来引用是因为数组元素的访问在编译器处理的时候也是转换成⾸元素的地址偏移量求出元素的地址然后解引用来访问的。所以在输入操作时arr i等价于arr[i]表示取数组arr的第i 1个元素的地址在输出操作时*(arr i)等价于arr[i]表示引用数组首地址所指元素后第i个元素
上面的代码中若把arr赋值给一个整型指针然后通过这个整型指针来访问数组和直接用数组名访问结果是一样的
一维数组传参的本质
在没有学指针之前数组传参传递的是数组名函数的形参部分也用数组来接收但是到这里我们已经知道了数组名是数组首元素的地址那么在数组传参的时候传数组名其实本质上传递的是数组首元素的地址
其实一维数组做函数形参时因为它只起到接收数组起始地址的作用所以会发生数组类型到指针类型的隐式转换即使将形参声明为一维数组他也将退化为指针系统仅仅为其分配指针所占的内存空间并不为形参数组分配额外的存储空间而是让形参数组共享实参数组所占的存储空间。
因此用一维数组作函数形参与用指针变量作函数形参本质上是一样的因为它们接收的都是数组的起始地址都需按此地址对主调函数中的实参数组元素进行间接寻址因此在被调函数中既能以下表形式也能以指针形式来访问数组元素
可以看看下面这段代码的运行结果
void test1(int arr[])//参数写成数组形式本质上还是指针
{printf(%d\n, sizeof(arr));
}
void test2(int* arr)//参数写成指针形式
{printf(%d\n, sizeof(arr));//计算⼀个指针变量的⼤⼩
}
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };test1(arr);test2(arr);return 0;
}
运行结果 因为数组传参的时候传数组名其实本质上传递的是数组首元素的地址所以用sizeof计算的就是一个地址变量的大小在64位机器下就是8个字节
总结
数组传参的时候传数组名其实本质上传递的是数组首元素的地址
⼀维数组传参形参的部分可以写成数组的形式也可以写成指针的形式
写成数组的形式便于理解
但即使写成数组的形式本质上还是指针
二级指针
只要是变量就有地址指针变量也是变量也有它对应的地址那么能不能用指针来存放一个指针变量的地址呢
答案是可以的而且存放指针变量的地址用的就是二级指针其实前面我们所说的存放变量如整型变量、字符型变量、结构体变量、数组或数组元素的指针都是一级指针
总结一下就是二级指针是用来存放一级指针变量的地址的以此内推存放二级指针
直接上代码
#includestdio.h
int main()
{int n 5;printf(%d\n, n);int* pn n;printf(%d\n, *pn);int** ppn pn;printf(%d\n, **ppn);return 0;
}
运行结果 这段代码中n是整型变量pn是一级指针变量用来存放变量n的地址类型是int *类型ppn是二级指针变量用来存放pn的地址类型是int **类型比一级指针变量的类型多一个*
其中int* *中的int*表示ppn指向的pn的类型是int*最后一个*表示ppn是指针变量
n、pn、ppn三者之间的关系n等价于*pn也等价于**ppnpn等价于*ppn即可以通过*pn或者**ppn来访问变量n也可以通过*ppn来访问pn变量 要注意的是二级指针和二维数组之间是没有对应关系的
指针数组
首先要搞清楚的是指针数组是数组是用来存放指针的数组即数组的每个元素都是指针类型的
例如char* arr[5];这句代码中arr数组就是存放字符指针的数组
指针数组模拟二维数组
这里我们用指针数组模拟二维数组的使用来打印二维数组的每个元素
#includestdio.h
int main()
{//定义一个3行5列的二维数组int arr[3][5] { {1,2,3,4,5},{3,4,5,6,7},{5,6,7,8,9} };//打印二维数组中的每个元素printf(打印二维数组中的每个元素\n);int i 0;for (i 0; i 3; i){int j 0;for (j 0; j 5; j){printf(%d , arr[i][j]);}printf(\n);}//用指针数组模拟二维数组的使用场景int arr1[5] { 1,2,3,4,5 };int arr2[5] { 3,4,5,6,7 };int arr3[5] { 5,6,7,8,9 };int* parr[3] { arr1,arr2,arr3 };printf(用指针数组模拟二维数组的使用场景\n);for (i 0; i 3; i){int j 0;for (j 0; j 5; j){printf(%d , parr[i][j]);}printf(\n);}return 0;
}
运行结果 首先要补充的一点是二维数组是一维数组的数组即在二维数组arr中arr[i]就是arr数组第i行的数组名。
因为parr数组中的元素是整型类型的一维数组的数组名而数组名相当于数组首元素的地址即int*类型所以parr数组就是用来存放int*类型指针的数组类型也就是int*类型的。
这里用parr打印数组元素和上面二维数组的打印是一模一样的parr[i]是访问parr数组下标为i的元素parr[i]找到的数组元素指向了一个整型的一维数组而parr[i][j]找到的就是整型一维数组中的元素。
上述的代码模拟出二维数组的效果实际上并非完全是二维数组因为每一行并非是连续的。
其实用来打印parr数组的代码parr[i][j]也可以写成下面这种形式
*(*(parr i) j)
parr[i]相当于*(parr i)表示对parr数组下标为i的元素解引用操作相当于找到了parr中数组名所指向的那个一维数组而parr[i][j]就相当于*(*(parr i) j)表示对parr数组第i行第j个元素地址解引用操作找到的就是parr数组中第i个元素数组名所指向的那个一维数组中第j个元素