【问题标题】:Why this arr[0] is not equal to arr+0?为什么这个 arr[0] 不等于 arr+0?
【发布时间】:2012-12-05 22:14:38
【问题描述】:

您好,我对以下问题的解释感到困惑,谁能给我解释一下?提前致谢。

#include<stdio.h>
int main(){
    char *arr,c;
    arr = &c;

    arr++;
    *arr = 'a';

    arr++;
    *arr = 'b';

    arr++;
    *arr = 'c';

    arr--;
    arr--;

    printf("\narr 1 = %c",arr);
    printf("\narr 2 = %c",arr[1]);
    printf("\narr 3 = %c",arr[2]);
    getch();
    return 1;
    }

输出是:
arr 1 = 一个
arr 2 = b
arr 3 = c

但如果换行:

printf("\narr 1 = %c",arr);

printf("\narr 1 = %c",arr[0]);

现在输出是:
arr 1 =
arr 2 = b
arr 3 = c

为什么 'a' 没有被打印出来。?

*对于那些质疑程序编码不好的人。我知道使用这样的指针不是一个好的编码习惯,但我的问题是为什么 arr[0] 没有打印任何作为 arr[1] 的东西& arr[2] 正在打印分配的内容?

【问题讨论】:

  • 你有一个更严重的问题,即你只有一个char,但你试图修改3个不同的chars。
  • 未定义的行为,在第一个 arr++ 之后,你指向没有你拥有的内存。
  • 我不明白反对意见。我同意代码非常糟糕。但那家伙尝试了一些东西,但并没有像他预期的那样奏效。恕我直言,这个问题非常合理,不值得反对。
  • @LucM "输出是:argc = a argc = b argc = c" 根本不正确。我不赞成那些声称无稽之谈的问题,因为它们表明提问者缺乏努力(甚至懒得复制/粘贴实际结果)。
  • @EricPostpischil 除了从“argc”更改为“arr 1”等等 - 这正是我的意思。

标签: c


【解决方案1】:

在程序的第一个版本中,可能发生的情况是:

  • 编译器将c 放在堆栈上的某个地址,比如1003(一个字节)。
  • 编译器将 arr 放在堆栈上的某个地址,例如 1004-1007(四个字节)。
  • 语句arr = &amp;c;c的地址1003放入arr中。
  • 语句arr++;arr 加一,所以现在是1004 而不是1003。
  • 语句*arr = 'a';'a' 写入arr 指向的位置。现在这是 C 标准未定义的行为。当 arr 设置为 &amp;c 时,该位置的对象中只有一个字节,并且 C 标准不保证在对象外部增加该地址后写入其他位置时会发生什么。
  • 在本例中,1004 指向arr,因此您将'a' 写入arr 的字节之一。这可怕地改变了arr 中的地址。
  • 然后arr++; 再次增加地址,*arr = 'b'; 在某处写入'b'。我们不知道在哪里,因为arr 的价值不高。
  • 然后arr++; 再次增加地址,*arr = 'c'; 在某处写入'c'。我们不知道在哪里。
  • 然后arr--;arr--; 将地址返回到写入'a' 后的值。
  • 然后第一个printf 调用传递arr 中的值以使用“%c”说明符打印。 “%c”用于打印一个字符,但你传递给它的arr 是一个指针。但是,您之前已将 'a' 写入指针,因此指针的值在其一个字节中包含 'a'
  • 碰巧的是,当“%c”说明符与指针一起使用时,printf 的此实现会打印指针值中的一个字节,在本例中与您写入 'a' 的字节相同。所以打印了“a”。 您不能依赖其他 C 实现中发生的这种情况。 在这种情况下发生这种情况只是因为编译器碰巧将 arr 放在内存中 c 之后,所以语句 *arr = 'a'; 写道'a' 变成 arr。这不会在所有 C 实现中发生。你得到了“幸运”。
  • 'a' 写入arr 后,arr 指向某个地方,虽然我们不知道在哪里。我们称这个位置为x
  • 语句arr++;*arr = 'b';'b' 写入位置x+1
  • 语句arr++;*arr = 'c';'c' 写入位置x+2
  • 然后两个arr--; 语句返回arr 指向x。所以arr[1]x+1 处的字符,即'b'arr[2]'c'。将这些传递给 printf 以使用“%c”打印“b”和“c”。

在程序的第二个版本中,当arr[0]被传递给printf时:

  • arr 的值是通过将'a' 写入其字节之一形成的值。这是一些“随机”地址,指向我们无法控制的任何位置。显然那里有一个 0 字符,或者其他一些非打印字符,因此,当 arr[0] 传递给 printf 时,什么都不会打印。
  • 传递arr[1]arr[2] 打印“b”和“c”,因为代码将'b''c' 写入这些位置。

【讨论】:

  • 好答案 (+1)。请注意,严格来说,只要您执行arr++,即使没有取消引用,行为也是未定义的。见this answer。 (编译器可以并且确实假设您没有做这种事情并相应地进行优化。)
  • @Nemo:第一个arr++ 是安全的。您可以在数组的最后一个元素之外指向一个,并且单个对象充当一个元素的数组。代码中的第一个未定义操作是对该位置的赋值。
【解决方案2】:

这只是未定义的行为 - arr++; *arr = 'a'; 是非法的,因为 arr 指向单个 char。任何事情都有可能发生。

【讨论】:

  • 第一个arr++ 是合法的,因为您可以在数组末尾指向一个(并且单个char 被认为是一个长度为1 的数组,以便进行指针运算)。后续的以及写入*arr = x; 是未定义的行为。
  • @DanielFischer 是的,这就是我将作业包括在内的原因。
  • 这也是未定义的行为:printf("\narr 1 = %c",arr); 因为可变参数函数的参数的实际类型(提升后)与解码的不同。
  • @LuchianGrigore:说“任何事情都可能发生”(根据 C 标准)并不是对“为什么会发生这种情况?”这个问题的回答。 C 标准不是唯一规范计算机行为的规范,观察到的行为发生是有原因的。
  • @LuchianGrigore:给定 ideone.com 使用的编译器规范和运行它的机器,我们当然可以确定为什么会发生这种特定的分段错误。但是为什么要打扰呢?这将如何帮助任何人学习?这个问题中询问的情况很有趣,因为当arr 传递给printf 时会打印“a”。也就是说,进行了完全错误的调用,但打印了“正确”字符。某些事情不仅出了问题,而且以某种方式对正在学习的人产生了误导。这里有受教育的机会。
【解决方案3】:

arr 不等同于arr[0],因为arr[0] 取消引用 数组。正确的等价物是&amp;arr[0]

顺便说一句,你正在做的事情会调用未定义的行为,因为在递增 arr 之后,它指向一个无效的内存位置 - 最初它指向一个 单个字符, 所以你可以'不要假设它之后有任何东西。

【讨论】:

  • 如果之后是arr,那可能会很有趣,因为*arr = 'a' 会更改地址(创建一个野指针),随后的写入不仅会破坏本地,还会破坏内存位置在整个计划中。
  • @H2CO3 好的,如果是这样,即如果 arr 不等于 arr[0]。那么 arr+1 如何打印与 arr[1] 相同的值?
  • @KuntalBasu 关键字是未定义的行为。不保证该程序会做任何有意义的事情。
猜你喜欢
  • 2017-11-06
  • 2010-11-18
  • 2011-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-19
  • 2020-07-10
  • 1970-01-01
相关资源
最近更新 更多