int main()
{
int arr[10] = { 0 };
printf("arr[0]=%p\n", &arr[0]);
printf("arr =%p\n", arr);
return 0;
}结果:

sizeof的疑惑?为啥
sizeof(arr)是40,不应该是4/8吗?
数组名确实是数组的首地址,但是有两个例外
sizeof():sizeof里面单独放一个数组,这里的数组名表示的是整个数组,计算的是整个数组的大小,单位字节&arr:这里的数组名也表示的是整个数组,取出的是整个数组的地址(整个数组的地址与数组首元素的地址有区别)
除此之外,数组名在任何地方都代表数组的首地址整个数组的地址与数组首元素的地址有啥区别?
虽然数值相同,但是类型不一样

前提:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (int i = 0; i < sz; i++)
{
scanf("%d", p + i);
}
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
}更多指针访问数组的写法扩展:


如何理解:

看下面的代码
#include<stdio.h>
void Print(int arr[])
{
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
Print(arr);
return 0;
}为啥
sz放到函数里面,不能把数组完整的打印出来?
由于数组传的是指针,应该用指针变量来接收,形参虽然可以写成数组的形式,但是本质还是指针变量
因为传的是指针变量,所以求的是指针变量的大小,sizeof(arr) / sizeof(arr[0])得不到元素的大小
小结:
需要想清楚以下问题?


具体代码:
void bubble_arr(int *arr,int sz)
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
void Print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[7] = { 4,9,7,6,3,2,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_arr(arr, sz);
Print(arr, sz);
return 0;
}当数组接近排好序时,可能会多此一举

优化后:
void bubble_arr(int *arr,int sz)
{
for (int i = 0; i < sz - 1; i++)
{
int flag = 1;
for (int j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
void Print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[7] = { 4,9,7,6,3,2,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_arr(arr, sz);
Print(arr, sz);
return 0;
}二级指针(变量)存放的是一级指针的地址 一级指针和二级指针理解方式是一样的,如图:

int main()
{
int a = 10;
int* p = &a;
int** pp = &p;
return 0;
}p+1–>跳过4个字节
pp+1–>跳过4/8个字节(取决于环境)

*pp指向的是p,那**pp指向的就是a,有间接访问的效果

是数组还是指针?
是数组 指针数组的每个元素是用来存放指针

用法:指针数组模拟二维数组管理一维数组

int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 6,7,8,9,10 };
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;
}可以模拟模拟二维数组,但不是真的数组,二维数组是连续存放的,而它只是打印出来 原因:

两种写法:
int main()
{
char arr1[] = "abcdef";
char arr2 = "abcdef";
return 0;
}
常量字符串是不能改的

标准: const char* p2 = arr2;让编译器提前编译出错误
还可以打印出来
printf("%s", p1); printf("%s", p2);
注意:%s打印字符串时,只需提供起始地址
子符串常量放在代码块,不在静态区、栈区和堆区(因为它们可以改的,而字符串常量不能改)
下面结果是啥?


原因:子符串常量放在代码块,不在静态区、栈区和堆区(因为它们可以改的,而字符串常量不能改)

理解方法:类比
int (*p)[10]:把*p去掉,相当于把数组名去掉,剩下类型int[10],所以是指向数组的指针
但是实际类型是int (*)[10],这是数组指针类型

如何理解:

不恰当的用法(非常啰嗦了)
补充知识:
*与&互为逆运算,并且&arr就是p,因此*p为arr也就是首元素的地址
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int (*p)[10] = &arr;
for (int i = 0; i < 10; i++)
{
printf("%d ", (*p)[i]);
}
return 0;
}具体用法在二维数组传参里用到
以前我们二维数组实参传的是数组,形参接收也是数组,那二维数组传参的本质是什么呢?
补充:
&arr、sizeof(arr)这两种情况以外)
那二维数组首元素的地址是谁的地址?
把二维数组的一行看成是一维数组组成,把每一个一维数组看成一个元素,二维数组每个元素是一维数组,首元素的地址就是第一行的地址,第一行的地址就是一维数组地址,类型是数组指针类型arr是第一行的地址,(arr+i)是每一行的地址,*(arr+i)是每一行的起始地址,而在二维数组中理解方法中arr[i]可以用来表示每一行起始地址,然后*(*(arr+i)+j)就可以表示arr[i][j]
打印二维数组(指针实现)void Print(int (*p)[4], int x, int y)
{
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][4] = { {1,2,3,4}, { 5,6,7,8 } ,{ 9,10,11,12 } };
Print(arr, 3, 4);
return 0;
}函数也有地址吗?可以打印出来吗?
不能类比数组

以Add函数为例
&Add与Add是同一个地址,不存在首元素的地址,都是函数的地址,没有区别
怎么存函数的地址呢?
函数指针变量存放函数的地址

去掉名字,int (*)(int,int)为函数指针类型,函数里的参数类型需要保持一致,形参的名字是可以干掉的(不用写,压根就不用)

