【问题标题】:Weird output of char * with getchar to printfchar * 的奇怪输出与 getchar 到 printf
【发布时间】:2014-01-18 20:11:59
【问题描述】:

我正在为即将到来的课程学习我的 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%s\n", input_buffer );
// foo -> f¿:¿o¿:¿0¿:¿

// weird side effect is the 4 is required to get a proper len
printf("\ncharacters: %lu\n", strlen( input_buffer ) / 4 );

我到处搜索,但在其他任何地方都没有看到这个,但这似乎有点像边缘情况。这是我没有考虑到的某种编码问题吗?

【问题讨论】:

  • 你正在通过 malloc 分配单个字符
  • 次要:使用printf("%zu", strlen( input_buffer ) / 4 );strlen() 返回的类型是 size_tsize_tint 的二元运算 (/) 将产生 size_t'z' 是无符号整数类型size_t 的匹配格式规范修饰符。
  • 为什么投反对票?在没有解释的情况下投反对票并不是很有帮助。

标签: c printf getchar cc


【解决方案1】:

你不能打电话给strcat(input_buffer, &c);

传递给strcat 的每个参数都必须是有效的以空字符结尾的字符串。

&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;
}

【讨论】:

  • 您不需要检查'\r'stdin 是一个文本流,因此行尾(无论是 UNIX 样式的 LF 还是 Windows 样式的 CR-LF 序列)都被转换为单个 '\n' 换行符。此外,您可以在for 循环中省略truefor (int i = 0; ; i++)。 (true 无论如何都不会定义,除非你自己定义了它,或者你有 #include <stdbool.h>,或者你正在使用 C++ 编译器来编译 C 代码。)
  • 你确定'\r'吗?最近有这个问题(虽然我使用的是conio.h中的getch)。
  • @barakmanos:是的,我确定。我不知道来自<conio.h>getch 的行为如何;我说的是标准 C I/O 函数。
  • @chux:嗨。在 break 之前的循环内有什么问题?
  • 对于strcatc 变成一个以null 结尾的字符串是否有意义(这对我来说似乎很浪费)?如果不是,那么使用什么将c 附加到input buffer
【解决方案2】:
  • 您将空间分配给input_buffer,仅用于一个char
  • strcat(input_buffer, &c); 是错误的。您正在用字符串连接字符(它不是以 null 结尾的)。
  • getchar 返回 int 类型,但您声明 c 的类型为 char

【讨论】:

  • strcat 确实将char 的地址作为其第二个参数。问题是char 必须是字符串的第一个字符。
  • @KeithThompson;现已编辑。
  • 根据文档getchar() 返回unsigned char 转换为intEOF。所以除了EOF 可能超出char 范围之外,这并没有什么真正的问题。
  • @ony:这就是问题所在。通过将getchar() 的结果分配给char 对象,您将失去检测文件结束或错误情况的能力。
  • 对于strcatc 变成一个以null 结尾的字符串是否有意义(这对我来说似乎很浪费)?如果没有,那么使用什么将c 附加到input buffer
【解决方案3】:
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 tostrcatis achar*, so you've got the right type -- but thatchar* 读取的任何字符,必须指向一个有效的以空字符结尾的字符串。

strcat 将首先扫描input_buffer 指向的数组以找到终止的'\0' 字符,因此它知道从哪里开始追加——它可能会扫描到不属于您的任何对象的内存中声明或分配,可能会使您的程序崩溃。如果这没有发生,它会复制从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] = '\0'; /* ensure that it's properly null-terminated */

为了简单起见,我分配了一个固定大小的缓冲区而不是使用malloc

另外,为了简单起见,我省略了任何检查输入没有超过输入缓冲区的末尾。如果是这样,程序可能会崩溃如果你很幸运;如果你不走运,它可能只是在破坏不属于你的记忆时起作用。如果输入行不太长,它会正常工作。在任何实际程序中,您都需要检查这一点。

顺便说一句,使用fgets() 可以更轻松地完成这里所做的事情——但最好在稍低的级别上了解事情的工作原理。

【讨论】:

  • 我发现的所有示例都使用char arr[] 作为字符串,但这需要我知道最长的字符串有多长,而对于这个练习我不知道。那么,为什么不直接使用char * 以及将什么用于附加到char *?我知道在一定程度上它们是相同的,但我认为不必管理数组的边界会更好。
  • @PhilipRegan:有了char*,你还是要知道最大长度;不同之处在于您可以在运行时确定它。您可以根据需要使用realloc 来扩展数组(实际上是将数组替换为更大的数组)。您将以相同的方式附加单个字符。 (顺便说一句,数组和指针在任何级别上都不是一回事;请参阅comp.lang.c FAQ 的第 6 节。)
  • "...数组和指针在任何层面上都不是一回事..." 抱歉,我在这里没有很好地解释自己。感谢你的回答。很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 2020-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多