seo网站概述,重庆提供行业网站建站报价,网站策划书,智慧团建网站几点关闭C语言指针详解-包过系列#xff08;二#xff09;目录版 1、数组名的深入理解1.1、数组名的本质1.2、数组名本质的两个例外1.2.1、sizeof#xff08;数组名#xff09;1.2.2、数组名 2、使用指针访问数组3、一维数组传参本质4、二级指针4.1、二级指针介绍4.2、二级指针… C语言指针详解-包过系列二目录版 1、数组名的深入理解1.1、数组名的本质1.2、数组名本质的两个例外1.2.1、sizeof数组名1.2.2、数组名 2、使用指针访问数组3、一维数组传参本质4、二级指针4.1、二级指针介绍4.2、二级指针运算 5、指针数组模拟实现二维数组 1、数组名的深入理解
1.1、数组名的本质
数组名的本质其实就是数组首元素的地址。我们以下面的代码测试为例
#includestdio.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;
}可见二者一模一样故验证了数组名即为数组首元素地址。 但是可能会有读者提出以下质疑
#includestdio.h
int main()
{int arr[10] {1,2,3,4,5,6,7,8,9,10};printf(%d, sizeof(arr));return 0;
}若数组名 arr 表示的是数组首元素的地址的话那么输出结果应当为一个地址的大小也就是4/8字节。这就涉及到了数组名是数组首元素地址的两个例外了请见 1.2 。
1.2、数组名本质的两个例外
1.2.1、sizeof数组名
在关键字 sizeof 中单独放入数组名时这个数组名表示的是整个数组计算的是整个数组的大小单位是字节。如上 1.1 中所示。 注意只有是单独放入数组名时数组名才表示整个数组其余情况下均是计算数据类型大小。 如下面代码所示
#includestdio.h
int main()
{int arr[10] {1,2,3,4,5,6,7,8,9,10};printf(%d, sizeof(arr 1));//此时 arr 就又恢复成数组首元素的地址了return 0;
}在 x86 环境下的运行结果 在 x64 环境下的运行结果: 如上所示当sizeof内部非单独放置数组名时sizeof 计算的就是对应运行环境的地址的大小。
1.2.2、数组名
数组名 这里的数组名表示的是整个数组取出的是整个数组的地址注意整个数组的地址与数组首元素地址是有区别的 我们通过下面代码的演示使大家对此有更加清晰的认识。
#includestdio.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 );printf(arr %p\n, arr );return 0;
}我们发现三个输出结果完全一样。不急我们通过对地址进行加减整数运算来使他们原形毕露。演示如下
#includestdio.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(arr 1 %p\n, arr 1);printf(arr %p\n, arr);printf(arr 1 %p\n, arr 1);return 0;
}通过上述演示我们发现在 x86 环境下arr[0] 和 arr[0] 1 之间相差4字节arr 与 arr 1 也是相差4字节。这是因为二者均表示首元素的地址 1 也就是跳过一个元素。 但是 arr 和 arr 1 则相差40个字节这就是因为 arr 表示的是整个数组的地址 1 操作是跳过整个数组的。
2、使用指针访问数组
由上文我们可知数组名表示数组首元素的地址那么数组名加整数就可以访问首元素后面的元素。 请看下面代码
#includestdio.h
int main()
{int arr[10] { 0 };int i 0;size_t sz sizeof(arr) / sizeof(arr[0]);int* parr arr;//将数组首元素地址存储到指针 parr 中//输入for (i 0; i sz; i){scanf(%d, parr i);//遍历数组地址//注意这里parri就已经表示数组对应下标元素的地址了parri表示的就是指针变量parr的地址加减整数了。//scanf(%d, arr i);也可以这样写}//输出for (i 0; i sz; i){printf(parr[%d] %d\n, i, *(parr i));//输出数组元素}return 0;
}在这里我们补充一点数组 arr[ i ] 在编译前系统会将其转化为 *arr i 然后再进行编译。 由上所述我们进一步分析数组名 arr 是首元素地址可以赋值给 parr 其实数组名 arr 和 parr 在这里是等价的。既然我们可以使用 arr[i] 访问数组那 parr[i]也应该能访问数组。如下演示
#includestdio.h
int main()
{int arr[10] { 0 };int i 0;size_t sz sizeof(arr) / sizeof(arr[0]);int* parr arr;//将数组首元素地址存储到指针 parr 中//输入for (i 0; i sz; i){scanf(%d, parr i);}//输出for (i 0; i sz; i){printf(parr[%d] %d\n, i, parr[i]);//等价于arr[i]}return 0;
}综上我们可以总结出以下四点 1、数组就是数组是一块连续的空间与数组大小数组元素个数数组元素类型都有关系 2、指针变量就是指针变量是一个变量4/8bytes 3、数组名除两个特例外表示的都是数组首元素的地址 4、可以使用指针来访问数组数组在系统编译时转化的内容就是指针。
3、一维数组传参本质
在之前函数章节我们已经讲过数组可以作为参数传递给函数在本节我们讲述一下数组传参本质。 我们先用一个问题来作引我们之前在函数外部计算过数组元素的个数在本节我们不妨尝试在函数内部计算求数组元素个数。
#includestdio.hvoid test(int arr[])
{size_t sz2 sizeof(arr) / sizeof(arr[0]);printf(sz2 %d\n, sz2);
}int main()
{int arr[] { 1,2,3,4,5,6,7,8,9,10 };size_t sz1 sizeof(arr) / sizeof(arr[0]);printf(sz1 %d\n, sz1);test(arr);return 0;
}在 x86 环境下的结果 我们可见 sz2 的值并不等于 sz1 。这就涉及到数组传参的本质了。数组传参的时候传递的是数组名也就是数组首元素的地址。 所以函数形参部分理论上应当使用指针来接收数组首元素地址。故在函数内部我们所写的sizeof(arr)计算的其实是地址的大小在 x86 环境下就是4字节。sizeof(arr[0])计算的是数组首元素的大小即int类型大小为4字节。故 sz2 的结果为1. 补充在x64环境下的运行结果
sizeof(arr)/sizeof(arr[0]) 8/4 2在 x64 环境下地址的大小为8字节。
#includestdio.hvoid test(int* arr)
{printf(%d\n, sizeof(arr));//计算一个指针变量的大小
}int main()
{int arr[] { 1,2,3,4,5,6,7,8,9,10 };size_t sz1 sizeof(arr) / sizeof(arr[0]);//计算数组内元素个数多少。printf(sz1 %d\n, sz1);test(arr);return 0;
}综上所述我们总结出以下两点 1、数组传参的本质是传递了数组首元素的地址所以形参访问的数组和实参的数组是同一个数组。故在形参数组中更改数组内容也会更改实参数组中的内容传址调用由此区别于传值调用。 2、形参的数组是不会再单独创建空间所以形参的数组是可以省略数组大小的。
4、二级指针
4.1、二级指针介绍
指针变量也是变量是变量就有地址那指针变量的地址存放于何处这就涉及到二级指针。
#includestdio.h
int main()
{int a 0;int* p a;//一级指针p存储a的地址int** pp p;//二级指针存储p的地址printf(p %p\n, p);printf(pp %p\n, pp);return 0;
}4.2、二级指针运算
1、*pp 通过对 pp 中地址的解引用如此找到 p *pp访问的就是 p
#includestdio.h
int main()
{int a 0;int b 10;int* p a;//一级指针p存储a的地址int** pp p;//二级指针存储p的地址*pp b;//通过解引用更改一级指针 p 所指向的对象printf(%d, *p);return 0;
}2、**pp 先通过*pp 找到 p 然后再通过对 p 解引用找到 a。
#includestdio.h
int main()
{int a 0;int* p a;//一级指针p存储a的地址int** pp p;//二级指针存储p的地址**pp 20;//通过两次解引用更改一级指针 p 所指向的对象的内容。//等价于*p 20//等价于 a 20printf(%d, a);return 0;
}5、指针数组模拟实现二维数组
我们知道有整型数组字符型数组浮点型数组等由此可知指针数组就是存储的元素是指针的数组。指针数组的每个元素又指向一块区域。 我们用指针数组模拟二维数组为例 首先先编写好3个一维数组
#includestdio.h
int main()
{int arr1[] { 1,2,3,4,5 };int arr2[] { 6,7,8,9,10 };int arr3[] { 11,12,13,14,15 };return 0;
}然后来设置指针数组让指针数组内存储一维数组的地址由上文知就是数组名。
#includestdio.h
int main()
{int arr1[] { 1,2,3,4,5 };int arr2[] { 6,7,8,9,10 };int arr3[] { 11,12,13,14,15 };int* arr[] { arr1 , arr2 , arr3 };//编写指针数组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);}return 0;
}上述代码只是模拟出了二维数组的效果但并不是完全等于二维数组因为上面模拟出的数组的每一行并不是连续的。
全文至此结束 写作不易不知各位老板能否给个一键三连或是一个免费的赞呢(-▽-)(-▽-)这将是对我最大的肯定与支持谢谢(-▽-)(-▽-)