1. 数组名的理解2. 使用指针访问数组3. ⼀维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟⼆维数组1. 数组名的理解int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p arr[0];看上述代码这里我们用arr[0]来得到数组首元素地址但是数组名arr其实就是首元素地址不信我们来做个测试。#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; }运行结果如下我们发现arr[0]和arr的地址相同这就说明了数组名就是其首元素地址。再来看下面一段代码#include stdio.h int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; printf(%d\n, sizeof(arr)); return 0; }如果根据上面的理解我们会认为这里sizeof计算的是其首元素字节数及结果是4或832位环境是464位环境是8但是当我们运行时就会发现结果是40及整个数组的字节个数。其实数组名就是数组首元素(第⼀个元素)的地址是对的但是有两个例外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] 0077F820 arr[0]1 0077F824 arr 0077F820 arr1 0077F824 arr 0077F820 arr1 0077F848这里我们发现arr[0]和arr[0]1相差4个字节arr和arr1相差4个字节是因为arr[0] 和 arr 都是首元素的地址1就是跳过⼀个元素。但是arr和arr1相差40个字节这就是因为arr是数组的地址1 操作是跳过整个数组的。2. 使用指针访问数组#include stdio.h int main() { int arr[10] {0}; //输⼊ int i 0; int sz sizeof(arr)/sizeof(arr[0]); //输⼊ int* p arr; for(i0; isz; i) { scanf(%d, pi); //scanf(%d, arri);//也可以这样写 } //输出 for(i0; isz; i) { printf(%d , *(pi)); } return 0; }关键知识点1. 数组名的本质arr 作为函数内表达式代表首元素地址类型为 int* 。2. 指针运算p i 会根据 int 的大小4字节自动偏移每次1偏移4字节正好指向下一个 int 元素。其实数组名arr和p在这里是等价的。那我们可以使用arr[i]可以访问数组的元素那p[i]是否也可以访问数组呢#include stdio.h int main() { int arr[10] {0}; //输⼊ int i 0; int sz sizeof(arr)/sizeof(arr[0]); //输⼊ int* p arr; for(i0; isz; i) { scanf(%d, pi); //scanf(%d, arri);//也可以这样写 } //输出 for(i0; isz; i) { printf(%d , p[i]); } return 0; }在第18行的地方将*(pi)换成p[i]也是能够正常打印的所以本质上p[i] 是等价于 *(pi)。同理arr[i] 应该等价于 *(arri)数组元素的访问在编译器处理的时候也是转换成首元素的地址偏移量求出元素的地址然后解引用来访问的。3. ⼀维数组传参的本质首先从⼀个问题开始我们之前都是在函数外部计算数组的元素个数那我们可以把数组传给⼀个函数后函数内部求数组的元素个数吗#include stdio.h void test(int 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}; int sz1 sizeof(arr)/sizeof(arr[0]); printf(sz1 %d\n, sz1); test(arr); return 0; }运行结果我们发现在函数内部是没有正确获得数组的元素个数。数组传参的时候传递的是数组名也就是说本质上数组传参传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写sizeof(arr)计算的是⼀个地址的大小单位字节而不是数组的大小单位字节。正是因为函数的参数部分是本质是指针所以在函数内部是没办法求的数组元素个数的。总结⼀维数组传参形参的部分可以写成数组的形式也可以写成指针的形式。4.冒泡排序冒泡排序的核心思想就是两两相邻的元素进行比较。//⽅法1 void bubble_sort(int arr[], int sz)//参数接收数组元素个数 { int i 0; for(i 0; i sz-1; i) { int j 0; for(j 0; j sz-i-1; j) { if(arr[j] arr[j1]) { int tmp arr[j]; arr[j] arr[j1]; arr[j1] tmp; } } } } int main() { int arr[] {3,1,7,5,8,9,0,2,4,6}; int sz sizeof(arr)/sizeof(arr[0]); bubble_sort(arr, sz); int i 0; for(i 0; i sz; i) { printf(%d , arr[i]); } return 0; } //⽅法2 - 优化 void bubble_sort(int arr[], int sz)//参数接收数组元素个数 { int i 0; for(i 0; i sz-1; i) { int flag 1;//假设这⼀趟已经有序了 int j 0; for(j 0; j sz-i-1; j) { if(arr[j] arr[j1]) { flag 0;//发⽣交换就说明⽆序 int tmp arr[j]; arr[j] arr[j1]; arr[j1] tmp; } } if(flag 1)//这⼀趟没交换就说明已经有序后续⽆需排序了 break; } } int main() { int arr[] {3,1,7,5,8,9,0,2,4,6}; int sz sizeof(arr)/sizeof(arr[0]); bubble_sort(arr, sz); int i 0; for(i 0; i sz; i) { printf(%d , arr[i]); } return 0; }根据上面的代码我们可以很清晰的了解与认识冒泡排序。5. 二级指针指针变量也是变量是变量就有地址那指针变量的地址存放在哪里这就是二级指针。对于⼆级指针的运算有*ppa通过对ppa中的地址进⾏解引用这样找到的是pa*ppa其实访问的就是pa 。int b 20; *ppa b;//等价于 pa b;**ppa先通过*ppa找到pa,然后对pa进行解引用操作*pa那找到的是a 。**ppa 30; //等价于*pa 30; //等价于a 30;6. 指针数组指针数组是指针还是数组我们类比⼀下整型数组是存放整型的数组字符数组是存放字符的数组。那么指针数组就是存放指针的数组。指针数组的每个元素都是用来存放地址指针的。如下图7. 指针数组模拟二维数组#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数组中 int* parr[3] {arr1, arr2, arr3}; int i 0; int j 0; for(i 0; i 3; i) { for(j 0; j 5; j) { printf(%d , parr[i][j]); } printf(\n); } return 0; }parr数组的画图演⽰parr[i]是访问parr数组的元素parr[i]找到的数组元素指向了整型⼀维数组parr[i][j]就是整型⼀维数组中的元素。上述的代码模拟出二维数组的效果实际上并非完全是⼆维数组因为每⼀行并非是连续的。
C语言指针精讲(三)∶数组名与指针访问,传参与冒泡排序
发布时间:2026/6/1 6:25:05
1. 数组名的理解2. 使用指针访问数组3. ⼀维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟⼆维数组1. 数组名的理解int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p arr[0];看上述代码这里我们用arr[0]来得到数组首元素地址但是数组名arr其实就是首元素地址不信我们来做个测试。#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; }运行结果如下我们发现arr[0]和arr的地址相同这就说明了数组名就是其首元素地址。再来看下面一段代码#include stdio.h int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; printf(%d\n, sizeof(arr)); return 0; }如果根据上面的理解我们会认为这里sizeof计算的是其首元素字节数及结果是4或832位环境是464位环境是8但是当我们运行时就会发现结果是40及整个数组的字节个数。其实数组名就是数组首元素(第⼀个元素)的地址是对的但是有两个例外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] 0077F820 arr[0]1 0077F824 arr 0077F820 arr1 0077F824 arr 0077F820 arr1 0077F848这里我们发现arr[0]和arr[0]1相差4个字节arr和arr1相差4个字节是因为arr[0] 和 arr 都是首元素的地址1就是跳过⼀个元素。但是arr和arr1相差40个字节这就是因为arr是数组的地址1 操作是跳过整个数组的。2. 使用指针访问数组#include stdio.h int main() { int arr[10] {0}; //输⼊ int i 0; int sz sizeof(arr)/sizeof(arr[0]); //输⼊ int* p arr; for(i0; isz; i) { scanf(%d, pi); //scanf(%d, arri);//也可以这样写 } //输出 for(i0; isz; i) { printf(%d , *(pi)); } return 0; }关键知识点1. 数组名的本质arr 作为函数内表达式代表首元素地址类型为 int* 。2. 指针运算p i 会根据 int 的大小4字节自动偏移每次1偏移4字节正好指向下一个 int 元素。其实数组名arr和p在这里是等价的。那我们可以使用arr[i]可以访问数组的元素那p[i]是否也可以访问数组呢#include stdio.h int main() { int arr[10] {0}; //输⼊ int i 0; int sz sizeof(arr)/sizeof(arr[0]); //输⼊ int* p arr; for(i0; isz; i) { scanf(%d, pi); //scanf(%d, arri);//也可以这样写 } //输出 for(i0; isz; i) { printf(%d , p[i]); } return 0; }在第18行的地方将*(pi)换成p[i]也是能够正常打印的所以本质上p[i] 是等价于 *(pi)。同理arr[i] 应该等价于 *(arri)数组元素的访问在编译器处理的时候也是转换成首元素的地址偏移量求出元素的地址然后解引用来访问的。3. ⼀维数组传参的本质首先从⼀个问题开始我们之前都是在函数外部计算数组的元素个数那我们可以把数组传给⼀个函数后函数内部求数组的元素个数吗#include stdio.h void test(int 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}; int sz1 sizeof(arr)/sizeof(arr[0]); printf(sz1 %d\n, sz1); test(arr); return 0; }运行结果我们发现在函数内部是没有正确获得数组的元素个数。数组传参的时候传递的是数组名也就是说本质上数组传参传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。那么在函数内部我们写sizeof(arr)计算的是⼀个地址的大小单位字节而不是数组的大小单位字节。正是因为函数的参数部分是本质是指针所以在函数内部是没办法求的数组元素个数的。总结⼀维数组传参形参的部分可以写成数组的形式也可以写成指针的形式。4.冒泡排序冒泡排序的核心思想就是两两相邻的元素进行比较。//⽅法1 void bubble_sort(int arr[], int sz)//参数接收数组元素个数 { int i 0; for(i 0; i sz-1; i) { int j 0; for(j 0; j sz-i-1; j) { if(arr[j] arr[j1]) { int tmp arr[j]; arr[j] arr[j1]; arr[j1] tmp; } } } } int main() { int arr[] {3,1,7,5,8,9,0,2,4,6}; int sz sizeof(arr)/sizeof(arr[0]); bubble_sort(arr, sz); int i 0; for(i 0; i sz; i) { printf(%d , arr[i]); } return 0; } //⽅法2 - 优化 void bubble_sort(int arr[], int sz)//参数接收数组元素个数 { int i 0; for(i 0; i sz-1; i) { int flag 1;//假设这⼀趟已经有序了 int j 0; for(j 0; j sz-i-1; j) { if(arr[j] arr[j1]) { flag 0;//发⽣交换就说明⽆序 int tmp arr[j]; arr[j] arr[j1]; arr[j1] tmp; } } if(flag 1)//这⼀趟没交换就说明已经有序后续⽆需排序了 break; } } int main() { int arr[] {3,1,7,5,8,9,0,2,4,6}; int sz sizeof(arr)/sizeof(arr[0]); bubble_sort(arr, sz); int i 0; for(i 0; i sz; i) { printf(%d , arr[i]); } return 0; }根据上面的代码我们可以很清晰的了解与认识冒泡排序。5. 二级指针指针变量也是变量是变量就有地址那指针变量的地址存放在哪里这就是二级指针。对于⼆级指针的运算有*ppa通过对ppa中的地址进⾏解引用这样找到的是pa*ppa其实访问的就是pa 。int b 20; *ppa b;//等价于 pa b;**ppa先通过*ppa找到pa,然后对pa进行解引用操作*pa那找到的是a 。**ppa 30; //等价于*pa 30; //等价于a 30;6. 指针数组指针数组是指针还是数组我们类比⼀下整型数组是存放整型的数组字符数组是存放字符的数组。那么指针数组就是存放指针的数组。指针数组的每个元素都是用来存放地址指针的。如下图7. 指针数组模拟二维数组#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数组中 int* parr[3] {arr1, arr2, arr3}; int i 0; int j 0; for(i 0; i 3; i) { for(j 0; j 5; j) { printf(%d , parr[i][j]); } printf(\n); } return 0; }parr数组的画图演⽰parr[i]是访问parr数组的元素parr[i]找到的数组元素指向了整型⼀维数组parr[i][j]就是整型⼀维数组中的元素。上述的代码模拟出二维数组的效果实际上并非完全是⼆维数组因为每⼀行并非是连续的。