【问题标题】:Why are extra garbage characters printed?为什么要打印多余的乱码?
【发布时间】:2015-03-14 16:22:44
【问题描述】:

我尝试使用 read() 从文件中获取一些字符,只是为了学习这个 API。我在同一目录中创建了一个名为“file”的文件,它是内容:

1:2:ab:cd:ef

代码如下:

#include <stdio.h>
#include <error.h>

int read_indent(int sockfd){
  int sport, cport;
  char user[3], rtype[3], addinfo[3];
  char buffer[4+4+3+3+3+1];

  if(read(sockfd, buffer, sizeof(buffer)) <= 0) {
    perror("read: %m");
    return -1;
  }

  buffer[sizeof(buffer)-1] = '\0';

  sscanf(buffer, "%d:%d:%s:%s:%s", &sport, &cport, rtype, user, addinfo);
  printf("%d:%d:%s:%s:%s", sport, cport, rtype, user, addinfo);
  return 0;
}

int main(){
  FILE *file_pt = fopen("file", "r");
  if(file_pt == NULL) { printf("fopen error\n"); return -1;}
  char buf[128];
  int a = read_indent(fileno(file_pt));
  fclose(file_pt);
  return 0;
}

我的 printf 返回我

1:2:ab:cd:ef::xPvx

x 是一些我无法识别的垃圾字符。这是什么原因? int 在我的系统中是 4 个字节。

【问题讨论】:

  • 你是否在每个字符串的末尾留了一个空格,并存储了一个尾随的 0 字符?
  • 您能详细说明一下吗?我认为我的用户 [3] 包含额外的字符来存储 '\0',因为我的文件只有 ab。同样,我为 rtype 和 addinfo 创建了三个条目。我还为最后一个'\0'创建了一个额外的...这可能是基本的,但我不确定你的意思

标签: c string


【解决方案1】:

一个问题是您没有为%s 参数指定宽度。这意味着它匹配到第一个空白字符。您的字符串中没有空格字符,因此第一个 %s 匹配到最后,在您的字符串之后只留下垃圾数据来填充其他变量。

试试这个:

sscanf(buffer, "%d:%d:%2s:%2s:%2s", &sport, &cport, rtype, user, addinfo);

另一个问题是您没有正确地以空值终止缓冲区,read 返回读取的字符数 - 之后添加一个空值。

【讨论】:

  • 效果很好。如果我使用上面的行,我可以使用“char user[2], rtype[2], addinfo[2];”在初始化期间?因为当我更改为这个初始化时,它仍然打印相同的输出,sscanf 似乎没有为它扫描的任何 %2s 添加额外的 '\0',即它只用 2 个字节填充缓冲区(没有尾随 ' \0')
  • @dannycrane No. C 中的字符串 (char*) 是一个内存位置。它从那个内存位置开始,一直持续到直到第一个空字符。您的字符串应该以空值结尾。 %2s 会自动为您终止 null,例如尝试将 user[2] 的值打印为 %d,您会看到它是 0
  • 这很有意义。我只是尝试“char user[2], rtype[2], addinfo[2];”和“char user[3], rtype[3], addinfo[3];”,都使用 %d 为 user[2] 打印 0。但是如果我用“char user[2], rtype[2], addinfo[2];”初始化缓冲区,为什么 user[2], rtype[2], addinfo[2] 仍然打印 0?因为我认为这三个缓冲区在堆栈中彼此相邻分配。所以至少有一个打印不应该打印 0。我的机器是 64 位的,带有 64 位操作系统。
  • @dannycrane 关于 C,您应该了解一件非常重要的事情。语言不会为您检查错误。它只是默默地失败,或者它可能崩溃,或者它现在可能默默地失败但稍后崩溃,或者它可能给出虚假值。
  • 因此,如果我的初始化是 char user[2]、rtype[2]、addinfo[2],并且 '\0' 存储在我的初始化之外的某个位置,这将导致静默失败或稍后崩溃.但是我们知道堆栈中存储的三个额外的'\0'在哪里吗? (感谢您回答这些问题,这是最后一个:))
【解决方案2】:
 char buffer[4+4+3+3+3+1];

缓冲区比您计划读取的要大,没关系,但是:

 buffer[sizeof(buffer)-1] = '\0';

这是错误的,在 size+1 处添加 \0,其中 size 是您使用 read() 返回的值,即实际读取的字节数。

here:

如果文件中剩余的字节数小于 nbyte,如果 read() 请求被信号中断,或者文件是管道或 FIFO 或特殊文件,则返回的值可能小于 nbyte,并且可立即读取的字节数少于 nbyte。

【讨论】:

  • 我不太明白你的回答。我更改为 char buffer[20];在初始化期间,我在buffer[sizeof(buffer)-1] = '\0'; 之后使用printf("sizeof(buffer) = %lu\n", sizeof(buffer));。它仍然打印 20 作为大小。
  • @dannycrane 'sizeof' 告诉您 缓冲区 的大小,而不是其中包含的字符串的大小。改变缓冲区的内容不能改变它的大小。问题不在于缓冲区的大小在变化;问题是您将 nul 终止符('\0')放在 buffer 的末尾,而不是 string 的末尾。
  • (很抱歉这里和那里的错别字:))。正如@davmac 所说。这可能是错误/漏洞的巨大来源。
【解决方案3】:
char buffer[4+4+3+3+3+1];
if(read(sockfd, buffer, sizeof(buffer)) <= 0) {
    //....
}
buffer[sizeof(buffer)-1] = '\0';

读取函数在读取后不会将\0 添加到缓冲区中。但是你只读取了 12 个字节,而你的缓冲区大小是 18。所以你的缓冲区中仍然有 5 个字节的垃圾。这将添加到您读取的最后一个字符串中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-23
    • 2011-10-05
    • 1970-01-01
    相关资源
    最近更新 更多