欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > 指针(3)

指针(3)

2025/4/27 17:59:33 来源:https://blog.csdn.net/2302_80045891/article/details/147375487  浏览:    关键词:指针(3)

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.转移表

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词