问题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个元素偏移量,计算出新的地址,然后从新的地址中取出值。
由上面所述,指针与数组根本是两个完全不同的东西,只是他们都可以以”指针形式“与”下标形式“来访问,