指针与数组(一)

本文详细阐述了指针和数组在内存中的分布方式,解释了指针如何通过*符号访问内存,以及如何正确地使用指针和数组进行数据操作。文章还讨论了数组与指针之间的相互作用,包括如何通过指针或下标形式访问指针和数组。通过实例演示了指针和数组的基本操作和注意事项。

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

问题1:指针的内存分布

double *p

我们知道是定义了一个指针变量p,而在sizeof中也知道是一个 "double*"类型的模子在内存中咔嚓出4个字节的空间,然后将这个空间命名为p,但同时限定这4个字节的空间里只能存储摸个内存的地址,即使你存入别的任何数据,都被他当做地址处理,而且这个内存地址开始的8个字节上只能存储某个double类型的值。对这个空间的访问是匿名的,只能通过指向他的指针变量通过*这个开锁工具来获取它。

问题2 :int *p=NULL与 *p=NULL一样吗?怎样向指定内存赋值?

利用监视窗口监视下面p值

int * p=NULL;

这时候我们可以查看p的值为0x000000000,这个代码的意思就是,定义一个指针变量p ,其指向的内存里边保存的是int类型的数据,在定义变量p的同时将p的值设置为0x00000000,这个过程是初始化,也是我们强调的,在定义时候养成初始化的习惯,因为如果不初始化是很严重的事情,可能p指向的内存会造成非法操作。

而 int*p;

*p=NULL:

这时候定义了一个指针变量,但这时候该变量的值是多少不知道,也就是说现在p保存的可能是一个非法地址(实际上编译器会不让其编译通过的),而第二行是将p指向的内存赋值为NULL,所以设想如果真让你通过那计算机的世界就混乱了,这样你都可以随意去修改任何地方的数据了,并且其实计算机中也有一些地方的地址进行保护的,所以也是不允许你随意的向一个指针变量赋值去操作。那么,应该怎样向指定的内存地址去操作呢?

看例子:

int i=10;

int *p=&i;

*p=NULL;

我们应该可以从这里获取灵感,那就是为了确认某个内存是我们可以操作的,可以先int i一个,然后通过监视窗口看下他是多少,例如我这里是0x12ff7c,那么我们就可以这样操作:

int *p=(int *)0x12ff7c

注意这里是将地址0x1277ff7c赋值给指针变量p的时候必须强制转化。

问题3 :数组的内存布局

看例子

int  a[5];

我们都知道这里是定义了一个数组,该数组包含了5个int型数据,我们可以用a[0],a[1]等来访问数组中的每个元素,但是一定要明白,a[0],a[1]这些都不是元素的名字。

当我们定义 一个数组a时候,编译器根据指定的元素个数和类型分配确定大小的一块内存(这里是4*sizeof(int)),并把这块内存命名为a。a[0],a[1]等都是a的元素并非元素的名字,数组中的每个元素是没有名字的。回顾我们前边讲sizeof时候  sizeof(a[5])会返回4 不出错,这是因为sizeof是关键字,不是函数,函数求值在运行时候,而关键字sizeof求值是在编译时候,虽然不存在a[5]但是这里也没有真正去访问a[5]而是仅仅根据了数组元素的类型来确定其值。

问题4:(重点) &a与 a——a其实是等于&a[0]

一定要注意,虽然你测试&a与a的值是一样的,但是其意义大不相同,先通过下面例子来看

int main(int argc, char* argv[])
{
    int a[5]={1,2,3,4,5};
	int *p=(int*)(&a+1);
	printf("%d,%d,%d\n",*(a+1),*(p-1),sizeof(&a));
	return 0;
}
这里输出的结果是 2,5,20

为什么呢?

这是因为a表示的是数组首元素的首地址,而不是数组的首地址!!!!a作为右值意义与&a[0]是一样的。而&a表示的是数组的首地址。

所以对于这个例子,

&a+1是表示取数组的首地址,而我们知道对一个地址加1加的是这个类型的偏移,前边我们在说数组内存分布时候已经知道,我们是根据元素类型跟大小分配一块确定的内存取名为a,所以这个数组是一整个数据模子,所以对&a加1就是移动到下一个数组处(同时注意这里的类型强制转化),也就是指向了a[5],虽然这里不存在

&(a+1),这里表示的是取首元素的首地址,然后对其地址偏移一个单位,这时候就是移动到了下一个元素即a[1]

*(p-1) 因为这里指向了a[5],而p又是被强制转为int*,所以 *(p-1)指向了a[4]

所以,再次明确强调一下:

a 表示的是数组a的首元素的首地址,与&a[0]意义一样;

&a 表示的是数组a的首地址,是否还记得在sizeof中我们利用宏实现sizeof的我们这样写

#define SIZEOF(var)  ((char*)(&var+1)-(char*)(&var))

#define SIZEOD(type)  ((char*)((type*)0+1)-(char*)((type*)0))

所以明白这里为什么这么写了吧!

问题5: 数组与指针之间的朦胧相似

5.1 以指针或下标形式访问指针

char *p="abcdef";

这里定义了一个指针变量p,p 本身在栈上占有4bytes,p指向的那块内存在静态区,其大小为7,这块内存也没有名字,对这块内存的访问只能是匿名的,比如读取'e',有两种方式

1) 指针形式 *(p+4)

2)以下标形式 p[4],编译器总是把以下标形式的操作解析为以指针的形式,p[4]会这样被解析;先去得p中存储的地址值,而后加上中括号中4个元素的偏移值,计算出新的地址,从而访问,所以实际上下标的形式访问与指针形式是一样的,他还是会将他解析成指针形式。所以这两种方式在访问上没有本质的区别,只是写法的不同罢了。

5.2 以指针或下标形式访问数组

char a[]="123456"; 定义了一个数组a,a拥有7个char类型的元素,其空间大小为7,数组a本身在栈上。对于a的元素的访问必须先根据数组名a首元素的首地址,然后加上偏移量找到对应的值,这是一种”具名+匿名“访问。现在我们要读取字符'5'.依旧可以有指针域下标的两种形式

1)指针形式: *(a+4);  a这时候代表数组首元素的首地址,然后加上4个字符的偏移量,这样就得到了‘5’所在的地址,再用*取出该位置上的值。

2)以下标形式 a[4] 这种应该是我们最常用的一种方式,编译器总是把以下标形式的做操作解析为以指针的形式的操作,所以a[4]的操作会被解析为:a是数组首元素首地址,加上中括号的4个元素偏移量,计算出新的地址,然后从新的地址中取出值

由上面所述,指针与数组根本是两个完全不同的东西,只是他们都可以以”指针形式“与”下标形式“来访问,


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值