什么是指针?
大家都知道为了存放一个整数,我们用的是int类型,那么如果我现在要存放一个地址应该使用什么变量呢?
- int a=10;
- int* p=&a;
- char a='b';
- char* p=&a;
这里的P就是一个指针变量,它存放了a变量的地址。我们用int* 来表示指针类型。那么int* 类型表示用来存放
整数型变量的地址。同理: char* 类型表示用来存放字符型变量的地址。这里需要强调一下:正真存放地址的是变量p,
前面的指针类型只是表示它应该存放什么类型的变量。
int a=10;int * p=&a;
- p=a;
- p=&a;
- *p=a;
- *p=&a;
上面几种形式中哪个是正确的?
第一句;将a赋给p,刚才已经说了a=10;相当于将10赋值给了p;而p是一个指针他应该存放的是地址,所以错误。
第二句;“&”表示一个变量的地址,所以&a就是将a的地址给了指针变量p,所以正确;
第三句:“*”表示解引用操作符,*p就表示p所指向的地址中所存放的值。所以将a赋值给*p是正确的;
第四局:&a得到的是a变量的地址,而*p得到的是p所指向了内存单元的值。地址就是地址,数值就是数值。两者没有半毛钱关系
所以就不能将&a赋值给*p;所以错误;
特殊指针
例如
- char string[]="abcdefg";
- char* string="abcdefg";
- int a[] ={1,2,3};
- struct Student* p
- typedef void(*p)()
第一句:string隐式转换是一个指针;
第二句:string是一个指针;而它和第一句的区别在于第一句直接将字符串内容赋值给了string开辟的数组的空间当中,而第二字符串没有在栈区而存放在常量区。
第三句:同样a隐式转换成指针。
第四句:p是结构体指针,指向结构体的地址。
第五句:定义一个返回值为void参数为空的函数指针变量p。
指针能干什么?
- 指针最大作用就是修改变量的值,例如:
#include<stdio.h> void Func(int* b){ *b = 1; } int main(){ int a = 10; Func(&a); printf("%d",a); return 0; }
假如我们想在函数中修改a的值,如果我们传参的时候传的是a那么能否在函数中修改a的值?
-
答案肯定不行,因为如果传一个a之后在函数中修改的是b的值,和a压根没有什么关系。有的同学就要问了,那我把函数中的形参改为a那不就行了吗?我只想说你太天真了,这就好比你有100元,而我也有100元。那你的100元就是我的了?,所以我们在函数传参的时候要传变量的地址因为只有传地址到函数中让指针解引用就间接的修改了a的值。(函数的形参是实参的一份临时拷贝)
-
所以只要得到了变量的地址就可以对它解引用修改地址所对应空间的值,但这并不是绝对的。有的指针就不能解引。
-
int* a=0; void* p=&a;
(*p=0)? 答案真的是这样吗?首先我们需要搞清楚一个问题就是void*是什么类型? void*就是可以接受所有指针类型的指针
简而言之就是万能指针,那它为什么就不能解引用? 因为系统并不知道他所指向的内存单元有多大,所以不能对它解引用。
二级指针
- int a=10; int* p=&a ;int** p=&&a
- 既然整形变量存在地址我们用int*表示 ,那么同样指针变量也存在地址,而存放它就需要用int** 存放。
- 同样多级指针也是类似的做法。只不过分析起来就会相对麻烦一点。
- **p=a;(成立)
- **p=&a(不成立)
- **p=&&a(成立)
指针数组
- int a[]={1,2,3};
- int* b[]={&a[0], &a[1], &a[2]};
顾名思义指针数组就是用来存放指针的数组
数组指针
- char a[10][=“abcdefg”;
- char* p=a;
- char* p=&a;
- &a==a;
- char (*)[10]=&a;
可能许多小伙伴有点懵逼,&a==a?
1.a表示数组首元素的地址,&a表示整个首元素的地址。在地址值方面它们是完全等价的。
2.但是a+1和&a+1则完全不是一回事,&a+1是跨过了整个数组,而a+1则是数组的下一个元素。
3.我们通常用(char (*)[10]=&a;)来表示数组指针。
常见的指针和数组问题(非常重要)
#include<stdio.h> #include<stdlib.h> #include<string.h> //一维数组 int main(){ int a[] = { 1, 2, 3, 4 }; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(a + 0)); printf("%d\n", sizeof(*a)); printf("%d\n", sizeof(a + 1)); printf("%d\n", sizeof(a[1])); printf("%d\n", sizeof(&a)); printf("%d\n", sizeof(*&a)); printf("%d\n", sizeof(&a + 1)); printf("%d\n", sizeof(&a[0])); printf("%d\n", sizeof(&a[0] + 1)); //字符数组 char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); char *p = "abcdef"; printf("%d\n", sizeof(p)); printf("%d\n", sizeof(p + 1)); printf("%d\n", sizeof(*p)); printf("%d\n", sizeof(p[0])); printf("%d\n", sizeof(&p)); printf("%d\n", sizeof(&p + 1)); printf("%d\n", sizeof(&p[0] + 1)); printf("%d\n", strlen(p)); printf("%d\n", strlen(p + 1)); printf("%d\n", strlen(*p)); printf("%d\n", strlen(p[0])); printf("%d\n", strlen(&p)); printf("%d\n", strlen(&p + 1)); printf("%d\n", strlen(&p[0] + 1)); //二维数组 int a[3][4] = { 0 }; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(a[0][0])); printf("%d\n", sizeof(a[0])); printf("%d\n", sizeof(a[0] + 1)); printf("%d\n", sizeof(*(a[0] + 1))); system("pause"); return 0; }
函数指针数组
顾名思义就是用来存放函数指针的数组
char* (*(*pf[3]))(char*)
这是定义一个函数指针数组。它是一个数组,数组名为 pf,数组内存储了 3 个指向函数的
指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函
数。这念起来似乎有点拗口。不过不要紧,关键是你明白这是一个指针数组,是数组。
转移表
例如我们现在有这样一种情况,如果我们要实现某种模块,而这个模块中有许多函数,而且不同的情况会有不同的函数。如果我们用常规的办法就是通过多种if..else语句。显然如果函数增多这个显然变得不合适,所以我们就诞生了转移表的概念。那他到底怎么使用呢?
例如:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
while (input)
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
printf("输入有误\n");
printf("ret = %d\n", ret);
}
return 0;
}
所以它明显可以提高程序的可读性和可写性,降低圈复杂度。
函数指针数组的指针
char * (*(*pf)[3])(char * p);
看见这个我相信大多数人还是非常懵的,首先我们将(*pf)[3]看成一个整体那么他就是一个返回值为char*类型的指针,参数为char*。
然后我们再看里面就是我们上边给大家提到的数组指针,所以就是函数指针数组的指针。其实函数指针,函数指针数组,函数指针数组的指针,这些都和前边提到过的类型没有多大区别,只不过就是名字看起来比较唬人而已。
回调函数
当我刚开始的时候,不觉得回调函数有多么重要。但是现在才发现回调函数非常重要。因为他可以解决许多问题,比如当我们需要解决同一种问题而不同的类型需求的时候,我们就可以用的回调函数。举个例子
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int less(int a, int b){
return a > b ? 1 : 0;
}
int up(int a, int b){
return a < b ? 1 : 0;
}
typedef int(*Cmp)(int, int);
void Sort(int* a, int size, Cmp cmp){
for (int i = 0; i < size; ++i){
for (int j = i; j < size - 1 - i; ++j){
if (cmp(a[j], a[j + 1])){
a[j] = a[j] ^ a[j + 1];
a[j + 1] = a[j] ^ a[j + 1];
a[j] = a[j] ^ a[j + 1];
}
}
}
}
int main(){
int a[] = { 1, 3, 4.5, 7, 6, 8, 8, 7 };
int size = sizeof(a) / sizeof(a[0]);
Sort(a, size, less);
for (int i = 0; i < size; ++i){
printf("%d", a[i]);
}
system("pause");
return 0;
}
例如这个冒泡排序算法,如果我们想采用两种排序方法,显然使用函数指针回调将使得问题显得简单,可维护。