【问题标题】:int pointer that points to a static char指向静态字符的 int 指针
【发布时间】:2015-02-13 17:21:11
【问题描述】:

考虑这段代码:

#include <stdio.h>

#define main_arg int argc, char *argv[]

int main(main_arg)
{
    static char chr = '5' - '0'; // Dec 5
    int *p = &chr;               // 4 bytes pointer

    printf("Result: %i", *p);

    return(0);
}

Result: 5

为什么我必须将chr 声明为静态才能获得适当的值? 如果我不使用静态,p 将总共读取 4 个字节,接下来的 3 个将是“随机”的。但是如果chr表示的那个字节位于静态内存中,那不会发生吗?

那是未定义的行为,结果是不确定的,它就这样偶然发生了(我一直在尝试)?

【问题讨论】:

  • 那是未定义的行为,结果是不确定的,只是偶然发生的?答案:是的。
  • 静态内存很奇怪。
  • 您正试图从分配的char 变量中读取sizeof int 字节。由于 sizeof int &gt; sizeof char 根据定义,这会产生未定义的行为(根据定义)。在声明 static 时获得正确的打印输出这一事实只是运气(机会问题)。
  • 我会一样的。如果它至少可以显示一次不同的结果。

标签: c pointers memory-management static


【解决方案1】:

在某些系统中,您的代码可能无法运行,主要是因为对齐。在某些 RISC 系统中,char 可以与任何内容对齐,但例如,int 必须与 4 个字节对齐。在这种情况下,如果你尝试像这样解引用指针,某些内存访问保护机制可能会发出中断,整个程序将立即失败。

在您的情况下,假设您在 Linux 中运行,使用 intel cpu,使用 gcc 编译,chr 将驻留在 .data 段中,并且它的所有不相关部分最有可能设置为 0。

但同样,您以错误的方式访问内存,一切都应该发生,因为这是未定义的行为。

提供更多信息,如果您删除static,并在上述环境中运行该程序,您读取的额外值不是“随机”,而是从字面上看是随机的。这称为canary。它是一种内存保护机制来检测堆栈集中攻击。

【讨论】:

    【解决方案2】:

    char 数据类型是一个字节。您正在使用整数点“int *P”指向 chr 的一字节内存位置,但 C 会将其视为四字节位置,因为它认为它是指向 int 的指针,长度为四字节.根据您的结果 5,显然当您将其声明为静态时, chr 的位置恰好后面跟着零,因此结果仅为 5。当它未声明为静态时,它在堆栈上并且只是选择在 chr 的位置之后的内存中的不同值。无论如何,使用 *P 引用它不应该并且会产生不可预测的结果的内存。

    顺便说一句,您的示例将 int* 声明为“*P”(大写 P)并稍后将其引用为“*p”(小写 p),因此此代码不会按原样运行。

    【讨论】:

    • 我知道并且我解决了这个问题。
    • 添加到 Paul 的声明中,如果您执行了 printf ("%d", foo & 0xff),它会起作用,因为您没有查看接下来的三个未初始化的字节。静态只是让它指向一个不同的内存区域,其中内存已初始化为零。
    【解决方案3】:

    当你声明一个静态变量时,它被分配在 .BSS 或 .data 中,并且该区域全部归零,所以即使你已经将 chr 初始化为 5,巧合的是,chr 地址前后的所有字节都是零

    这是一种验证方法。 (在您的系统上可能会有所不同)

    (gdb) x/16w &chr - 16
    0x601030:   0   0   0   0
    0x601040 <chr.2180>:    5   0   0   0
    0x601050:   0   0   0   0
    0x601060
    

    这里是部分。

    06:43:46 ~$ readelf -s a.out

    53: 0000000000601030     0 NOTYPE  WEAK   DEFAULT   24 data_start
    54: 0000000000601041     0 NOTYPE  GLOBAL DEFAULT   24 _edata
    55: 00000000004005e4     0 FUNC    GLOBAL DEFAULT   14 _fini
    56: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    58: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
    59: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    60: 0000000000601038     0 OBJECT  GLOBAL HIDDEN    24 __dso_handle
    61: 00000000004005f0     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    62: 0000000000400570   101 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    63: 0000000000601048     0 NOTYPE  GLOBAL DEFAULT   25 _end
    64: 0000000000400440     0 FUNC    GLOBAL DEFAULT   13 _start
    65: 0000000000601041     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
    66: 000000000040052d    53 FUNC    GLOBAL DEFAULT   13 main
    67: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    

    【讨论】:

    • 这并没有回答问题,虽然这个事实本身是正确的,但这个答案在其上下文中是错误的!
    • 现在有意义吗?
    • 只有没有初始值的数据才会驻留在.bss中。在这种情况下,chr 应该位于 .data
    • @HuStmpHrrr 你是对的......显然数据和 BSS 都归零了......查看我的答案的更新
    • 恕我直言,这不是证明。此外,它仍然是依赖于平台的行为,例如,您会在大端平台上获得 0 0 0 5。更不用说您无法保证在内存中的 chr 变量之后在可访问的内存地址处有 3 个字节。使用int 指针来访问分配给char 变量的内存仍然是未定义的行为。如果幸运的话,它会立即触发内存访问冲突。如果你不走运,那么你只会得到一些意想不到的价值,这将在稍后的程序执行中“造成严重破坏”!!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    相关资源
    最近更新 更多