理解数组的概念

时间:2016-09-19 15:58:36

标签: c undefined-behavior

main()
{
    char buffer[6]="hello";
    char *ptr3 = buffer +8; 
    char *str;   
    for(str=buffer;str <ptr3;str++)
        printf("%d \n",str);
}

这里,ptr3指向数组边界。但是,如果我运行这个程序,我将获得连续的内存位置(ex.1000 ..... 1007)。因此,根据C标准,指向多个数组边界的指针明显是未定义的行为。

我的问题是上面的代码是如何导致未定义的行为的?

3 个答案:

答案 0 :(得分:1)

程序中出现多次未定义的行为。

对于初学者,您在没有必需printf的情况下呼叫#include <stdio.h>,而main()应该是int main(void)。这不是你要问的问题,但你应该解决它。

char buffer[6]="hello";

没关系。

char *ptr3 = buffer +8; 

评估表达式buffer +8具有未定义的行为。 N1570 6.5.6指定+加法运算符的行为,第8段说明:

  

如果指针操作数和结果都指向了元素   相同的数组对象,或者超过数组对象的最后一个元素,   评估不得产生溢出;否则,行为   未定义。

计算指针值本身具有未定义的行为,即使您从未取消引用它或访问其值。

char *str;   
for(str=buffer;str <ptr3;str++)
    printf("%d \n",str);

您将char*值传递给printf,但%d需要int类型的参数。将错误类型的值传递给printf也具有未定义的行为。

如果要打印指针值,则需要编写:

printf("%p\n", (void*)str);

可能会以十六进制打印指针值,具体取决于实现方式。 (我删除了不必要的尾随空格。)

str指向buffer[5]时,str++有效;它会导致str指向buffer的末尾。 (在此之后取消引用str会有未定义的行为,但是你不这样做。)之后再次增加str具有未定义的行为。比较str < ptr3也有未定义的行为,因为ptr3具有无效值 - 但您在初始化ptr3时已经触发了未定义的行为。所以这只是锦上添花的结果。

请记住,“未定义的行为”意味着C标准没有定义行为。这并不意味着程序将崩溃或打印错误消息。实际上,未定义行为的最坏结果是代码似乎“有效”;这意味着你有一个错误,但诊断和修复它会很困难。

答案 1 :(得分:0)

您正在查看指针的地址。如果您想要该值,则需要在*中使用取消引用(printf)运算符。

另一方面,如果你想看到字符而不是ASCII码,你应该在%c中使用printf

printf("%c\n",*str);

答案 2 :(得分:0)

在C中,您始终可以添加两个数字。您始终可以向指针添加整数,或减去两个指针。您将始终获得“答案”:编译器将生成代码并执行代码。这无法保证答案有效,有用,甚至定义

C标准定义了语言。在语法允许的范围内,它定义了什么是有效的 - 什么是肯定的东西 - 什么不是。当您在这些行之外着色时,编译器可能会产生奇怪的代码或没有代码。在C中,编译器的工作不是预测每个奇怪的情况并得出合理的答案。编译器编写者假定程序员知道规则,并且不需要验证他是否遵循它们。

有许多有效语法的例子没有意义或未定义。在数学中,你不能记录负数,也不能除以零。除以零不会产生零或不为零;该操作是 undefined

在您的情况下,ptr3有一个正确计算的值,比buffer大8。这是一些指针算法的结果。到现在为止还挺好。

但仅仅因为你有一个指针,并不意味着它指向任何东西。 (void*) 0明确保证不会指向任何内容。同样,您的ptr3并未指出任何内容。它甚至不需要大于buffer的值8。 C标准的6.5.6节定义了向指针添加整数的结果,并将其置于这样:

  

如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出;否则,行为未定义。

当你说,我得到连续的记忆位置(对于ex.1000 ..... 1007),你所看到的就是行为。你必须看到一些行为。而且这种行为是不确定的。根据标准,您可以看到一些其他行为,例如回绕到1000或0。

编译器接受的内容和标准定义的内容是两个不同的东西。