C:奇怪的弦现象

时间:2011-04-19 15:18:16

标签: c arrays string printf

有人可以解释这种现象。

#include "stdio.h"
#include "stdlib.h"

int main()
{
    char foo[]="foo";
    char bar[3]="bar";
    printf("%s",foo);
    printf("\n");
    printf("%s",bar);
    return 0;
}

结果:

foo
barfoo

如果我更改订单并在foo之前创建栏,我会得到正确的输出。

#include "stdio.h"
#include "stdlib.h"

int main()
{
    char bar[3]="bar";
    char foo[]="foo";
    printf("%s",foo);
    printf("\n");
    printf("%s",bar);
    return 0;
}

结果:

foo
bar

还有一个。

#include "stdio.h"
#include "stdlib.h"

int main()
{

    char foobar[]="foobar";
    char FOO[3]={'F','O','O','\0'};
    char BAR[3]="BAR";
    printf("%s",foobar);
    printf("\n");
    printf("%s",FOO);
    printf("\n");
    printf("%s",BAR);
    return 0;
}

结果:

foobar
FOOfoobar
BARFOOfoobar

11 个答案:

答案 0 :(得分:7)

字符串"bar"长度为四个字符:{'b', 'a', 'r', '\0'}。如果明确指定数组长度,则需要至少分配四个字符:

char bar[4]="bar";

执行此操作时:

char bar[3]="bar";
printf("%s",bar);

您正在调用未定义的行为,因为bar变量没有空终止符。什么事情都可能发生。在这种情况下,编译器已经在内存中连续布局了两个数组:

'b' 'a' 'r' 'f' 'o' 'o' '\0'
 ^           ^
bar[3]      foo[4]

当你打印bar时,它会一直读取,直到找到空终止符,任何空终止符。由于bar没有,因此只有在"foo\0"结尾处找到它之后才会继续运行。

答案 1 :(得分:2)

如果声明char bar[3]="bar";,那么您将声明一个char数组,没有空终止符的空间。因此,printf()将继续从内存中读取char,将其打印到控制台,直到遇到'\0'

答案 2 :(得分:2)

其他海报已经向你解释过你的

char bar[3] = "bar";

示例字符串终止符不适合数组,因此字符串最终不会终止。从形式上讲,它甚至不是一个字符串(因为需要根据定义终止叮咬)。您正在尝试将非字符串作为字符串打印(使用%s格式说明符),这会导致未定义的行为。未定义的行为正是您所观察到的。

用C ++语言(例如)

char bar[3] = "bar";

声明是非法的,因为C ++不允许零终止符在这样的声明中“脱落”。 C允许它,但仅适用于隐式零终止符。

char bar[3] = "barr";

声明在C和C ++中都是非法的。

同样,“缺少零”技巧仅在C中使用隐式零终止符。它不适用于任何显式初始值设定项:不允许显式指定比数组中的元素更多的初始值设定项。这将我们带到你的第三个例子。在你的第三个例子中,你有

char FOO[3] = { 'F', 'O', 'O', '\0' };

声明,显式为大小为3的数组指定4个初始化器。这在C中是非法的。您的第三个示例是不可编译的。如果编译器在没有诊断消息的情况下接受它,则必须破坏编译器。第三个程序的行为无法用C语言解释,因为它不是C程序。

答案 3 :(得分:1)

当你暗示知道你的行char FOO[3]={'F','O','O','\0'};时,这是一个空终止问题。问题是null终止符是一个字符。如果为3个字符分配内存,则不能在该位置放置4个字符(只需要前3个字符并截断其余字符)。

答案 4 :(得分:0)

你错过了字符串末尾的\ 0和一个包含4个元素的数组被声明为FOO [4]而不是FOO [3] ..

答案 5 :(得分:0)

在第一个示例中,您没有以空字符结尾的字符串。碰巧它们被连续地放在内存中,因此行为可以解释为从一个字符串到另一个字符串的运行。

在下一个示例中,FOO的大小为3,但您要给它四个元素。同样,许多BAR不会以空值终止。

char FOO[3]={'F','O','O','\0'};
char BAR[3]="BAR";

答案 6 :(得分:0)

bar不是以null结尾,因此printf会一直跟随数组,直到它变为'\ 0'字符。堆栈被布置成使得bar和foo在存储器中彼此相邻。 C知道数组大小的唯一方法是找到一个空终端。所以,如果你在内存中放置你的堆栈,它将看起来像:

  0    1    2    3    4    5    6
 'b'  'a'  'r'  'f'  'o'  'o'  '\0'
  ^bar begins    ^foo begins

通过说foo [],编译器根据初始化的常量字符串设置foo的大小。它的智能足以使4个字符包含空终止符'\ 0'。

要解决此问题,bar的大小实际应为4,即:

 char bar[4] = "bar"; // extra space for null terminal

或更好,让编译器像你使用foo一样解决它:

 char bar[] = "bar"; // compiler adds null term character('\0')

答案 7 :(得分:0)

char bar[3]="bar";没有足够的空间来添加终止'\0'字符。

如果你char bar[4]="bar";,你应该得到你期望的结果。

答案 8 :(得分:0)

请阅读this详细解答,以便深入了解这一点......

你看到“有趣”的原因是因为字符串不是NUL终止......

答案 9 :(得分:0)

罪魁祸首就是这条线:

char bar[3]="bar";

这会导致'b','a'和'r'位于您创建的长度为3的数组中。

现在碰巧,foo中的字符串是'f','o','o'和'\ 0',并且它被分配了连续的位置和条形码。所以记忆看起来像:

b | a | r | f | o | o | \0

我希望这说清楚。

答案 10 :(得分:0)

这一行

char bar[3]="bar";

导致未定义的行为,因为"bar"是由'\0'处理的四个字符。所以你bar数组应该是四个字节。

未定义的行为意味着任何事情都可能发生 - 包括好事和坏事