具体用法:
以Add函数为例
原来调用函数是用函数名调用
Add(),如何用指针实现这个功能呢?
以Add函数为例(自己用要根据实际情况来看要不要传参,要不要接收返回值)

根据指针可以找到函数并调用函数,但是Add能直接调用,其实指针不用解引用也能够直接调用,*号可以不写或者写多个,这样也不会有问题,*就是个摆设


里面函数指针类型和强制类型转换非常重要,需要识别出来

解析:

代码二理解起来非常难,有什么办法呢?
具体用typedef进行简化
概念:当类型不合适时,可以重新起名

用法:适用于简单的指针类型(int*、char*……)
typedef unsigned int u_int;
int main()
{
unsigned int a1;
u_int a2;
}对于复杂的指针类型

正确方式:

同理函数指针一样

回到第十章中的代码2,可以简化:


区别:
ptr_t就是int *类型PTR_T把int*替代了,是符号不是类型

总结:用define指定类型不够彻底,最好不用,但是也不是没有这种做法,看具体情况
先类比一下指针数组int *arr[10],说明数组是可以存放指针的,当数组存放的是函数指针时,那就是函数指针数组
应用场景:

使用方法:

有了函数指针数组,就可以进行多次计算(利用for循环遍历数组):
当你写了一个计算器(用switch语句不停的调用case1、case2、case3……不停的调用,并且补充相应的功能),如何用函数指针去优化
操作:

总的操作:

很好的利用到函数指针数组,这样写方便扩展其他的功能:

函数指针数组就像是个跳板,可以转到不同的功能,故称为转移表
补充:函数指针的知识
函数指针应用场景: 回调函数就是通过函数指针调用的函数 当在主调函数里面没有直接去调用函数时,传递所需的函数的地址给另一个函数,在另一个函数中用函数指针调用所需的函数,这些所需的函数就叫做回调函数,另一个函数相当于中介,中介与主调函数是拿函数指针去沟通的
qsort——是用来排序的库函数,底层用的是快速排序的方法,而我们之前学过冒泡排序了补充知识:冒泡排序函数实现,对一组整形数据进行排序,排为升序 思想:两两相邻元素进行比较,不满足顺序就交换
void Bubble_arr(int *p,int sz)
{
for (int i = 0; i < sz-1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (*(p+j) > *(p + j+1))
{
int tmp = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = tmp;
}
}
}
}
void Print(int *arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
printf("\n");
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_arr(arr, sz);
Print(arr, sz);
return 0;
}缺点:只能排序整形数据,形参部分已经写死了,再写一个函数排浮点型太麻烦了
而qsort可以排序任意类型的数据

如何理解第四个参数: 看一下如何用回调函数改造冒泡排序的案例:


注意:
qsort()的使用者,明确知道要排序的是什么数据以及这些数据怎么比较,所以应由我们来提供比较函数void*类型的指针是无具体类型指针,也不能进行解引用(不知道访问几个整型)和指针加减运算
整数比较为例int com_int(void* p1, void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void print_arr(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[] = { 4,6,7,1,3,9,0,1,3 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), com_int);
print_arr(arr, sz);
return 0;
}
结构体比较,按名字来排序
#include<stdlib.h>
#include<string.h>
struct Stu
{
char name[100];
int age;
};
int Fuc(void* p1, void* p2)
{
return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
int main()
{
struct Stu arr[] = { {.name = "zhangsan",.age = 18},{"lishi",19},{"hehe",20} };
int len=sizeof(arr) / sizeof(arr[0]);
qsort(arr, len, sizeof(arr[0]), Fuc);
return 0;
}结构体比较,按年纪来排序
#include<stdlib.h>
#include<string.h>
struct Stu
{
char name[100];
int age;
};
int Fuc(void* p1, void* p2)
{
return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
int main()
{
struct Stu arr[] = { {.name = "zhangsan",.age = 19},{"lishi",18},{"hehe",20} };
int len = sizeof(arr) / sizeof(arr[0]);
qsort(arr, len, sizeof(arr[0]), Fuc);
return 0;
}可以通过调试里的监视来进行验证

模仿qsort函数来实现冒泡排序的函数,可以实现排序任何数据
原来bubble函数中形参为int*,为了接收任何数据的地址,改成void*类型的1地址
void Swp(char* p1, char* p2, size_t width)
{
for (int i = 0; i < width; i++)
{
char tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2++;
}
}
int cmp(void* p1, void* p2)
{
return *(int *)p1-*(int *)p2;
}
void Bubble_arr(void* base, size_t sz, size_t width, int(*cmp)(void* p1, void* p2))
{
for (int i = 0; i < sz - 1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0)
{
Swp((char*)base + width * j, (char*)base + width * (j + 1), width);
}
}
}
}
void Print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_arr(arr, sz, sizeof(arr[0]), cmp);
Print(arr, sz);
return 0;
}分析过程如下:
