末尾有垃圾字符的字符串数组

13

我有一个字符数组缓冲区,用于逐个存储用户输入的字符。下面是我的代码,虽然可以工作,但存在一些问题:

  1. 当我执行 printf 查看 Buffer 中的内容时,它会填充数据,但末尾会出现一些垃圾字符。
  2. 尽管已声明为 char Buffer[8],但它不会在 8 个字符处停止。

请问有人能够解释一下发生了什么,并可能告诉我如何修复这个问题吗?谢谢。

char Buffer[8]; //holds the byte stream
int i=0;

if (/* user input event has occurred */) 
{
        Buffer[i] = charInput;
        i++;

        // Display a response to input
        printf("Buffer is %s!\n", Buffer);

}

输出:

tagBuffer is 1┬┬w!
tagBuffer is 12┬w!
tagBuffer is 123w!
tagBuffer is 1234!
tagBuffer is 12345!
tagBuffer is 123456=!
tagBuffer is 1234567!
tagBuffer is 12345678!

tagBuffer是123456789!

7个回答

30

你必须用\0字符来结束字符串。这就是为什么它们被称为零结束字符串。

另外,分配一个额外的字符来保存\0是明智的。


太棒了。谢谢。只有一个问题:buffer[8] 的作用是什么? - Steve
buffer[8] 告诉编译器为您的数组分配 8 字节的内存。您的字符串可能比这个短(例如通过 "buffer[4] = 0;"),但它不能(或不应该)更长。 - Paige Ruten
1
Buffer[8] 是一个包含7个字符和一个终止符的缓冲区;-)。 - Toon Krijthe

8
你传递给printf()函数的唯一参数是你字符串的第一个字符的指针。printf()函数无法知道你数组的大小。(它甚至不知道它是否真的是一个数组,因为指针只是一个内存地址。) printf()和所有标准的c字符串函数都假定在你的字符串末尾有一个0。例如,printf()函数将继续从你传递给该函数的字符开始打印内存中的字符,直到遇到一个0为止。
因此,你应该将你的代码更改为如下所示:
char Buffer[9]; //holds the byte stream
int i=0;

if( //user input event has occured ) 
{
        Buffer[i] = charInput;
        i++;

        Buffer[i] = 0; // You can also assign the char '\0' to it to get the same result.

        // Display a response to input
        printf("Buffer is %s!\n", Buffer);

}

3

除了之前关于零终止的评论外,您还需要承担不超出自己缓冲区的责任。它不会在8个字符处停止,因为您的代码没有停止!您需要像以下示例一样做些改变(借鉴Jeremy的建议):

#define DATA_LENGTH 8
#define BUFFER_LENGTH (DATA_LENGTH + 1)

char Buffer[BUFFER_LENGTH]; //holds the byte stream
int charPos=0;  //index to next character position to fill

while (charPos <= DATA_LENGTH  ) { //user input event has occured
    Buffer[i] = charInput;

    Buffer[i+1] = '\0';

    // Display a response to input
    printf("Buffer is %s!\n", Buffer);

    i++; 

}

换句话说,当达到最大长度时,请确保停止接受数据,而不管环境试图向您推送什么。

@Jonathan:我认为DATA_LENGTH是主要的应用程序域概念,而缓冲区的大小是依赖值,不是反过来。可能会有其他与缓冲区无关的DATA_LENGTH用法,因此不应该提到它。 - joel.neely
@joel.neely 虽然你上面的代码不正确(while语句注释位置错误),但是你应该得到一个赞,因为将下一个字符设置为'\0'是解决方案。我建议修改为"Buffer[i] = charInput; Buffer[i+1] = '\0'; i++;"以增加清晰度,虽然你的实现并没有错误,只是更难调试,因为你在同一行中设置了2个变量(这是风格问题,我猜)。 - tony gil

0

由于Buffer未初始化,因此它从所有9个垃圾值开始。

从观察到的输出中,第2、3、4、5、6、7、8和2个紧接着的内存位置(在数组外部)的元素明显是'T''T''W''\0''\0''=''\0''\0''\0'

字符串会消耗掉所有字符,直到遇到NULL字符为止。这就是为什么在每次迭代中,当数组元素逐个分配时,缓冲区会打印到存在垃圾NULL的部分。

也就是说,如果字符数组没有以'\0'结尾,则字符串具有未定义的行为。您可以通过在缓冲区末尾留出一个额外的空间来避免这种情况,用于'\0'


0
如果你在使用C或C++进行编程,那么你需要记住以下几点: 1)字符串以\0字符结尾。 2)C语言没有对字符串进行边界检查,它们只是字符数组。

0

很奇怪没有人提到这种可能性:

char Buffer[8]; //holds the byte stream
int i = 0;

while (i < sizeof(Buffer) && (charInput = get_the_users_character()) != EOF)
{
    Buffer[i] = charInput;
    i++;

    // Display a response to input
    printf("Buffer is %.*s!\n", i, Buffer);
}

printf() 格式字符串中的此符号指定要显示的字符串的最大长度,不需要空终止符(尽管空终止符最终是最好的选择——至少在离开此循环后)。

while 循环比简单的 if 更可行,此版本确保您不会溢出缓冲区的末尾(但不能确保您留出足够的空间用于尾随 NUL '\0'。如果您想处理它,请使用 sizeof(Buffer) - 1,然后在循环后添加 NUL。


-1

你可能还想考虑使用 stringstream


1
stringstream 不是 C 相关的。 - quinmars

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