1. 数组名的理解
在上⼀篇blog中我们在使⽤指针访问数组的内容时,有这样的代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
这⾥我们使⽤ &arr[0] 的⽅式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址,⽽且
是数组⾸元素的地址:
#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;
}
输出结果:数组名和数组⾸元素的地址打印出的结果相同,数组名就是数组⾸元素(第⼀个元素)的地址
但是,数组名如果是数组⾸元素的地址,这句话不够严谨:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%zu\n", sizeof(arr));
return 0;
}
输出的结果是40,如果arr是数组⾸元素的地址,那输出应该的应该是8才对(x64)
数组名的理解有两个特殊情况:
•
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 = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}
&arr和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[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] = 0077F820
&arr[0]+1 = 0077F824
相差4个字节
arr = 0077F820
arr+1 = 0077F824
相差4个字节
&arr = 0077F820
&arr+1 = 0077F848
两者相减得0x28,40byte,是整个数组的大小,&arr+1跳过的是整个数组。这个原理和上一篇blog中介绍的不同类型的指针的意义相似,类型不同,单位不同,所能操作的内存空间就不同
除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址
2. 使用指针访问数组
int main()
{
int arr[10] = { 0 };
//给数组中存值
for (int i = 0; i < 10; i++)
{
scanf("%d", *(arr+i));
}
//打印数组
for (int i = 0; i < 10; i++)
{
printf("%d", *(arr+i));
}
return 0;
}
不用指针打印数组元素时,一般这样写arr[i],这里用指针的表达方式是这样,二者等价
arr[i] == *(arr + i)
//加法交换律
arr[i] == *(arr + i) == *(i+arr) == i[arr] ???
发现了一个有趣的点
是否可以这样表示呢???
int main()
{
int arr[10] = { 0 };
//给数组中存值
for (int i = 0; i < 10; i++)
{
scanf("%d", (arr + i));
}
//打印数组
for (int i = 0; i < 10; i++)
{
printf("%d ", i[arr]);//没毛病6666
}
return 0;
}
通过运行发现,编译通过。布什戈门,有点逆天了哈哈哈哈哈
这说明:[]仅仅是一个操作符而已,这是这样子表示的,在计算机内部表示的就是*(i+arr)
3. 一维数组传参的本质
通过一段代码进行观察:
//一维数组传参的本质
size_t test(int a[])
{
size_t sz = sizeof(a) / sizeof(a[0]);
return sz;
}
int main()
{
int a[] = { 1,2,3,4,5,6,7 };
printf("1. %zu\n", sizeof(a) / sizeof(a[0]));
printf("2. %zu\n", test(a));
}
运行结果:两次打印的结果并不相同,第一种就是7,元素个数;第二种则为2(x64)
倒推一下,a[0]为int大小是4,结果是2说明sizeof(a)为8,这就说明这是一个指针。而我们实参就是数组名,也就是首元素的地址
//一维数组传参的本质
size_t test(int a[])//本质上是指针
{
size_t sz = sizeof(a) / sizeof(a[0]);
//传进来的是a,不是&a,不是sizeof(a)
//因此并不能算出来整个数组的大小
return sz;
}
int main()
{
int a[] = { 1,2,3,4,5,6,7 };
printf("1. %zu\n", sizeof(a) / sizeof(a[0]));
printf("2. %zu\n", test(a));
}
//本质上传的是首元素地址
函数的参数部分是本质是指针,在函数内部是没办法求的数组元素个数的
总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式
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[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = 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 - 优化
//冒泡排序
int bubble_sort(int arr[],int sz)
{
int cnt = 0;
for (int i = 0; i < sz - 1; i++)
{
int flag = 1;
for (int j = 0; j < sz - i -1; j++)
{
int tmp = 0;
if (arr[j] > arr[j + 1])
{
cnt++;//每交换一次,记录一下
tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;
flag = 0;
}
}
if (flag)//排完一趟发现并没有实现交换,则不用再排序了,解释:
//因为冒泡排序是从前到后依次比较的,没有实现交换,那么则都是整齐的
{
return cnt;
}
}
return cnt;
}
int main()
{
int arr[] = { 3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int cnt = bubble_sort(arr,sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
printf("%d\n", cnt);
return 0;
}
5. ⼆级指针
指针的指针
#include <stdio.h>
int main(){
int a = 100;
int * pa = &a;
int* * ppa = &pa;
return 0;
}
pa是一个指针变量,势必也会有开辟内存空间,该空间也势必有自己的指针,即使这块空间中存放的就是一个指针
6. 指针数组
//指针数组
//类比 字符数组 整形数组
//存放指针的数组
是存放指针的数组,指针数组的每个元素都是⽤来存放地址(指针)的
指针数组的每个元素是地址,⼜可以指向⼀块区域
7. 指针数组模拟二维数组
//使用指针数组模拟一个二维数组
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* arr[] = { arr1,arr2,arr3 };//二维数组
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
数组arr中放着各个数组的首元素地址,通过这些地址又可以找到他们数组中的其他元素,可以看成是一个二维数组,但:
上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的。