【pointer to c】指针1:引入指针、 *和&运算符 、 指针类型

本文详细介绍了C语言中的指针概念,包括内存地址的理解、*和&运算符的作用,以及不同类型指针的声明和使用。通过示例展示了指针如何存储地址和间接寻址,以及类型转换对指针值的影响。警告读者注意野指针可能导致的潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们学习C语言时,会不可避免的接触到指针,那么指针是干什么的?首先我们要引入内存的概念,正在运行的程序存储在内存中,那么我们想知道:对于一个变量/常量,到底存储在内存的那个地方,打个比方,我知道一个人住在一座公寓,却不知道他在那一层哪一个房间,这个房间号码我们称之为内存地址

一.*和&运算符

首先我们来看看这个程序,大家看看输出结果:


int main()
{
	int i = 10;
	char c = 'c';
	float f = 3.14;

	int* pi = &i;
	char *pc = &c;
	float *pf = &f;
	pri+ntf("the value of i is %d\n", i);
	printf("the value of c is %c\n", c);
	printf("the value of f is %f\n", f);
	printf("\n");

	printf("the address of i is %p\n", &i);
	printf("the address of c is %p\n", &i);
	printf("the address of f is %p\n", &i);
	printf("\n");


	printf("the value of pi is %p\n", pi);
	printf("the value of pc is %p\n", pc);
	printf("the value of pf is %p\n", pf);
	printf("\n");


	printf("the address of pi is %p\n", &pi);
	printf("the address of pc is %p\n", &pc);
	printf("the address of pf is %p\n", &pf);
	printf("\n");



	printf("the value of *pi is %d\n", *pi);
	printf("the value of *pc is %c\n", *pc);
	printf("the value of *pf is %f\n", *pf);
	printf("\n");



	return 0;
}

用VS2022编译的结果是:
在这里插入图片描述
我们暂时将int * pi = &i; 这句话理解为:有一个变量,叫做pi,它的类型是int * ,我们用 i 的地址给它赋值。其实这句话是声明了一个指向int类型的指针变量叫做pi,用 i 的地址给它赋值。
注意:多个指针的声明形式是下面第一行,而非第二行:

	int* p1, * p2, * p3;
	int *p1, p2, p3;//指针的声明从右向左读,例如:p1是指针,指向一个int类型;这里p2,p3都是int类型,不是指针

所以我们会看到上面的结果:pi的值与i的地址相同。
那么下面我们看到这句话:the value of *pi is 10 ,你可能感到奇怪,我们的程序似乎并没有给这个叫*的变量赋值,但是我们看到*pi的值是10,正好与i的值相等,*pc,*pf类似。
下面我们介绍& 取地址运算符 &i 显然是取 i 变量所在的内存地址
* 间接寻址运算符 *pi 是将pi存的地址所对应的值取出。即从地址0000009D4A72F9C8从低到高开始取int(32位系统是4个字节)大小的空间,将存储的值返回,也就是i 的值返回。(如果没看懂请看本文下一节)
注意:在32系统中,指针占4个字节,在32系统中,指针占8个字节。
简单理解:指针存地址,星号(间接寻址运算符)取空间,空间取多大,请看星(指针声明的位置或者使用时前面有强制类型转换)前面。

下面截取的《c和指针》一书的截图更好的说明这一点:
在这里插入图片描述
在这里插入图片描述
假设上面的程序在32位系统运行,那么d和e都是占4个字节,d里面存a的地址,e存c的地址。

指针类型

刚才我们演示的程序,都是int类型的地址赋给指向int类型的指针 ,char类型的地址赋给指向char类型的指针 ,float类型的地址赋给指向float类型的指针.那么我们不妨试试类型转换:
比如下面的例子:

	int i = 10000;
	int  *pi1 = &i;
	char* pi2 = (char*)pi1;
	char* pi3 = pi2 + 1;
	printf("the value of *pi1 is %x\n", *pi1);
	printf("the value of *pi2 is %x\n", *pi2);
	printf("the value of *pi3 is %x\n", *pi3);

在这里插入图片描述
可以看到,我们的pi1,pi2,pi3 所存储的地址都相同(0x000000654a4ff6d4),但最终pi1和pi2的值不同。我们看到变量i 在内存中存储的东西是:
(按字节从高到低排序)00 00 27 10

  • *pi1是从0x000000654a4ff6d4开始取4个字节,结果就是00 00 27 10
  • *pi2是从0x000000654a4ff6d4开始取1个字节,结果就是 10 *pi3是从0x000000654a4ff6d5开始取1个字节,结果就是27

指针 + - ,就是指针的起始地址 + - 一个指向类型大小。比如说这里char* pi3 = pi2 + 1; 就是将pi2起始地址+一个char类型的大小(一个字节)
0x000000654a4ff6d4+0x0000000000000001=0x000000654a4ff6d5(前缀0x表示数字是16进值)(后面讲到数组时还会再次演示)
在这里插入图片描述
简单理解:指针存地址,星号(间接寻址运算符)取空间,空间取多大,请看星(指针声明的位置或者前面有强制类型转换)前面。
再看一个程序:


	int i = 1000000;
	int  *pi1 = &i;
	char* pi2 = (char*)pi1;
	char* pi3 = pi2 + 1;
	char* pi4 = pi2 + 2;
	char* pi5 = pi2 + 3;
	char* pi6 = pi3 + 1;
	long * pl=(long*)pi3;
	printf("the value of *pi1 is %x\n", *pi1);
	printf("the value of *pi1 is %x\n", *((char*)pi1));
	printf("the value of *pi2 is %x\n", *pi2);
	printf("the value of *pi3 is %x\n", *pi3);
	printf("the value of *pi4 is %x\n", *pi4);
	printf("the value of *pi5 is %x\n", *pi5);
	printf("the value of *pi6 is %x\n", *pi6);
	printf("the value of *pl is %x\n", *pl);

	pi2++;
	printf("the value of *pi2 is %x\n", *pi2);


以下是pi2++;之前的调试结果:

在这里插入图片描述

以下是pi2++;之后的调试结果:pi2++;将pi2的起始地址+一个char类型的大小。
在这里插入图片描述
注意到 * pl的值了吗? 0xcc000f42 ,前面一个字节 cc ,我们从没有使用过,但是我们误打误撞访问了这个未知的字节,这种情况我们要尽量避免,以免程序运行出现我们意想不到的bug。至于如何避免,在将野指针的时候再提。在这里插入图片描述

参考文献:
《c和指针》(译自《pointer to c》)【美】Kenneth A. Reek著 徐波译
《彻底搞定C 指针》姚云飞
《C语言程序设计现代方法》【美】K.N.King著 吕秀峰译

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值