C语言中的字符串处理行为

5
我正在阅读一门C语言课程(它是荷兰语的,所以你可能不知道),有一个小练习是为了理解字符串的行为。因此,我创建了一个小的C程序来开始这个练习,但是我的程序的第一个输出已经让我感到惊讶了。
我的C程序源代码:
#include <string.h>
#include <stdio.h>

void printString(char *string)
{
    printf("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19\n");
    printf("%c ",string[0]);
    printf("%c ",string[1]);
    printf("%c ",string[2]);
    printf("%c ",string[3]);
    printf("%c ",string[4]);
    printf("%c ",string[5]);
    printf("%c ",string[6]);
    printf("%c ",string[7]);
    printf("%c ",string[8]);
    printf("%c ",string[9]);
    printf("%c  ",string[10]);
    printf("%c  ",string[11]);
    printf("%c  ",string[12]);
    printf("%c  ",string[13]);
    printf("%c  ",string[14]);
    printf("%c  ",string[15]);
    printf("%c  ",string[16]);
    printf("%d  ",string[17]);
    printf("%d  ",string[18]);
    printf("%d\n",string[19]);
}

void main(){

    char str[20];

    strcpy(str,"Dag grootmoeder!");
    printString(str);
}

我使用gcc编译程序(没有特殊的开关),并运行了几次: (对于讲英语的人来说:Dag grootmoeder! == Hi grandma!
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

D a g   g r o o t m o  e  d  e  r  !    94  -90  111

$./oefString 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

D a g   g r o o t m o  e  d  e  r  !    51  -12  96

$./oefString 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

D a g   g r o o t m o  e  d  e  r  !    -17  -117  28

$./oefString 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

D a g   g r o o t m o  e  d  e  r  !    96  15  -28

$./oefString 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

D a g   g r o o t m o  e  d  e  r  !    -20  -46  -18

$./oefString 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

D a g   g r o o t m o  e  d  e  r  !    68  -75  58

这里是问题:
1)为什么我在 str 的最后 3 个索引处得到垃圾值?起初,我也使用 %c 打印它们,并注意到字符发生了变化,这就是为什么我随后使用 %d 显示整数值的原因。
2)为什么这些值会改变?我只是使用 strcpy() 将相同的字符串复制到 str 中而已。
感谢您抽出时间阅读和回复! Jorn
3个回答

8
您只是访问了字符串结尾之后的内存。您没有填充它,因此在每次运行时都会发现其他内容。
在 C 语言中,“字符串”实际上是以0为结尾的数组(或内存指针)。因此,如果使用“%d”打印字符,您将注意到在感叹号之后的最后一个元素是0。
如果您希望其可预测,可以在使用之前初始化该字符串:
memset(str, 0, sizeof(str));

或者

char str[20] = {0,};

作为一个副注,应该是 int main,而不是 void main

我想我确实看到了内存,但是我使用 char str[20] 的初始化保留了 20 字节的空间;(至少我是这样认为的)。 那么,填充我保留的 20 字节未使用内存是一种内存优化吗? - Jorn De Pril
谢谢,我注意到当禁用堆栈随机化时,这些值保持不变。执行以下命令以禁用堆栈随机化:echo "0" > /proc/sys/kernel/randomize_va_space - Jorn De Pril
如果您不初始化数组,它可能包含任何内容。@JornDePril - cnicutar
关于你的便签,如果有退出代码时,不是应该用“int main”,而没有返回值时使用“void main”吗? - Jorn De Pril
@JornDePril 在这里阅读相关内容:http://c-faq.com/ansi/voidmain.html。此外,这个链接也很有趣:http://c-faq.com/ansi/voidmainbooks.html :-) - cnicutar
好的,显然我正在阅读一门课程,其中作者是目标受众之一。他总是使用 void main ... 现在我将开始使用 int 来定义主函数。 - Jorn De Pril

0

在没有任何存储类别说明符(例如您的str数组对象)的块作用域定义的对象具有自动存储持续时间。

未明确初始化的自动对象具有不确定值。 因此,在声明您的str对象之后,所有数组元素都具有不确定值。 通过在数组中复制字符串"Dag grootmoeder!",您使得前17个(字符串长度+尾随空字符的长度)元素具有指定值。 这会使得您的数组的最后3个元素保持不确定的值。

C语言表示,不确定的值可以指未指定或是陷阱表现形式。 C还表示,读取不确定值是未定义行为,因此与未定义行为一样,当您这样做时,可能发生任何事情,例如打印垃圾信息。


0
char str[20];
...
strcpy(str,"Dag grootmoeder!");

str以随机值开始(上次使用RAM时存在的任何内容)现在你将一个16字节的字符串复制到它中,留下最后3个字符的初始(随机)值,因此这些将被打印出来


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接