目录
一.数组名的理解
1.通关代码举例:
(1)讨论数组名到底是不是首元素的地址?
int main()
{
char arr[10] = "0";
printf("%p\n", arr[0]);//007AFE3C
printf("%p\n", arr); //007AFE3C
printf("%d\n", sizeof(arr));//10
//1.数组名不是传递首元素地址吗?sizeof计算出来应该首元素的大小才对,但这里计算的是整个数组大小。
printf("%p\n", &arr); //007AFE3C
//2.&数组名表示是整个数组,取出的是整个数组的地址。
return 0;
}
特例:有2个数组名取的是整个数组的地址
1.sizeof(数组名)内部单独放一个数组名的时候,数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2. & 数组名,这的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组的元素
的地址是有区别的)。
除此之外,遇到的所有数组名都是数组元素的地址。
特点:不管取的首元素地址,还是取的整个元素地址,他们拿到的都是首元素地址。
arr[0];//007AFE3C
arr; //007AFE3C
&arr; //007AFE3C
(2)整个数组的地址和数组首元素的地址的区别:
int main()
{
int arr[10] = { 0 };
//(1)
printf("%p\n", &arr[0]);//int*类型
printf("%p\n", &arr[0] + 1);//跳过4个字节
//(2)
printf("%p\n", arr);//int*类型
printf("%p\n", arr + 1);//跳过4个字节
//(3)
printf("%p\n", &arr);//类型?
printf("%p\n", &arr + 1);//跳过40个字节,类型不一样
return 0;
}
讨论:指针+1取决于类型,所以&arr取数组名到底是什么类型?
注意:取出数组的地址和取出数组首元素的地址,从值上来看是一样的,但有很大的区别。
二.使用指针访问数组
(1)因为数组在内存中是连续存放的。
(2)数组名就是首元素的地址(方便找到起始位置)。
(3)可以使用指针访问数组。
代码举例:指针访问数组
总结:
1.上面4种写法,只是写法不同,编译器都是转换成第二种写法计算的。
2.我平常使写代码,一般只用1,2种写。
3.数组名就是地址,数组访问元素依然通过指针找的,数组的本质就是指针。
4.所有关于指针的运算,底层最终都是指针( + -)整数,指针解引用相关运算。
补充:
因为数组名和指针等价 pa < == > arr 等价
scanf:
&arr[i] = &pa[i];
pa + i = arr + i;
&i[pa] = &i[arr];
printf:
arr[i] = pa[i];
*(arr + i) = *(pa + i);
i[arr] = i[pa]
三.一维数组传参的本质
先从一个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把数组传给个函
数后,函数内部求数组的元素个数吗?用sizeof计算 。
void fufu(int arr[])//数组形式
{
int sz = sizeof(arr) / sizeof(arr);// 4 / 4 =1
//sizeof这里拿到的只是第一个元素的地址
printf("%d", sz);//? 1
}
void fufu(int* arr)//指针形式
{
int sz = sizeof(arr) / sizeof(arr);// 4 / 4 =1
printf("%d", sz);//? 1
}
int main()
{
int arr[10] = { 0 };
fufu(arr);//这里数组名就是数组首元素的地址
return 0;
}
最终通过代码发现传参之后的值不一样,为什么?
1.数组传参的时候,传递的是并非是整个数组。
2.传递的是数组首元素的地址。做不到传递整个数组。
3.数组传参,形参部分可以写成数组的形式,也可以写成指针形式,只是形式上的差异,本质上一模一样。
4.使用sizeof计算数组个数,要在没传参之前求,求完之后以参数形式传给函数。如果想在函数里面求做不到,一旦传参就变了。
5.没传参之前是个数组,传参之后就是首元素地址。
6.数组变成首元素地址了,这叫(降级)。
数组传参和变量传参区别:
1.数组传参,形参部分写成数组,也不会创建数组,本质是指针,还是访问实参的数组。
2.变量传参:形参和实参不是同一块空间。
讨论:为啥不传递整个数组 ,代价非常大,从空间节省来说
假设一个数组放了一个1000个元素。
实参传给形参,形参要自己创建一块空间,要是传递整个数组,那我是不是又要创建能放下1000个元素的空间。所以只需要传递首元素地址,访问主函数里面元素就行。
四.冒泡排序
排序的方法很多:
1.冒泡排序
2.选择排序
3.插入排序
4.希尔排序
5.快速排序……等等。
2.练习
题目:写一个函数,对一个整形数组的数据进行排序。
冒泡排序讲解:
核心思想:相邻的两个元素比较,如果不满足顺序就交换!
函数实现:
int count = 0;//比较的次数
void fufu(int* arr, int sz)
{
int flag = 1;//假设已经有序
//1.趟数:sz-1
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//2.一趟冒泡排序的过程
int j = 0;
//3.一趟冒泡排序进行比较的对数:sz-1-i
for (j = 0; j < sz - 1 - i; j++)
{
//4.一对数的比较,和交换
if (arr[j] > arr[j + 1])
{
//5..交换
int tmp = 0;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
//6.一但交换,flag变为0
flag = 0;//还不是有序的
count++;//比较一次count++
}
}
if (flag == 1)
{
break;
}
}
}
void PrintfAdd(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
fufu(arr, sz);//冒牌排序的实现
PrintfAdd(arr, sz);//打印数组的实现
printf("\ncount = %d\n", count);//count = 45
return 0;
}
五.二级指针
一级指针:
char* pc
int* pi
double* pd;只有一颗*都是一级指针。
1.二级指针
代码举例:
2.二级指针用法
代码举例:
int main()
{
int a = 10;
int* p = &a;
int** pp = &p;
printf("%d\n", **pp);//10
return 0;
}
第一次解引用得到p,第二次解引用得到a的值。
总结:
指针变量也是变量!只是用来存放地址的,除此之外没啥nb的了。
指针变量也有地址,也可以被其他指针变量存起来!
语法虽然支持多级指针写法,但一般用到二级指针差不多了。
二级指针就是一个普通的变量而已,这个变量只不过用来存放一级指针变量地址。
补充:错误写法
当二级指针,指针变量的类型写错了,少写一颗星,可以存放一级指针的地址,但解引用和(+-)权限也会有变化,所以类型写错了,计算也会有问题。
六.指针数组
指针数组是指针还是数组?是数组,是指针类型的数组。
整形数组 - 存放整形数据的数组(数组中每个元素是整形类型)
字符数组 - 存放字符数据的数组(数组中每个元素是字符类型)
指针数组 - 存放指针的数组(数组中的每个元素是指针类型)
int arr[10]; 整形数组
char ch[5]; 字符数组
希望有一个数组,数组有4个元素,每个元素是整形指针
int* arr[4];每个元素是整形指针,所以是指针数组。
总结:指针数组每个元素都是指针。
七.指针数组模拟二维数组
模拟写出二维数组的效果,但不是二维数组。
代码举例:
补充:二维数组的每一行是一个一维数组
int main()
{
//1.这3个一维数组当做二维数数组每一行
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//2.用指针数组,包装3个数组
int* arr[3] = { arr1,arr2,arr3 };//整形指针数组
//3.打印元素
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%d ", *(*(arr+i)+j);
}
}
return 0;
}