C/C++中指针,堆栈和寻址空间的理解

关键字: x86, x64,静态内存分配,栈区,寻址空间


问题1:
学C的时候说C语言静态数组一旦声明就不能改变大小,可是想过这是为什么吗?
首先,C语言的静态数组存放再栈区,栈区地址再x86系统中自上而下存,在x64系统中自下而上存,但是内部元素的地址增长都是递增的。
例如对于

int a[2]={1,2};
int b[3]={3,4,5};

在x86编译时,a的首地址大于b的首地址,但是a[0]的地址永远小于a[1]的地址,这也就是为什么可以通过*(a+1)来访问a[1]的原因。 有没有发现定义变量时候的地址增长方向和a内部元素的地址增长方向是相反的。 假如静态变量的长度可变的话会发生什么现象呢? 如何长度可变, 静态数组新增元素的时候,所有元素都要整体移位置,而移动将会覆盖其他变量的存储地址,所以静态数组(栈上数组)的长度必须在一开始就固定


问题2:
另外一点,是调试的debug和release模式的区别。
在debug模式下,定义每个变量前后编译器会自动加入一定长度的空间便于调试。release模式下不是这个问题,但是输出cout变量的时候也会占用一定空间。


问题3:
考虑下面一段代码

int arr[5] = {1,2,3,4,5};
std::cout << arr << std::endl;
std::cout << &arr << std::endl;

毫无疑问,输出的都是arr数组首元素的地址
但是考虑下面的代码

int arr[5] = {1,2,3,4,5};
std::cout << arr +1 << std::endl;
std::cout << &arr +1 << std ::endl;

此时,第一句输出的是数组中第二个元素2的地址,但是第二句输出的是该数组最后一个元素5之后的未使用地址
即arr+1 比arr地址多4个字节,但是&arr比 arr多20个字节。需要注意的是虽然&arr代表的是整个数组大小的指针,但是sizeof(&arr)返回的值和sizeof(arr)仍然是一样的。
结论: 对于数组arr, 数组名arr表示该数组第一个元素的首地址,而&arr表示的是该数组所有元素的首地址,虽然值相同,但是意义不同,注意,只对于栈上的静态数组可以这么做,堆上的数组返回的指针只代表首地址,而不是数组"对象"

再次理解一下,既然arr是指针,那&arr应该是指向指针的指针也就是地址的地址,在这里地址的地址&arr和地址arr是指向同一块内存的。

此外还有一点需要指出,上面说arr是一个指针,那我如果定义一个int *p = arr会有什么问题吗,当然,值依然是一样的,不一样在于意义
看下面代码

int arr[5] = {1,2,3,4,5};
int *p  = arr;
std::cout << arr +1 << std::endl;
std::cout << &arr +1 << std ::endl;
std::cout << ++p << std::endl;  // 这句是正确的,p会指向数组的下一个元素
std::cout << arr++<< std::endl;  // 这句会报错

所以需要指出的是,arr是数组名也是指针但是他是静态指针即指针常量,其指向的对象不可变,永远是指针的首地址,而p是指针变量,既然是变量,那他的指向就可以变来变去,只要数据类型相同就可以。

进一步的,加入我定义p的时候是int * const p = arr呢,这个时候的p和arr已经没有什么区别,都是常量指针,也不能++p或者p++了。

说到const,看另外的两句代码 int * const p=arrint const * p = arr 是一样的,也就是const和int的位置可以互换,他们都修饰p 也就是说p不可以重新赋值了, 也就是不能有 *p=xx, 但是可以用 p=&xx
而另一种情况int * const p const修饰的是p指针本身,所以不能有 p=&xx ,我们也不能把p指向其他的变量,但是我们可以修改arr*p ,即修改p所指向的变量的内容。因为这样并不会影响p指向的地址,即存储的值。

总结一下: 如果const 在p前,则不能修改p但是能修改p; 如果const在p前,则不能修改*p 可是能修改p
也就是说, 当const 修饰p时, 可以改变p的指向,但是原来指向的变量不变,只是解开了与p的指向而已; 当const修饰p时, 可以改变p的值,从而改变指向,但是这个时候本质上是改变了原来指向的变量的值而已,地址不变。

再进一步, const修饰的变量只能由const 修饰的p来指向,即 const int i=1;若要用指针来指向地址,则只能用const int * p , 否则i的值就可以被p修改了。
const int * const p 修饰的指针则既不能修改
p也不能修改 p。


问题4:
再这个基础上行我们再来深入的讨论一下指针的指针

此外对于指针+1的问题,即p为指针,p+1的值是多少的问题,要看p所代表的数据类型,如果p是int型指针,

指针变量大小的问题: 64位系统的地址总线宽度为8个字节,32位系统位4个字节,所以在32位系统上,一个指针占4个字节,但是在64位系统上,一个指针占8个字节

可由以上结论可知的一个推论是,32位系统的内存寻址空间是 2 32 4 G b 2^{32} \approx{4Gb} , 64位系统的内存寻址空间是 2 64 b i t 2^{64} bit

注意: 以上实验在visual studio 2017中进行。


参考:
https://blog.csdn.net/oqqhutu12345678/article/details/52605952/
https://www.cnblogs.com/qiaogaojian/p/5861554.html

发布了71 篇原创文章 · 获赞 27 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/github_34777264/article/details/84565573