C语言——指针与地址的关系

1、通过指针详细解释数据在内存中是如何存储的

#include <stdio.h>
#include <stdlib.h>
int main()
{
	unsigned int a[5]={0x12345678,0x89ABCDEF,0xAF3D1596,0x424AD841,0x5638ECFB};
	unsigned int *p = a;
	unsigned char i;
	for(i=0; i<sizeof(a)/sizeof(a[0]); i++) //sizeof(a)是数组a的总大小,除以每个数组元素的大小,即得元素个数
	{
		printf("&a[%d] = 0x%p---->a[%d] = 0x%08X\n",i,&p[i],i,p[i]);
	}
	return 0;

在这里插入图片描述
可以看出数组a中元素在内存中存储方式是从低地址开始的,如下图方式:

  • 结论一
    上述虽然可以看出数组中的元素在地址空间中的存储方式,但由于每个数组元素都是unsigned int型,即占四个字节,而每个地址只能放一个字节数据,所以从上述也能看出每个数组元素占用4个地址,那么每个数组元素的四字节数据在连续地址中又是如何存储的呢?下面代码做出解释。
#include <stdio.h>
#include <stdlib.h>
int main()
{
	unsigned int a[5]={0x12345678,0x89ABCDEF,0xAF3D1596,0x424AD841,0x5638ECFB};
	unsigned int *p = a;  //这里p是指向数组元素a[0]的首地址,但通过指针取值(或赋值)时,是直接以a[0]首地址为起点,连续取(赋)四个字节。
	unsigned char *q = a; //这里定义q为unsigned char*,q也是指向数组元素a[0]的首地址,通过指针取值(赋值)也是只取(赋)首地址的一个字节数据,这里编译会出警告,因为数组a中的元素都是四字节数据,即每个元素占四个地址,这样的话q+1不会跳到下一个数组元素,只会跳到下一个地址(或者说是下一个字节)。
	unsigned char i,j;
	for(i=0; i<sizeof(a)/sizeof(a[0]); i++) //sizeof(a)是数组a的总大小,除以每个数组元素的大小,即得元素个数
	{
		printf("&a[%d] = 0x%p---->a[%d] = 0x%08X\n",i,&p[i],i,p[i]);
	}
	printf("\n\n\n");
	for(i=0; i<sizeof(a)/sizeof(a[0]); i++)
	{
		for(j=0; j<4; j++)
			printf("&a[%d%d] = 0x%p---->a[%d%d] = 0x%X\n",i,j,&q[i*4+j],i,j,q[i*4+j]);
		printf("\n");
	}
	return 0;
}
  • 解释编译警告,即下面代码引起的警告:因为&a[0]为unsigned int*型,而q为unsigned char *型,赋值号两边指针类型不同,导致警告。
unsigned int a[5]={0x12345678,0x89ABCDEF,0xAF3D1596,0x424AD841,0x5638ECFB}; 
unsigned int *p = a;
unsigned char *q = a; 

指针变量类型一般和所指向的变量类型一致,就是为了取值和赋值时方便
因为数组a中的数据是4字节数据,即每个数组元素都需要4个地址来存储,正常情况下我们也是定义一个4字节指针变量(这样的指针加一是直接跳4个地址,取值和赋值也是连续取(赋)4个地址的值),来指向存放数组元素的地址,如下图(a)所示;但我们如果想知道每个数组元素在内存中是如何存储的,那么这种指针指向就不能展示出来,上面指针q便是指向单字节的指针变量(这样的指针加一,是指向下一个地址,对它取值或者赋值也是只取(赋)一个地址的值,如下图(b)所示:根据下图及下面的程序输出结果结合便能理解


  • 结论二
    四字节数据在地址中存储方式为小端模式,即低字节存储在低地址中。

2、指针与地址的区别

指针与地址最大的区别就是,指针可以自增,地址不能自增;可以对指针进行取值,若想对地址(这里指立即数,例0x45FE)进行取值,则必须把地址强转为指针类型再进行取值

  1. 不同类型指针指向同一变量,然后对指针取值(下面代码编译会出警告,原因上面已解释)
#include <stdio.h>
#include <stdlib.h>
int main()
{
	unsigned int b = 0x4567ABCD;
	unsigned int *p = &b;
	unsigned short int *q = &b;
	unsigned char *h =&b;
	int i;
	printf("&b = 0x%p---->b = 0x%X\n",&b,b);
	printf("p = 0x%p---->*p = 0x%X\n",p,*p);
	printf("\n");
	for(i=0; i<2; i++)
	{
		printf("q[%d] = 0x%p---->*q[%d] = 0x%X\n",i,(q+i),i,*(q+i));	
	}
	printf("\n");
	for(i=0; i<4; i++)
	{
		printf("h[%d] = 0x%p---->*h[%d] = 0x%X\n",i,(h+i),i,*(h+i));	
	}	
	return 0;
}

在这里插入图片描述

这里定义了三个指针变量,全部指向变量b,每个指针变量中存储的都是变量b的地址(系统在内存中,为变量分配存储空间的首个字节单元的地址,称之为变量的地址),这里是0x65FDFC,但由于指针变量p,q,h的类型不同:对指针变量p进行取值或者赋值时,默认以变量b的地址为起始,取或赋连续的4个字节(这里4个字节是因为指针类型为unsigned int*);同理,对指针变量q进行取值或者赋值时,默认以变量b的地址为起始,取或赋连续的2个字节(这里2个字节是因为指针类型为unsigned short int*);对指针变量h进行取值或者赋值时,默认以变量b的地址为起始,取或赋连续的1个字节(这里1个字节是因为指针类型为unsigned char*)。

注:对不同类型指针进行自增运算时,自增一次所跨越的地址数由指针类型决定,如:unsigned int*类型指针变量,自增一次,跨越的地址数为4;同理unsigned short int*类型指针变量,自增一次,跨越的地址数为2;等等。

  1. 将地址强转为指针类型,对其赋新值
#include <stdio.h>
#include <stdlib.h>
int main()
{
	unsigned int b = 0x4567ABCD;
	int i;
	printf("&b = 0x%p---->b = 0x%X\n",&b,b);
	printf("&b = 0x%p---->b = 0x%X\n",(unsigned int *)(0x65FE18),*((unsigned int *)(0x65FE18)));
	printf("\n");	
	/**对变量b的4个地址分别写值**/
	*((unsigned char*)(0x65FE18)) = 0xEF; 
	*((unsigned char*)(0x65FE18)+1) = 0x59;//指针加1,地址加1
	*((unsigned char*)(0x65FE18)+2) = 0xA8;
	*((unsigned char*)(0x65FE18)+3) = 0x7B;
	for(i=0;i<4;i++)
	{
		printf("&b[%d] = 0x%p---->b[%d] = 0x%X\n",i,(unsigned char*)(0x65FE18)+i,i,*((unsigned char*)(0x65FE18)+i));		
	}
	printf("\n");
	/**对变量b的4个地址分两次写值(即一次写2个地址)**/	
	*((unsigned short int*)(0x65FE18)) = 0x254A;
	*((unsigned short int*)(0x65FE18)+1) = 0x4C94;//指针加1,地址加2
	for(i=0;i<2;i++)
	{
		printf("&b[%d] = 0x%p---->b[%d] = 0x%X\n",i,(unsigned short int*)(0x65FE18)+i,i,*((unsigned short int*)(0x65FE18)+i));
	}
	
	printf("\n");
			/**对变量b的4个地址一次写值(即一次写4个地址)**/	
	*((unsigned int*)(0x65FE18)) = 0xAEF0127D;
	printf("&b = 0x%p---->b = 0x%X\n",(unsigned int*)(0x65FE18),*((unsigned int*)(0x65FE18)));
	printf("\n");
	printf("&b = 0x%p---->b = 0x%X\n",&b,b);
	return 0;
}

在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可可斯蒂彳亍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值