文章目录
- 一、字符指针
- 1.1字符指针的创建
- 1.2字符数组的地址和使用
- 1.3例题分析
- 二、数组指针
- 2.1什么是数组指针?
- 2.2数组指针的初始化
- 三、二维数组传参的本质
- 四、函数指针
- 4.1函数指针的创建
- 4.2函数指针变量的使用
- 4.3两端有趣的代码解析
提示:以下是本篇文章正文内容,下面案例可供参考
一、字符指针
在c语言中,我们把存放字符变量的地址的变量,称为字符指针
1.1字符指针的创建
int main()
{char ch = 'c';char* p = &ch;return 0;
}
代码分析
在这里我们创建一个字符型变量ch,存储字符c,取出字符型变量的地址放入变量p中,此时p的类型就是字符型指针变量。
1.2字符数组的地址和使用
int main()
{char arr[] = "abcdef";char* p = arr;return 0;
}
代码分析
在这里我们创建了一个字符数组,数组中存放一个字符串,通过小编前面所讲述的内容,除了&arr和sizeof(arr),其他时候的数组名都代表数组首元素的地址,因为数组在内存中是连续存放的,取出数组首元素的地址也就是取出数组的地址。
通过上面我们是否可以采取下面方法使用呢
char* p = "abcdef"
;
在这里我们是不是直接将abcdef 赋值给字符指针p,答案是错误的,abcdef也是和数组一样的,它只是把字符a也就是首元素的地址给了字符指针变量p的。与数组相比,它是把字符串的首元素赋值给p。那么两者是否存在不同。接下来我们对他进行解引用打开vs调试一下。
错误分析
在这里出错的原因是p1中存放的是数组的地址,而p2中存放的则是常量字符串的地址。数组的内容是可变的,而常量字符串的内容是不可变的。因此对p2的解引用是错误的。此外数组创建需要开辟一份空间,这份空间是存储在栈区的,而常量字符串是不可变的,就是因为它是不可变的,所以我们把他放在代码段中,想要用的话直接从代码段中去拿。
1.3例题分析
#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char *str3 = "hello bit.";const char *str4 = "hello bit.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}
分析
这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域,当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
二、数组指针
2.1什么是数组指针?
在前面内容我们学习了很多类型的指针,整型指针(存放整型变量的地址),字符指针(存放字符变量的地址)等等,那么什么是数组指针呢?没错数组也有它的地址,存放数组的地址就是数组指针。讲到数组,这里需要区分一下数组指针和指针数组。
定义3个整型数组
int arr1[5] = {1,2,3,4,5};
int arr2[5] = {2,3,4,5,6};
int arr3[5] = {3,4,5,6,7};
指针数组
什么叫指针数组呢?指针数组是一个数组,存放指针的数组
int* arr[3] = {arr1,arr2,arr3};
在这里我们把三个整型的数组名放入arr中,数组名是地址,所以arr数组中的三个元素都是指针,指针变量的类型是int*类型的。所以如上面代码所写,[ ]的优先级高于*的优先级,所以arr先与[ ]结合代表arr是一个数组,数组中有三个元素,元素的类型是int*类型的。
数组指针
数组指针是一个指针,存放数组的地址。
int (*p)[5] = &arr1;
在这里我们把整个数组的地址取出来,地址是指针,指针指向的是一个数组,数组元素有5个,元素的类型是int,所以先将p与*结合括起来,代表p是一个指针,然后p再与[ ]结合说明指针变量p指向的是一个数组,里面的5表示指向的这个数组有5个元素,int表示指向的数组内的元素是int类型。
2.2数组指针的初始化
数组指针变量就是存放数组的地址,那么给数组指针进行赋值,那么就要给出数组的地址,数组的地址怎么求呢?也就是我们前面所写的 &数组名。
int arr[10] = {0};
&arr;//得到的就是数组的地址
如果需要存放整个数组的地址,那么就需要将它存入数组指针当中
int(*p)[10] = &arr;
三、二维数组传参的本质
在过去我们学习二维数组进行传参的时候是直接写数组形式的
那么二维数组传参是否可以写成其他形式的呢?在二维数组中,我们讲到过二维数组由许多个一维数组组成,也就是说二维数组可以看成每个元素是一维数组的数组。所以二维数组首元素就是第一行,也就是一个一维数组。根据数组名就是数组首元素的地址,所以二维数组的数组名就是第一行的地址,因为第一行是一维数组,所以二维数组首元素地址的类型是数组指针,
所以二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。
二维数组传参写成指针的形式
#include <stdio.h>
void print(int (*p)[5], int x, int y)
{int i = 0;for (i = 0; i < x; i++){int j = 0;for (j = 0; j < y; j++){printf("%d ",*((*p+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);return 0;
}
代码分析
在这里二维数组传参也就是把第一行数组传参进去,也就是一维数组的地址,数组元素类型是int,元素个数有5个,所以传参进行接收的形参类型是int (p)[5],后面((*p+i)+j)解读是:&和*是可以相互抵消的,p就是&arr的地址,*p则就是arr数组名也就是数组首元素的地址,arr数组首元素就是一维数组,所以通过解引用p+i可以访问1到3行的地址,再通过下标访问1到3行内的元素。
四、函数指针
在前面我们讲解了,整型指针就是存储整型变量地址的变量,那么函数指针就是存储函数地址的变量
4.1函数指针的创建
函数指针变量是用来存储函数地址的,那么函数的地址是什么呢?我们通过一段来代码来分析一下
int add(int x, int y)
{return x + y;
}
int main()
{printf("%p\n", add);printf("%p\n", &add);return 0;
}
代码分析
在这里我们分别打印函数名的地址,和把函数的地址取出来进行打印,发现结果相同,因此函数名就是函数的地址,与数组不同的是,函数名代表函数的地址,而数组名代表数组首元素的地址
,而我们需要存储函数地址,则就需要函数指针变量进行存储。
函数指针变量的创建
int add(int x, int y)
{return x + y;
}
int main()
{int(*pf)(int, int) = &add;//int(*pf)(int x, int y) = &add; //在这里x和y都可以省略return 0;
}
代码分析
在这里我们取出加法函数add的地址,我们把他放入函数指针变量pf当中,所以pf是一个指针,又因为他是一个函数,函数调用操作符()的优先级大于*所以代表指针变量pf的*要加上一个括号代表它是一个指针变量。后面接一个括号代表指针变量pf指向的是函数,函数的参数有两个,参数类型都是int类型,最前面的int代表函数的返回参数是int类型。
函数指针类型解析
定义一个变量的时候,我们都是一个类型加上一个变量名进行定义,所以我们想要得到一个变量的类型,我们只要去掉变量名即可,
因此指针变量pf的变量类型就是 int( * )(int , int)
4.2函数指针变量的使用
int add(int x, int y)
{return x + y;
}
int main()
{int(*pf)(int, int) = &add;printf("%d\n", add(3,4));printf("%d\n", (*pf)(3, 4));printf("%d\n", pf(3, 4));return 0;
}
在这里我们之前在函数学习中,调用函数都是直接函数名加上要传递的参数,在这里我们有讲解了函数名就是函数地址,pf指针就是函数地址,所以pf函数指针变量可以直接调用函数,当然对函数指针变量进行解引用,也就是拿到了函数的地址指向的函数,再去使用函数也是可行的。
4.3两端有趣的代码解析
代码段1
( * ((*pf)(3, 4)) 0 ) ();
在这里我们看到这段冗长的代码就感到头疼,首先我们先找到熟悉的0,0前面用括号阔着一个void ()(),这个不就是函数指针变量类型么,变量类型用括号阔着不就是强制类型转换么,所以(void ()()) 0 的意思就是将int类型的0强制类型转化成函数指针类型,函数指针类型中存储的是函数的地址,那么将0转换成函数指针类型,不就是将0的地址放入到函数指针类型么,此时函数的地址就是0所在的地址。
此时通过0地址处放着无参,返回类型是void的函数
所以前面的*也就代表着通过0这个地址调用这个函数。和前面函数指针的使用是一样的(*pf)(3, 4)。