简体   繁体   English

指向静态分配对象的指针

[英]Pointers to statically allocated objects

I'm trying to understand how pointers to statically allocated objects work and where they can go wrong. 我试图理解静态分配对象的指针是如何工作的以及它们可能出错的地方。

I wrote this code: 我写了这段代码:

int* pinf = NULL;
for (int i = 0; i<1;i++) {
    int inf = 4;
    pinf = &inf;
}

cout<<"inf"<< (*pinf)<<endl;

I was surprised that it worked becasue I thought that inf would dissapear when the program left the block and the pointer would point to something that no longer exists. 我感到很惊讶,因为我认为当程序离开块并且指针指向不再存在的东西时, inf会消失。 I expected a segmentation fault when trying to access pinf . 我想在尝试访问pinf时出现分段错误。 At what stage in the program would inf die? 在程序中的什么阶段会inf死吗?

Your understanding is correct. 你的理解是正确的。 inf disappears when you leave the scope of the loop, and so accessing *pinf yields undefined behavior. 离开循环范围时, inf消失,因此访问*pinf产生未定义的行为。 Undefined behavior means the compiler and/or program can do anything, which may be to crash, or in this case may be to simply chug along. 未定义的行为意味着编译器和/或程序可以执行任何操作,这可能是崩溃,或者在这种情况下可能只是简单地进行操作。

This is because inf is on the stack. 这是因为inf在堆栈上。 Even when it is out of scope pinf still points to a useable memory location on the stack. 即使它超出范围, pinf仍然指向堆栈上的可用内存位置。 As far as the runtime is concerned the stack address is fine, and the compiler doesn't bother to insert code to verify that you're not accessing locations beyond the end of the stack. 就运行时而言,堆栈地址很好,并且编译器不会费心插入代码来验证您是否没有访问超出堆栈末尾的位置。 That would be prohibitively expensive in a language designed for speed. 在为速度设计的语言中,这将是非常昂贵的。

For this reason you must be very careful to avoid undefined behavior. 因此,您必须非常小心,以避免未定义的行为。 C and C++ are not nice the way Java or C# are where illegal operations pretty much always generate an immediate exception and crash your program. C和C ++并不像Java或C#那样非常好,非法操作几乎总是会产生立即异常并导致程序崩溃。 You the programmer have to be vigilant because the compiler will miss all kinds of elementary mistakes you make. 程序员必须保持警惕,因为编译器会错过你所犯的各种基本错误。

You use so called Dangling pointer . 你使用所谓的悬空指针 It will result in undefined behavior by the C++ Standard. 它将导致C ++标准的未定义行为。

It probably will never die because pinf will point to something on the stack. 它可能永远不会死,因为pinf将指向堆栈上的某些东西

Stacks don't often shrink. 堆栈通常不会缩小。

Modify it and you'll pretty much be guaranteed an overwrite though. 修改它,你几乎可以保证覆盖。

If you are asking about this: 如果你问这个问题:

int main() {
  int* pinf = NULL;
  for (int i = 0; i<1;i++){
    int inf = 4;
    pinf = &inf;
  }
  cout<<"inf"<< (*pinf)<<endl;
}

Then what you have is undefined behaviour. 那么你所拥有的是未定义的行为。 The automatically allocated (not not static) object inf has gone out of scope and notionally been destroyed when you access it via the pointer. 自动分配(非非静态)对象inf已超出范围,并且当您通过指针访问它时,概念上已被销毁。 In this case, anything might happen, including it appearing to "work". 在这种情况下,任何事情都可能发生,包括它似乎“工作”。

You won't necessarily get a SIGSEGV (segmentation fault). 您不一定会得到SIGSEGV(分段错误)。 inf memory is probably allocated in the stack. inf内存可能在堆栈中分配。 And the stack memory region is probably still allocated to your process at that point, so, that's probably why you are not getting a seg fault. 堆栈内存区域可能仍然分配给您的进程,因此,这可能就是您没有遇到seg错误的原因。

The behaviour is undefined, but in practice, "destructing" an int is a noop, so most compilers will leave the number alone on the stack until something else comes along to reuse that particular slot. 行为是未定义的,但实际上,“破坏” int是一个noop,因此大多数编译器会将数字单独留在堆栈上,直到其他东西出现以重用该特定的槽。

Some compilers might set the int to 0xDEADBEEF (or some such garbage) when it goes out of scope in debug mode, but that won't make the cout << ... fail; 某些编译器在调试模式下超出范围时可能会将int设置为0xDEADBEEF (或某些此类垃圾),但这不会使cout << ...失败; it will simply print the nonsensical value. 它只会打印出荒谬的价值。

The memory may or may not still contain a 4 when it gets to your cout line. 当它到达你的cout线时,内存可能会或可能不会包含4。 It might contain a 4 strictly by accident. 它可能严重意外地包含4。 :) :)

First things first: your operating system can only detect memory access gone astray on page boundaries . 首先要做的事情是:您的操作系统只能检测在页面边界上误入歧途的内存访问。 So, if you're off by 4k or 8k or 16k or more. 所以,如果你的距离是4k或8k或16k或更多。 (Check /proc/self/maps on a Linux system some day to see the memory layout of a process; any addresses in the listed ranges are allowed, any outside the listed ranges aren't allowed. Every modern OS on protected-memory CPUs will support a similar mechanism, so it'll be instructive even if you're just not that interested in Linux. I just know it is easy on Linux.) So, the OS can't help you when your data is so small. (有一天在Linux系统上检查/proc/self/maps以查看进程的内存布局;允许列出范围内的任何地址,不允许在列出的范围之外的任何地址。受保护内存CPU上的每个现代操作系统将支持类似的机制,所以即使你对Linux不感兴趣它也会很有启发性。我只知道它在Linux上很容易。)因此,当你的数据太小时,操作系统无法帮助你。

Also, your int inf = 4; 另外,你的int inf = 4; might very well be stashed in the .rodata , .data or .text segments of your program. 可能很好地存储在程序的.rodata.data.text段中。 Static variables may be stuffed into any of these sections (I have no idea how the compiler/linker decides; I consider it magic) and they will therefore be valid throughout the entire duration of the program. 静态变量可以填充到这些部分中的任何一部分(我不知道编译器/链接器如何决定;我认为它很神奇)因此它们在程序的整个持续时间内都是有效的。 Check size /bin/sh next time you are on a Unix system for an idea how much data gets put into which sections. 在下次进入Unix系统时检查size /bin/sh ,以了解将哪些数据放入哪些部分。 (And check out readelf(1) for way too much information. objdump(1) if you're on older systems.) (并查看readelf(1)以获取太多信息readelf(1) objdump(1)如果您使用的是较旧的系统。)

If you change inf = 4 to inf = i , then the storage will be allocated on the stack, and you stand a much better chance of having it get overwritten quickly. 如果你将inf = 4改为inf = i ,那么存储将被分配到堆栈中,你有更好的机会快速覆盖它。

A protection fault occurs when the memory page you point to is not valid anymore for the process. 当您指向的内存页面不再对该进程有效时,会发生保护错误。

Luckily most OS's don't create a separate page for each integer's worth of stack space. 幸运的是,大多数操作系统不会为每个整数的堆栈空间创建单独的页面。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM