指针的进阶
1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台/64位平台)。
3.指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4.指针的运算。
字符指针
int main()
{
char str1[] = "hello bit";
char str2[] = "hello bit";
const char* str3 = "hello bit";// 常量字符串
const char* str4 = "hello bit";// 内存中只会存一份
if (str1 == str2)
printf("1 and 2 \n");
else
printf("1and2are not sasme \n");// 输出
if (str3 == str4)
printf("3 and 4 \n");// 输出
else
printf("str3 and str4 not sasme \n");
return 0;
}
数组指针
int arr[10] = { 0 };
printf("%p",arr);
printf("%p",&arr);
- 数组名代表首元素地址 arr[0]
- &arr 是取出的数组的地址,数组的起始位置地址也是 arr[0]
- 地址打印出来一样,但是意义不同,类型不同。
- 数组名是数组首元素的地址
- 但是有两个例外
- sizeof(数组名)数组名表示整个数组 计算的是整个数组的大小,单位是byte
- &数组名 数组名表示整个数组,取出的是整个数组的地址
int main()
{
int arr[5] = { 1,2,3,4,5 };
//int* par = &arr; 这样只存储数组的第一个元素
// 取出数组的地址 和数组一个地址 一样元素
// par 就是数组指针
int (*par)[5] = &arr;
for (int i = 0; i < 5; i++)
{
printf("%d \n", (*par)[i]);
// 1 2 3 4 5
}
double* d[5];
double* (*pd)[5] = &d;// pd 就是一个数组指针
return 0;
}
// ---------------------------------------------------------------------
void print1(int (*par)[5],int r,int c)
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
printf("%d", (*par +i)[j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
print1(arr, 3, 5);
return 0;
}
数组参数、指针参数
在写代码的时候难免要把【数组】或者【指针】传给函数,函数的参数该如何设计?
- 一维数组传参
- 二维数组传参
- 一级指针传参
// int * p 接收到传过来数组的第一个地址
void test(int * p,int qz)
{
for (int i = 0; i < qz;i++)
{
printf("%d", *(p + i));
}
}
// char* pc 传过来字符的第一个地址
void test1(char* pc)
{
}
int main()
{
int arr[5] = {1,2,3,4,5};
int qz = sizeof(arr) / sizeof(arr[0]);
// p 是一级指针
int* p = &arr;
test(p, qz);
char ch = 'w';
char* pc = &ch;
test1(pc);
return 0;
}
- 二级指针传参
void test(int ** p)
{
**p = 20;// 改变 a
}
int main()
{
int a = 10;
int * pa = &a;// pa 是一级指针
// 二级指针是专门来存放一级指针变量的地址
int** ppa = &pa;// ppa 是二级指针
// 把二级指针进行传参
test(ppa);
test(&pa);// 传一级指针变量地址没问题
int* arr[10] = { 0 };
test(arr);// 也是可以的
printf("%d", a);// a = 20
return 0;
}
函数指针
函数指针:指向函数的指针!存放函数地址的指针!
int Add(int x,int y)
{
return x + y;
}
void test(char *str)
{
}
// 函数指针存储
int main()
{
int arr[10] = { 0 };
int(*pa)[10] = &arr;// 取出数组的地址
// pa 是指向数组的指针 存放的是数组的地址
// 函数指针 存放函数地址的指针
// &函数名 取到的就是函数的地址
// padd 就是一个函数指针变量
int (*padd)(int ,int) = &Add;
void (*pt)(char*) = &test;
return 0;
}
// -----------------------------------------------------------
int Add(int x,int y)
{
return x + y;
}
// 函数指针调用
int main()
{
// padd 就是一个函数指针变量
int (*padd)(int ,int) = &Add;
// &Add == Add
// 用函数指针调用 他所指向的那个函数
int res = (*padd)(10, 10);
int res1 = padd(10, 10);
// padd == (*padd)
printf("%d", res1);
return 0;
}
函数指针数组
函数指针数组:存放函数指针的数组。
整型指针 int*
整型指针数组 int * arr[5];
函数指针数组 int (*arr[2]) (int,int) = {Add,Sub};
int Add(int x,int y)
{
return x + y;
}
int Sub(int x,int y)
{
return x - y;
}
int main()
{
// parr 就是函数指针数组
int (*parr[2])(int, int) = { Add,Sub };
printf("%d" , parr[0](10, 10));// 20
return 0;
}
// -----------------------------------------------------------------------
int Add(int x,int y)
{
return x + y;
}
int Sub(int x,int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Dev(int x, int y)
{
return x / y;
}
void muon()
{
printf("************************************ \n");
printf("***** 1.add ************ 2.sub ******* \n");
printf("****** 3. mul************ 4.dev ******* \n");
printf("************** 0.exit ************** \n");
printf("************************************ \n");
}
int main()
{
int input = 0;
int x = 0; int y = 0;
// parr 函数指针数组
int (*parr[5])(int, int) = {NULL, Add,Sub,Mul ,Dev };
do {
muon();
printf("请选择 1-4: > \n");
scanf("%d", &input);
if (!input) break;
printf("请输入计算数: > \n");
scanf("%d %d", &x,&y);
int ret = (parr[input])(x,y);
printf("%d \n", ret);
} while (input);
return 0;
}
回调函数
回调函数:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
int main()
{
int(*parr[4])(int,int) = {Add,Sub,Mul,Dev};
int x = 10; int y = 20;
// 回调函数
int ret = Carc(parr[0],x,y);
printf("%d", ret);
return 0;
}
指针和数组面试题的解析
总结:数组名的意义:
- sizeof(数组名),在这里的数组名表示整个数组,计算的整个数组的大小。
- &数组名,这个理的数组名表示整个数组,取出整个数组的地址。
- 初此之外所有的数组名都表示首元素的地址。
int main()
{
int a[5] = { 1,2,3,4,5 };
int* prt = (int*)(&a +1);
printf("%d , %d", *(a + 1), *(prt - 1));// 2 5
/*
解释: 声明int a[5];数组
(&a +1) &a相当于取出整个数组 整个数组 +1 相当于跳过了整个 a数组
并强制类型转换 int(*)[5]
*(a + 1) 取出 a地址 1 + 1 为 2
*(prt - 1) 当前的指针在 数组的最后面 让它往后 -1 为 5
*/
return 0;
}
// ----------------------------------------------------------------------
int main()
{
// 这里是 逗号表达式 (0,1) 有坑
int a[3][2] = { (0,1),(2,3),(4,5) };
// 放的是 { {1,3},{5,0},{0 ,0} }
int* p;
p = a[0];// 1
printf("%d", p[0]);// 1
return 0;
}
// ---------------------------------------------------------------------