我在理解指针的概念时遇到了一些麻烦,其中一个就是这个:
让我们声明一个整数类型变量n,以及一个指向它的指针* p
int n=23,*p;
现在,
p=&n;
,如果我没有错,请将变量n的地址(例如,3000)分配给p。
因此cout<<p<<" "<<*p;
将分别输出3000和23
我怀疑这是假设我们做了这样的事情:
p=5;
即为设计用于保存记忆位置的变量分配数值,会发生什么?
变量是移动到内存位置'5'(很可能不是),还是指针刚刚转换为'int'并保持值5?我本能尝试这一点,只是弄乱了我的系统记忆让我有了第二个想法。
另外,当我们声明任何变量(假设int为2个字节的空间)时,它是存储在3000,101,2700等随机存储器位置,还是存储在0,2,4等中?并且下一个变量被声明存储在下一个变量中(如3002,103或2702),或者介于两者之间是否存在某种差距?
答案 0 :(得分:4)
在您的示例中,这是编译器错误。
但我认为你想做的是:
int n =23, *p;
p = &n;
//Change the value of p to 3000, p now points to address 3000
p = reinterpret_cast<int*>(3000);
//Check if the address of n has changed
std::cout << "Address of n : " << reinterpret_cast<int>(&n) << std::endl;
正如您在运行此代码时所知道的那样。 n的地址不会改变。
关于你的第二个问题。
是和否:)
如果您将两个变量彼此相邻定义,则可能在内存中彼此相邻。
int a,b,c,d;
char c = 1;
short s = 1;
void* p = nullptr;
int i = 1;
std::cout << "a is at: " << reinterpret_cast<int>(&a) << std::endl;
std::cout << "b is at: " << reinterpret_cast<int>(&b) << std::endl;
std::cout << "c is at: " << reinterpret_cast<int>(&c) << std::endl;
std::cout << "d is at: " << reinterpret_cast<int>(&d) << std::endl;
std::cout << "Char is at: " << reinterpret_cast<int>(&c) << std::endl;
std::cout << "Short is at: " << reinterpret_cast<int>(&s) << std::endl;
std::cout << "Pointer is at: " << reinterpret_cast<int>(p) << std::endl;
std::cout << "Int is at: " << reinterpret_cast<int>(&i) << std::endl;
这种行为导致编译器确定在哪里坚持翻转。它可能存在也可能不存在于彼此之间。如果您想保证它们彼此相邻,请使用数组。
int arr[] = {1,2,3,4,5,6,7};
int * p = &arr[0]; //get address of first element
for(int i = 0 ; i < 7; ++i)
std::cout << "Value at address: " << reinterpret_cast<int>(p+i)
<< " is: " << *( p + i) << std::endl;
答案 1 :(得分:2)
假设您通过添加强制转换
进行编译p=(int *)5;
然后解除引用该指针将是未定义的行为。 它可以做任何事情,但很可能你的程序将中止。它可以在不同的系统上表现不同。
此外,变量或下一个变量的地址也取决于您运行程序的系统。大多数系统使用堆栈作为局部变量,但它们可以向上或向下计数,所以你不知道。
调试和优化构建也可以发出不同的代码。一些编译器在本地数组或变量之后留出空间,因此当您在数组外部写入时,它们可能会产生有意义的错误。但在发布时,他们可能会尽可能地使用内存。
为了使问题更加难以预测,主流操作系统采用了一种称为地址空间布局随机化(ASLR)的技术。
https://en.wikipedia.org/wiki/Address_space_layout_randomization
每次运行程序时,变量获得的基址将被随机化。这样黑客就不会滥用缓冲区溢出并再次计算返回地址,而是注入自己的代码。
所以你不能对变量地址说些什么。您必须检查编译器和操作系统。
答案 2 :(得分:2)
您可以将int转换为指针。这是标准的。并且标准保证如果将指针强制转换为int,则应该获得原始值,前提是不会发生截断。
但解除引用这样的指针是Undefined Behavior。这意味着在通常的实现中,如果您尝试读取未映射或只写地址,或者只是获取不可预测的值,您将获得段错误或内存冲突,因为您在一个您不知道它在哪里的位置读取。
如果你在那里写,那就更糟了,因为你可以覆盖程序中的随机位置。试想一下当你从函数中覆盖返回地址时会发生什么......
唯一真正的用例是当一些特殊硬件寄存器映射到众所周知的地址时。然后你实际上写道:
char *p;
p = 0x60; // say 0x60 is the address of a special register you want to read
char reg_value = *p;
这不能通过标准来定义,因为标准不对底层平台做出任何假设,而是应该为特定硬件平台记录的本地扩展。
答案 3 :(得分:1)
让我们用一个类比......
int x = 3;
int* x_p = &x;
你可以这样看:你现在有一张纸,名为x
,上面写着数字3
。你还有另一张纸,上面写着第一张纸的位置。如何详细完成这一点实际上并不重要,所以我们可以说第二张纸被称为x_p
并且上面写有x
。在这方面,
int y = *x_p;
表示:查看论文x_p
,将其解释为另一篇论文的位置,并将该值写在该论文上,即y
将具有值3
。
现在,如果您在论文x_p
上写下其他内容会怎样?首先,如果你仍然试图把它解释为另一张纸的位置,你会失败,或者你只会得到一张随机纸,但不是你要找的纸。其次,这对第一张纸x
有何影响?它根本不会影响它。指针只是与其他变量一样的变量,只是你通常将它们的值解释为其他变量的位置,但是否则指针和指针对象之间没有连接。
不是最好的比喻,但也许有帮助。
答案 4 :(得分:-1)
是的,你是对的&amp;是地址运营商。它给出了一个指向它的指针。
如果您输入带星号的p表示您希望该指针指向的值。
Integer从内存中获取4个字节,因此每次创建Integer时,它都会从内存堆栈中获取4个字节。它可以是递减或递增顺序。
你也可以在你的ide上尝试所有这些。
int n = 23,*p;
int n2 = 24;
int n3 = 25;
p = &n;
printf("%d %d %d %d %d",p,n,*p,&n2,&n3);