目录
- [1. 数组名的理解](#1. 数组名的理解)
- [2 . 使用指针访问数组](#2 . 使用指针访问数组)
- [3. ⼀维数组传参的本质](#3. ⼀维数组传参的本质)
- [4. 冒泡排序](#4. 冒泡排序)
- [5. ⼆级指针](#5. ⼆级指针)
- [6. 指针数组](#6. 指针数组)
- [7. 指针数组模拟⼆维数组](#7. 指针数组模拟⼆维数组)
1. 数组名的理解
我们知道数组名就是数组首元素的地址,如arr
&数组名就是数组第一个元素的地址,也是数组首元素的地址,如&arr
c
#include <stdio.h>
int main()
{
char arr[] = {1,2,3};
printf("arr =%p\n", arr);
printf("&arr[0] =%p\n", &arr[0]);
return 0;
}

如果数组名是首元素的地址,那么如下代码怎么解释?
c
#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;
}

可以看到输出的是40,那么arr就不是数组首元素的地址了吗
其实数组名就是数组首元素(第一个元素)的地址是对的,但是有两个例外:
- sizeof(数组名),sizeof中存放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
除此之外,任何地方出现数组名,都表示数组首元素的地址
我们可以来观察如下实验
c
#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("arr+1 = % p\n", arr+1);
printf("&arr = % p\n", &arr);
printf("&arr+1 = % p\n", &arr + 1);
return 0;
}

我们可以观察到&arr[0],取出首元素的地址,+1(&arr[0]+1)跳过4个字节(跳过一个元素)
arr(首元素的地址),arr+1(首元素的地址+1)跳过4个字节(跳过一个元素)
为什么跳过4个字节呢?因为它们的类型都是一样的,都是int类型,所以是跳过一个整型
那么为什么&arr是跳过40个字节呢(10个元素,每个元素4个字节),因为它的类型不一样,是int()[10]类型的,具体的会在下一篇指针(3)里会讲到
2 . 使用指针访问数组
有了前面的知识,我们就可以通过指针访问数组了
c
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
size_t sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for ( i = 0; i < sz; i++)
{
//printf("%d ", arr[i]);
//我们知道arr是数组首元素的地址,p里放的也是arr首元素的地址那么p = arr,
//我们就可以写下如下代码
printf("%d ", p[i]);
//printf("%d", *(p + i));//p里放的是arr首元素的地址,p+i跳过i个元素,
//printf("%d", *(arr + i));//上面的注释我们可知p = arr,那么也可以这样写
//printf("%d", *(i + arr));//加法时支持交换律的,如:(6+8) =(8+6),那么我们这样写也是可以的,但是不提倡
//printf("%d", i[arr]);//不提倡,要求简介明了
}
return 0;
}
arr[i] 应该等价于*(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移量求出元素的地址,然后解引⽤来访问的
3. ⼀维数组传参的本质
既然我们可以在函数外部计算数组的元素,那么可以把数组传给一个函数后,在函数内部求一个数组的元素个数吗?
c
#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;
}

显然是不能的,在test函数内部, sizeof(arr)/sizeof(arr[0]),我们知道sizeof(arr[0])是4,既然结果是1的话,那么sizeof(arr)也是4,说以在传数组时,只传了数组首元素的地址
所以函数形参的部分我们因该使用指针变量来接收首元素的地址,我们在函数内部写sizeof(arr)计算的是一个地址的大小(单元字节),而不是数组的大小(单位字节)
正因为参数部分是指针,说以在函数内部没办法计算数组元素个数。
c
#include <stdio.h>
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;
}
4. 冒泡排序
c
#include <stdio.h>
#include <assert.h>
void bubble_sort(int* arr, size_t sz)
{
assert(arr);
int i = 0;
int j = 0;
int temp = 0;
for ( i = 0; i < sz - 1; i++)
{
int a = 1;
for ( j = 0; j < sz - 1 - i; j++)
{
if (*(arr + j) > *(arr + j + 1))
{
temp = *(arr + j);
*(arr + j) = *(arr + j + 1);
*(arr + j + 1) = temp;
a = 0;
}
}
if (a)
{
break;
}
}
}
int main()
{
int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
size_t 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. ⼆级指针
指针变量也是变量,是变量就有地址,那么指针变量的地址因该存哪里?
存二级指针里
c
#include <stdio.h>
int main()
{
int a = 0;
int* pa = &a;
int** ppa = &pa;
return 0;

- *ppa通过对二级指针的解引用,找到pa,*ppa其实访问的就是pa
c
int b = 20;
*ppa = &b//等价于pa = &b
- **ppa先通过*ppa找到pa,在通过对pa进行解引用的操作:*pa找到a
c
**ppa = 30;
//等价于*pa = 30
//等价于a = 30
6. 指针数组
指针数组是指针还是数组呢?
我们类比一下,整型数组是存放整型的数组,字符数组是存放字符的数组
那么指针数组就是存放指针的数组

指针数组的每块区域都是用来存放地址(指针)的

指针数组每个元素都是一个地址,可以指向一块空间
7. 指针数组模拟⼆维数组
c
#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 };
int i = 0;
int j = 0;
for ( i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]);//parr[i]是访问parr数组的元素,
//parr里存放的是一维数组首元素的地址
//parr[i][j]访问的就是一维数组j下标的元素
}
printf("\n");
}
return 0;
}

