一篇文章了解指针变量

2023-12-13 23:44:00

字符指针变量

在指针的类型中我们知道有一种指针叫做字符指针

它的使用情况如下:

#include<stdio.h>
int main()
{
	char pa = 'w';
	char*p1=&pa;
	*p1 = 'a';
	printf("%c\n", *p1);


	return 0;
}

在这段代码当中,我们将‘w’字符的地址传到了p1里面,而p1就是一个字符指针。

除了上面这种使用方法,还有一种关于字符指针变量的使用方法如下:

#include<stdio.h>
int main()
{
	char* p1 = "abced";
	printf("%s\n", p1);

	return 0;
}

有没有感到很好奇,对于这段代码的解释,大家有什么想法吗?

咱们的第一个反应应该是,这个是不是把字符串“abced”放到字符指针p1里面啊,但真实情况不是这样的,这个是将“abced”中的首字母的地址放到了p1的指针变量当中,从而在打印的时候,可以通过p1找到字符串首字母的地址,从而顺藤摸瓜地打印出整个字符串。

下面呢,我们来看一段代码,这段代码是一道经典的面试题,来自《剑指offer》

#include<stdio.h>
int main()
{
	char arr3[] = "hello,bit.";
	char arr4[] = "hello,bit.";
	char* p1 = "hello,bit.";
	char* p2 = "hello,bit.";
	if (arr3 == arr4)
	{
		printf("arr3 and arr4 are the same\n");
	}
	else
		printf("arr3 and arr4 are not same\n");
	if (p1 == p2)
	{
		printf("p1 and p2 are the same\n");
	}
	else
		printf("p1 and p2 are not same\n");
	return 0;
}

下面是这段代码运行的结果

这个结果大家想到了吗??

其中的原理如下:

在数组中,用相同的常量字符串初始化数组时,系统会开辟不同的内存空间。

而在指针当中,两个指针指向的是同一个常量字符串,也就是指向同一个开辟下的内存空间。这个就是上面答案的原理所在。

数组指针变量

前面我们讲了指针数组,指针数组是一个数组,存放的是指针(地址),而数组指针是一个指针,存放的是数组的地址,看下面两个:

//指针数组和数组指针
//1.int*arr[10]
//?
//2.int(*arr)[10]

大家可以看看这两个,哪个是指针数组,哪个是数组指针。

很明显,第一个是指针数组,数组名是arr,数组中存放有10个元素,每个元素是int*类型

然后第二个是一个数组指针,根据优先级考虑,在这个当中,首先arr应该与*结合,构成一个指针,然后指向的是一个10个元素的数组,数组中的每个元素都是int类型

数组指针变量的初始化

数组指针是一个指针,存放的应该是数组的地址,那我们怎么可以得到数组的地址呢,&arr,通过这个便可以得到数组的地址

#include<stdio.h>
int main()
{
	
	int arr[10] = { 0 };
	int(*pte)[10] = &arr;

	return 0;
}

?

从这个当中我们可以看到,&arr和pte的地址是相同的,二者指向了同一块内存空间,这个就是数组指针变量的初始化。

二维数组传参的本质

有了前面的数组指针变量的基础,我们就可以好好地了解一下二维数组传参的内容,之前,我们写过二维数组传参的内容,请看下面的代码:
?

#include<stdio.h>
void print(int arr[3][4], int r, int j)
{
	for (int i = 0; i < r; i++)
	{
		for (int h = 0; h < j; h++)
		{
			printf("%d ", arr[i][h]);
		}
		printf("\n");
	}


}
int main()
{
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	print(arr, 3, 4);

	return 0;
}

?我们在之前的代码当中,形参是数组形式,实参也是数组的形式,除了这个写法,我们还有其他的写法吗?

我们再来看二维数组,二维数组其实可以看成是每个元素都是一维数组的数组,然后数组名是数组首元素的地址,也就是说,实参的第一个参数的意思是二维数组第一行的四个元素的地址,所以,我们可以在形参部分写成这样:

#include<stdio.h>
void print(int (*ptr)[4], int r, int j)
{
	for (int i = 0; i < r; i++)
	{
		for (int h = 0; h < j; h++)
		{
			printf("%d ", *(*(ptr+i)+h));
		}
		printf("\n");
	}


}
int main()
{
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	print(arr, 3, 4);

	return 0;
}

在这里,我们在详细讲一个*(*(ptr+i)+h),首先,先看最里面的那个括号,ptr是数组首元素的地址,ptr+i代表着二维数组的第几行的地址,然后再加*找到第几行的元素,也就是arr[i],然后用arr[i]+j代表的是第i行第j列的地址,然后在使用解引用符,就可以找到该地址所代表的元素,然后打印出来,就是数组。

数组名,是数组首元素的地址

&数组名,是数组的地址

函数名,是函数的地址

&函数名,也是函数的地址

二维数组传参,形参可以形成数组形式,也可以写成指针形式。

函数指针变量

通过前面对于指针变量的理解,我们大概可以知道,函数指针变量应该是一个指针变量,指向的应该是函数的地址,那么问题来了,函数有地址吗?让我们来看一下:

#include<stdio.h>
int main()
{
	printf("printf = %p\n", &printf);

	return 0;
}

从上面的结果,我们可以看到,函数是有地址的,函数名就是函数的地址,那我们也可以通过&函数名来获得函数的地址,那我们也可以通过函数地址的调用实现对于函数的调用。

如果我们将函数变量的地址存放起来,就可以创建函数指针了,函数指针其实和数组指针是极其相似的。

void test()
{
	printf("hehe");
}
void (*ptr1)() = test;
void(*ptr2)() = &test;

int Add(int x, int y)
{
	return x + y;
}
int (*ptr3)(int, int) = Add;
int (*ptr4)(int x, int y) = &Add;//加不加 x,y都是可以的

?函数指针类型分析:

函数指针变量的使用

通过函数指针调用指针所指向的函数

#include<stdio.h>
int Add(int x,int y)
{
	return x + y;
}
int main()
{
	int (*ptr)(int, int) = Add;
	printf("%d\n", (*ptr)(3, 4));
	printf("%d\n", ptr(3, 4));
	return 0;
}

?

两段有趣的代码

接下来,我们来看两段出自《C陷阱和缺陷》这本书中的代码:

(*(void(*)())0)()?

//第一段:
//(*(void(*)())0)()
/*在这段代码当中:
* 第一步:void(*)()这个是函数指针的类型
* 第二步:(void(*)())0----这个是对0的强制类型转换,使0转换成函数指针类型
* 第三步:(*(void(*)())0)()----是一个指针
*/

第二段:

第二段:
void (*signal(int, void(*)(int)))(int);
第一步:signal是一个函数名
第二步:函数名后面的(int, void(*)(int))是函数参数
第三步:整个又是一个函数指针,参数类型是int

typedef关键字

typedef是关键字没在C语言当中可以起到重命名的作用

?

typedef int* unit;
typedef void(*ptr)(int);
typedef void(*deff)();

函数指针数组

我们之前学了指针数组,同理,函数指针数组是将函数指针放入数组当中,那么这个该怎么实现呢??

//函数指针数组
void (*)()ptr[10] =;?

?

?

?

?

文章来源:https://blog.csdn.net/in_seattle/article/details/134875444
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。