目录
一、指针
1.1字符指针
1.1.1字符
char ch = 'z';
char* p = &ch;
1.1.2字符串
char* p = "hello world";
从表面上看会产生误解,hello world是个地址??
- 但其实只是把hello world的
首字母地址
传给了指针变量p
- hello world作为常量字符串,随意改变其内容会报错
int main() { char* p = "hello world";//const char *p *p = "haha"; printf("%c", *p); }
辨析:int main() { char str1[] = "hello world"; char str2[] = "hello world"; char* str3 = "hello world"; char* str4 = "hello world"; if (str1 == str>2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if (str3 == str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); }
C/C++会把常量字符串存储到单独的一个内存区域
1.2指针数组与数组指针
1.2.1定义一个指针数组
int a = 1, b = 2, c = 3, d = 4;
int* parr[4] = { &a,&b,&c,&d };
char* pch[] = { "hello","world" };
Tip:
- 指针数组的本质是:数组
1.2.2指针数组的使用
int main()
{
int a = 1, b = 2, c = 3, d = 4;
int* parr[4] = { &a,&b,&c,&d };
for (int i = 0; i < 4; i++)
{
printf("%d ", *(parr[i]));
}
return 0;
}
int main()
{
char* pch[] = { "hello","world" };
for (int i = 0; i < 2; i++)
{
printf("%s ", pch[i]);//对于printf()来说只要给字符串的起始地址就能打印出对应的内容
}
return 0;
}
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int* parr[] = { arr1,arr2 };
printf("%d %d", *(parr[0]), *(parr[1]));
}
刚刚针对数组只能打印出首元素,那么用数组指针如何遍历数组呢?
所以可以这样写int main() { int arr1[5] = { 1,2,3,4,5 }; int arr2[5] = { 2,3,4,5,6 }; int* parr[] = { arr1,arr2 }; for (int i = 0; i < 2; i++) { for (int j = 0; j < 5; j++) { printf("%d ", parr[i][j]); } printf("\n"); } }
1.2.3定义一个数组指针
int a = 10;
int* pi = &a;//整型的地址放在整型指针中
char b = 'a';
char* pc = &b;//字符型的地址放在字符型指针中
int arr[10] = { 0 };
int(* parr)[10] = &arr;//数组的地址放在数组指针中
Tip:
[ ]
的优先级要高于*
号的,所以必须加上()
来保证p先和*结合数组指针的本质是:指针
数组指针的类型:int(* )[10]
1.2.4指针数组的使用
对于遍历一维数组:
//初级想法
void print1(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
//深入本质
void print2(int* parr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(parr + i));// *(parr + i)等价于arr[i]
}
}
//数组指针的方式
void print3(int(* parr)[10], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ",parr[0][i]);//parr[0][i]等价于(*parr+0)[i]
}
}
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
print1(arr, sz);//传数组首位的地址
printf("\n");
print2(arr, sz);
printf("\n");
print3(&arr, sz);
return 0;
}
对于遍历二维数组:
void print1(int arr[3][3])
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d ", arr[i][j]); //arr[i][j]等价于*(*(arr+i)+j)等价于 *(arr[i] + j)
}
printf("\n");
}
}
void print2(int (*parr)[3][3])
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d ", parr[0][i][j]);//parr[0][i][j]等价于(*parr+0)[i][j]
}
printf("\n");
}
}
int main()
{
int arr[3][3] = { 0 };
print1(arr);
printf("\n");
print2(&arr);
return 0;
}
总结:数组参数、指针参数
一维数组传参
void test1(int arr1[])//[]里面的数字随便
{}
void test2(int *p)
{}
void test3(int* arr2[])//[]里面的数字随便
{}
void test4(int** p)
{}
int main()
{
int arr1[10] = { 0 };
int* arr2[10] = { 0 };
test1(arr1);
test2(arr1);
test3(arr2);
test4(arr2);
return 0 ;
}
arr2
的类型为int* [10]所以要用int**p传参
二维数组传参
void test1(int arr[][3])//只可以省略第一个[]里面的数字
{}
void test2(int(*p)[3])
{}
void test3(int* arr2[][3])//只可以省略第一个[]里面的数字
{}
void test4(int** p)
{}
int main()
{
int arr1[3][3] = { 0 };
int* arr2[3][3] = { 0 };
test1(arr1);
test2(arr1);//二维指针传参传的是第一行的地址
test3(arr2);
test4(arr2);
return 0;
}
一级指针传参
如果形参为
那么实参可以为?
void test(int* ptr)
{}
int main()
{
//整型变量的地址
int a = 0;
int* p1 = &a;
test(&a);
test(p1);
//一维数组
int arr[10] = { 0 };
int* p2 = arr;
test(arr);
test(p2);
return 0;
}
二级指针传参
如果形参为
那么实参可以为?
void test(int** ptr)
{}
int main()
{
int a = 0;
int* pa = &a;
int** ppa = &pa;
test(&pa);
test(ppa);
int*arr[10] = { 0 };
test(arr);
return 0;
}
1.3函数指针
定义:本质是一个指针,指向的是一个函数的地址。任何一个变量,总是要先声明,之后才能使用的函数指针变量也应该要先声明。
eg.
int (*pf)(int,int);//函数指针的声明
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
Add(a, b);
int(*pf)(int, int) = Add;//函数指针的使用
printf("%p", pf);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/0ed979e2c0c26bc17aa08fd848743cb2.png)
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int, int) = Add;
printf("%d\n", (*pf)(10, 20));//函数指针的调用
printf("%d", Add(10, 20));
return 0;
}
Tip:
- &函数名 == 函数名
- 函数指针的类型:int(* )(int ,int )
- (*pf)(10,20)与 pf(10,20)等价
1.3.1函数指针数组
eg.
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 Div(int x, int y)
{
return x/y;
}
int main()
{
int (*pfArr[4])(int, int) = { Add,Sub,Mul,Div };//定义一个函数指针数组
for (int i = 0; i < 4; i++)
{
printf("%p ", pfArr[i]);
}
return 0;
}
PS:升级版简单计算器的实现请戳👉C语言实现简单计算器👈
1.3.2指向函数指针数组的指针
int(*pf)(int, int) = Add;//函数指针
int (*pfArr[4])(int, int) = { Add };//函数指针数组
int (*(*ppfArr)[4])(int, int) = &pfArr;//指向 函数指针数组的 指针
1.3.3回调函数
定义:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。