【问题标题】:C variable not where I expect to find it in memoryC 变量不是我希望在内存中找到它的位置
【发布时间】:2017-01-18 16:33:11
【问题描述】:

谁能解释为什么打印指向两个整数的指针会导致它们被放置在与字符相关的不同位置。

下面的代码应该打印出从 &a 到 &c 的内存地址(我认为)应该包括定义的两个整数,但它没有,但是当我试图找出它们在内存中的存储位置时(请参阅第二个代码段)它确实按预期在两个字符之间打印它们。

请解释为什么打印 int 指针会影响 int 存储在内存中的字符之间。

这两个代码示例是相同的,只是代码 2 有一个额外的行 printf("\n\n%p,%p\n",&i,&j); 打印两个整数的指针。

编辑:是的,我知道 prinf 格式很难看,但代码只是为了帮助我澄清内存和指针的工作原理,所以我不需要它漂亮

代码1

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv){
    char a='a';
    int i=1;
    int j=2;
    char c='c';
    char *pos;
    for ( pos=&c; pos<=&a; pos++ ){
        printf("%p\t",pos);
    }
    printf("\n");
    for ( pos=&c; pos<=&a; pos++ ){
        printf("%i\t\t",*pos);
    }
}

代码 1 的结果

0x7ffde6321e7e  0x7ffde6321e7f  
99              97      

代码2

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv){
    char a='a';
    int i=1;
    int j=2;
    char c='c';
    char *pos;
    for ( pos=&c; pos<=&a; pos++ ){
        printf("%p\t",pos);
    }
    printf("\n");
    for ( pos=&c; pos<=&a; pos++ ){
        printf("%i\t\t",*pos);
    }
    printf("\n\n%p,%p\n",&i,&j);
}

Code2 的结果

0x7ffc3575616b  0x7ffc3575616c  0x7ffc3575616d  0x7ffc3575616e  0x7ffc3575616f  0x7ffc35756170  0x7ffc35756171  0x7ffc35756172  0x7ffc35756173  0x7ffc35756174  0x7ffc35756175  0x7ffc35756176  0x7ffc35756177  
99              2               0               0               0               1               0               0               0               -4              127             0               97              

0x7ffc35756170,0x7ffc3575616c

【问题讨论】:

  • 第一个程序从不使用ij,所以编译器直接忽略了它们。
  • 两者都是未定义的行为。
  • 尝试将您的程序编译为汇编代码,您会看到@Barmar 所说的:第一个程序只是有 no int varables ij – 编译器发现它们未被引用,因此没有必要,并删除它们。

标签: c pointers memory


【解决方案1】:

您依赖的是 C 标准中未指定的Note 1。无法定义行为。它调用undefined behavior注2

也就是说,您应该始终将%p 的参数转换为void *,因为预期的类型是void *,并且没有针对指针的默认提升。


注 1:

C 没有提及或保证程序中变量/对象的分配顺序。无法保证它们将具有连续的内存位置,无论是增加还是减少。它们完全被允许具有随机内存位置,因此您所相信的理论

 for ( pos=&c; pos<=&a; pos++ )

不成立。 An(y) 实现可以选择放置 (reorder) 变量,但它确实认为合适。对于内存地址在代码中的定义,绝对无法保证的顺序。


注2:

对于关系运算符,引用 C11。第 §6.5.8 章,(强调我的

当比较两个指针时,结果取决于指针中的相对位置 指向的对象的地址空间。如果两个指向对象类型的指针都指向 同一个对象,或者都指向同一个数组对象的最后一个元素,它们 比较相等。如果指向的对象是同一个聚合对象的成员, 指向稍后声明的结构成员的指针比较大于指向成员的指针 在结构的前面声明,以及指向具有较大下标的数组元素的指针 值比较大于指向具有较低下标值的同一数组的元素的指针。所有指向同一个联合对象成员的指针比较相等。如果 表达式P 指向数组对象的元素,表达式Q 指向 同一个数组对象的最后一个元素,指针表达式Q+1比较大于 P在所有其他情况下,行为未定义。

因此,对于您的情况,比较 pos&lt;=&amp;a; 是尝试比较两个两者都不是的指针

  • 指向同一个对象
  • 同一聚合对象的成员
  • 指向数组元素的指针
  • 指向同一联合对象成员的指针

简而言之,它们不在定义的范围内,因此,将它们用作关系运算符的操作数会调用未定义的行为。

【讨论】:

    【解决方案2】:

    局部变量的位置由实现定义。编译器可以按照它认为最好的任何顺序排列它们。

    进行看似无关的代码更改(例如额外的打印语句或更改优化级别)可能会改变编译器布置变量的方式。

    简而言之,您不能依赖内存中任何特定的变量布局。

    【讨论】:

    • 吹毛求疵:在标准中“定义的实现”意味着实现必须记录它所做的事情。这里不是这样。
    【解决方案3】:

    局部变量被放置在堆栈中(或者如果可能的话,在寄存器中并且如果它们的地址没有被引用)。在您的示例中,i 是第一个,j 是第二个本地变量,因此您有 push i, push j - 第二个 &amp;j 的地址是 &amp;i-1

    【讨论】:

    • 很抱歉,但这可能会误导人们。 C 中没有定义固有的“堆栈”。
    • 确实 - “除了静态定义和函数的局部变量提供的堆栈规则之外,该语言没有定义任何存储分配工具;” (C 编程语言——Brian W. Kernighan、Dennis M. Ritchie)
    猜你喜欢
    • 2018-12-06
    • 2015-08-25
    • 1970-01-01
    • 2013-10-16
    • 2012-10-25
    • 1970-01-01
    • 2014-07-30
    相关资源
    最近更新 更多