1.字符指针变量
有一种指针类型是字符指针:char *
一般的字符指针书写:
//字符指针一般的写法:
int main()
{char ch = 'q';char * c=&ch;*c = 'e';printf("%c\n", ch);return 0;
}
还有一种写法:
int main()
{char* ch = "q";char *pc=&ch;//pc 就是字符指针const char* p = "abcdef"; //例如在X86的环境下,指针变量大小是4字节,是不可能存放的下//"abcdef\0"7个字符的,所以不是将字符串存入p中的,而是将第一个字符的地址存放在p中//顺藤摸瓜就可以找到整个字符串了,其实在上面的描述中,指针变量里面存放的是字符串中首//元素的地址,在这一点上就像是数组printf("%c\n", *p);//输出的结果为 a//1.可以将这个字符串想象成一个字符串数组,但是这个数组是不能修改的,因为是常量字符串//2.当常量字符串出现在表达式中的时候,他的值是第一个字符的地址printf("abcdef[3]=%c\n", "abcdef"[3]);printf("p[3] =%c\n", p[3]);p[3] = 'w'; //出现错误 因为是常量字符串是不可以修改的return 0;
}
上面的代码中的 const char* p = "abcdef"; 是将字符串的首字符放在了指针变量 p 中,并不是将整个字符串放在 变量 p 中。
下面还有一个有趣的代码:
int main()
{char ch1[] = "hello world";char ch2[] = "hello world";char * ch3 = "hello world";char * ch4 = "hello world";if (ch1 == ch2){printf("ch1 and ch2 are same\n");}elseprintf("ch1 and ch2 are not same\n");if (ch3 == ch4){printf("ch3 and ch4 are same\n");}elseprintf("ch3 and ch4 are not same\n");return 0;
}
为什么是这样的输出的结果?
ch1 和 ch2 是两个不同的数组,向内存中申请了两个不同的空间,数组名表示的是首元素的地址,所以 ch1 与 ch2 不相等
在内存中内容相同的常量字符串只会保存一份,ch3 和 ch4 是两个指针变量,但是里面的内容是一样的,所以指向的是同一块地址,所以指向的都是 a 的地址,所以 ch3 和 ch4 是相等的
2.数组指针变量
2.1 数组指针变量是什么?
前面提到的指针数组是一种数组,数组中存放的是指针(地址)。
数组指针变量类比于其他的类型的指针变量:
整型指针变量:int * pa; 存放的是整型变量的地址,进行解引用操作能够指向整型的数据。
浮点型指针变量:char * pc; 存放的是浮点型的变量的地址,进行解引用操作能够指向浮点型的数据。
那么数组指针变量:存放的就是数组的地址,进行解引用操作能够指向数组的数据。
思考一下,下面的两个代码中 p1 和 p2 分别代表什么?
int *p1[10]; // * p1 不加括号,p1 优先和 [] 先结合 int (*p2)[10];
p1 [10] 优先结合,代表一个数组,所以 p1 就是数组,里面有 10 个元素,每个元素是 int * ,p1是 指针数组
p2 是指针,指针指向的是数组,数组里面有10个元素,每个元素的类型是 int,所以 p2 是指向数组的指针,即数组指针
数组指针变量:
int (*pa)[10];
注意:
[ ] 的优先级要高于 * 号的优先级,所以必须加上 () 来保证 p 和 * 先结合。
2.2 数组指针变量怎样初始化?
数组指针变量是存放数组的地址,取出数组的地址:&数组名。
int main()
{int arr[5]={0};&arr;//得到的就是数组的地址reurn 0;
}
将取出的数组的地址存放在数组指针变量中:
int (*parr)[5]=&arr;
对数组指针类型进一步剖析:
int ( *p )[5] 的剖析:((*p))p是数组指针的变量名,代表是一个指针变量,指向的是一个数组,( [ 5 ] )里面有5个元素,( int )每个元素类型是整型
3.二维数组的传参本质
基于数组指针的理解,进一步的来了解一下二维数组传参的本质:
过去我们对二维数组传参时,写法如下:
//二维数组传参,形参写的是二维数组
//写法1:
void Print(int arr[3][5], int r, int c)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){printf("%d ", arr[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} };Print(arr, 3, 5); //打印arr数组的内容return 0;
}
另外一种二维数组传参的写法:
//写法2:
void Print(int (*p)[5], int r, int c)//传过来的是首元素的地址,而二维数组的首元素的地址是一行一维数组//所以要用数组指针变量来接收传过来的一行一维数组地址
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){printf("%d ",*(*(p+i)+j));//*(p+i)=arr[i]//*(*(p+i)+j)=arr[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} };Print(arr, 3, 5); //打印arr数组的内容return 0;
}
结论:
二维数组传参,形参的部分可以写成数组(便于理解,数组本质上是指针),也可以写成指针的形式,因为传参传的是地址,需要用指针来接收。
4.函数指针变量
4.1 函数指针变量的创建
什么是函数指针,类比于数组指针,我们就可以很快的理解到,就是存放函数地址的指针变量,之后通过地址也可以来调用函数的。
函数有地址吗?
测试代码如下:
void test()
{printf("hello!\n");
}int main()
{printf("test: %p\n", test);printf("&test: %p\n",&test);return 0;
}
上述的两个输出结果是有一样的,
函数名:函数的地址
&函数名:函数的地址
4.2 函数指针类型的书写和函数指针变量的使用
int Add(int x, int y)
{return x + y;
}int main()
{int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };int (*pa)[10] = &arr;//数组指针//可以类比于数组指针,写出函数指针//printf("&Add=%p\n", &Add);//printf("Add =%p\n", Add);上述的两个输出结果是有一样的,函数名:函数的地址&函数名:函数的地址//想把函数的地址存放起来:int (*pc)(int, int) = &Add;//pc 就是函数指针变量,存放的是函数的地址,* pc 后面的(int,int)代表的是两个参数各自的类型int ret1 = Add(3, 5);//之前调用函数的方法printf("%d\n", ret1);int ret2 = (*pc)(8, 9); //(*pc)--对pc变量解引用找到那个函数,(8, 9)进行传参printf("%d\n", ret2);因为上述的两个输出结果是有一样的,函数名:函数的地址&函数名:函数的地址//所以:int(* pc2)(int, int) = Add;int ret3 = (*pc2)(1, 6);printf("%d\n", ret3);//函数调用时,Add( )前面是函数名,函数名存放的是地址,当我们不解引用,直接用地址去调用也是可以的int ret4 = pc2(1, 6);printf("%d\n", ret4);return 0;
}
函数指针类型的解析:
int (*pf) ( int x , int y)
int 是 pf指向函数的返回类型,pf 函数指针变量名,( int x , int y) 是 pf 指向函数的参数类型和个数,其中的 x 和 y 可以省略掉
4.3 两个有趣的代码
代码1:
(*(void (*)())0)();
void (*)() 是函数指针类型,参数为空,返回类型是 void(void (*)())0 <===> (类型)0 --->强制类型转换,转换为存放地址的指针变量*(void (*)())0 进行解引用操作,结果应该是一个函数(*(void (*)())0)();--->函数的调用
void (* signal(int, void(*)(int) ) )(int);
signal是一个函数
signal函数的参数有2个,第一个是int类型
第二个是函数指针类型,该指针指向的函数参数是int,返回类型是void
signal函数的返回类型是这种类型的void(*)(int)函数指针
该指针指向的函数参数是int,返回类型是void
4.3 typedef 关键字
typedef 是用来类型的重命名的,将复杂的类型简单化
typedef unsigned int uint; //将 unsigned int 重命名为 unit//数组指针类型的重定义
//typedef int (*)[10] pArr_t; //error
typedef int (*pArr_t)[10] ;//函数指针类型的重定义
//typedef int (*)(int,int) pf_t; //error
typedef int (*pf_t)(int,int);int main()
{unsigned int n1;uint n2;pArr_t pa;int (*pb)[10];pf_t pf;int (*pl)(int, int);return 0;
}
5.函数指针数组
6.转移表