我正在为即将到来的课程掸去我的 C 技能,在使用getchar
构建字符串后,我遇到了这个奇怪的输出printf
。具体来说,我尝试输出的任何字符串都会在每个字母后附加相同的字符序列。foo
变得"f?8@{?o?8@{?o?8@{?"
编译cc
,f¿:¿o¿:¿0¿:¿
Apple LLVM 5.0
(Xcode)。下面是说明该问题的示例代码:
char * input_buffer = malloc( sizeof( char ) );
char c;
while ( ( c = getchar() ) != 'n' ) {
strcat(input_buffer, &c);
}
// problem output
printf( "n%sn", input_buffer );
// foo -> f¿:¿o¿:¿0¿:¿
// weird side effect is the 4 is required to get a proper len
printf("ncharacters: %lun", strlen( input_buffer ) / 4 );
我已经搜索了所有地方,但我在其他任何地方都没有看到这个,但这似乎有点边缘情况。这是我没有考虑的某种编码问题吗?
您不能调用strcat(input_buffer, &c);
。
传递给strcat
的每个参数都必须是以 null 结尾的有效字符串。
&c
之后的下一个字节为 0 的可能性非常渺茫。
input_buffer
指向的第一个字节为 0 的可能性也不是很高。
换句话说,strcat
在两个参数中都读取"垃圾",直到遇到 0 字符。
改变:
while ( ( c = getchar() ) != 'n' ) {
strcat(input_buffer, &c);
}
自:
for (int i=0; 1; i++)
{
c = getchar();
if (c == 'r' || c == 'n')
{
input_buffer[i] = 0;
break;
}
input_buffer[i] = c;
}
- 您只为一个
char
分配了要input_buffer
的空间。 strcat(input_buffer, &c);
错了。您正在将字符(它不是以 null 结尾)与字符串连接起来。getchar
返回int
类型,但您声明c
类型为char
。
char * input_buffer = malloc( sizeof( char ) );
根据定义,sizeof (char)
为 1。这会为单个字符分配空间,并指出input_buffer
。
您也不会检查分配是否成功。malloc
失败时返回空指针;您应该始终检查这一点。
input_buffer
指向的分配char
对象包含垃圾。
char c;
while ( ( c = getchar() ) != 'n' ) {
strcat(input_buffer, &c);
}
getchar()
返回一个int
,而不是一个char
。您可以将结果分配给char
对象,但这样做将失去检测和文件结束或错误条件的能力。getchar()
在没有更多要读取的字符时返回EOF
;您应该始终检查这一点,这样做需要将结果存储在int
中。(EOF
是不等于任何有效字符的整数值。
strcat(input_buffer, &c);
input_buffer
指向单个未初始化的char
。您可以将其视为由单个char
元素组成的数组。要strcat
的第一个参数必须已经包含有效的以 null 结尾的字符串,并且它必须有足够的空间来容纳该字符串以及要附加到它的任何内容。
c
是一个char
对象,包含您刚刚读取的任何字符getchar(). The second argument to
strcatis a
char*, so you've got the right type -- but that
char*' 必须指向有效的以 null 结尾的字符串。
strcat
将首先扫描input_buffer
指向的数组以找到终止' '
字符,以便它知道从哪里开始追加 - 并且它可能会扫描到不属于您声明或分配的任何对象的内存中,这可能会使您的程序崩溃。如果它没有爆炸,它将复制从c
开始的字符,并将其通过到您不拥有的内存中。您有多种形式的未定义行为。
您不需要使用strcat
将单个字符附加到字符串中;您只需分配它即可。
下面是一个简单的示例:
char input_buffer[100];
int i = 0; /* index into input_buffer */
int c;
while ((c = getchar()) != 'n' && c != EOF) {
input_buffer[i] = c;
i ++;
}
input_buffer[i] = ' '; /* ensure that it's properly null-terminated */
我分配了一个固定大小的缓冲区,而不是使用malloc
,只是为了简单起见。
同样为简单起见,我省略了输入未超过输入缓冲区末尾的任何检查。如果是这样,如果幸运的话,程序可能会崩溃;如果你不走运,它可能只是在破坏不属于你的记忆时看起来有效。如果输入行不太长,它将正常工作。在任何实际程序中,您都需要检查这一点。
顺便说一句,这里所做的工作更容易使用fgets()
来完成 - 但最好了解事情在稍低的级别上的工作方式。