为指针赋值

Assigning values to a pointer

我对指针的概念理解有点困难,其中之一是:
让我们声明一个整型变量 n 和一个指向它的指针 *p.
int n=23,*p;
现在,
p=&n;,如果我没记错的话,把变量n(比如3000)的地址赋值给p。
所以cout<<p<<" "<<*p;会分别输出3000和23。
我的疑问是假设我们做了这样的事情:
p=5; 将一个数值分配给一个用于保存内存位置的变量,会发生什么?
变量是否移动到内存位置“5”(很可能没有)或者指针只是转换为 'int' 并保存值 5?我本来会自己尝试的,只是弄乱了我的系统内存才让我重新考虑。

此外,当我们声明任何变量时(假设 int 有 2 个字节 space),它是存储在随机内存位置,如 3000、101、2700 或其他什么,还是存储在 0,2 ,4,等等?下一个声明的变量是否正确存储在下一个变量中(如 3002、103 或 2702),或者两者之间是否存在某种差距?

假设您通过添加强制转换使其编译

p=(int *)5;

然后取消引用该指针将是未定义的行为。 它可以做任何事情,但很可能你的程序会中止。它在不同系统上的行为可能不同。

此外,变量或下一个获取的地址也取决于您 运行 您的程序所在的系统。大多数系统对局部变量使用堆栈,但它们可以向上或向下计数所以你不知道。

调试和优化构建也可以发出不同的代码。一些编译器在局部数组或变量之后留下 space,这样当您在数组外写入时它们可以给出有意义的错误。但在发布时,他们可能会尽可能地使用内存。

为了让事情变得更加不可预测,主流操作系统有一种称为地址 space 布局随机化 (ASLR) 的技术。

https://en.wikipedia.org/wiki/Address_space_layout_randomization

每次程序运行时,变量获取的基地址都会被随机化。这样黑客就不能滥用缓冲区溢出并在他们注入自己的代码的地方计算 return 地址。

所以不,你不能说任何关于变量地址的一般信息。您必须检查您的编译器和操作系统。

是的,您是对的,& 是地址运算符。它给出了一个指针,它将指向哪个地址。

如果您键入带有星号的 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);

在您的示例中,这是一个编译器错误。

但是我认为您想要做的是:

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;

打个比方....

int  x = 3;
int* x_p = &x;

你可以这样看:你现在有一张纸,叫做x,上面写着数字3。你有另一张纸,上面写着第一张纸的位置。具体如何实现并不重要,假设第二张纸被称为 x_p 并且上面写着 x。在这方面,

int y = *x_p;

的意思是:看纸x_p,将其解释为另一张纸的位置,并取那张纸上写的值,即y的值为3 .

现在,如果您在纸上写其他东西会怎样x_p?首先,如果你仍然试图将它解释为另一张纸的位置,你就会失败,或者你只会得到一张随机的纸,而不是你要找的那张纸。第二,这对第一张纸x有什么影响?它根本不影响它。指针和其他变量一样只是变量,只是您通常将它们的值解释为其他变量的位置,否则指针和指针对象之间没有任何联系。

不是最好的类比,但也许有帮助。

您可以将 int 转换为指针。这是标准的。并且标准保证如果你将指针转换回一个 int 你应该得到原始值,前提是两者之间没有发生截断。

但是取消引用这样的指针是未定义行为。这意味着在常见的实现中,如果您尝试读取一个未映射或只写的地址,您要么会遇到段错误或内存冲突,要么只是因为您在一个您不知道那里有什么的位置读取而得到一个不可预测的值。 .

如果你写在那里,情况会更糟,因为你可能会覆盖程序中的随机位置。想象一下当您从函数中覆盖 return 地址时会发生什么...

唯一真正的用例是当一些特殊的硬件寄存器映射到众所周知的地址时。然后你实际上写:

char *p;
p = 0x60;    // say 0x60 is the address of a special register you want to read
char reg_value = *p;

这不能由标准定义,因为该标准对底层平台没有任何假设,而是一个本地扩展,应该针对特定硬件平台进行记录。