【问题标题】:stdout buffering problems标准输出缓冲问题
【发布时间】:2013-10-27 22:30:19
【问题描述】:

这段代码我已经写好了

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

char *list[20],*story[100];

FILE*listfile;
FILE*infile;
FILE*outfile;

int check(char*string)
{
  int i=0;
  while(list[i]!=NULL)
    {
      if(strcmp(string,list[i])==0){return 1;};
      i++;
    };
  return 0;
};

void print_d(int d){ printf(" debug %d ",d);};

int main(int argc,char**argv){

  assert(argc==4);
  printf("hello \n");
  //assigning the file pointers in their respective modes
  printf("%s %s %s ",argv[1],argv[2],argv[3]);
  listfile=fopen(argv[1],"r");
  print_d(12);
  infile=fopen(argv[2],"r");
  outfile=fopen(argv[3],"w");
  print_d(0);
  int i=0; /* the infamous 'i' */

  while(1)
    {

      if(feof(listfile)!=0)
        { break;};
      list[i]=malloc(sizeof(char [15]));
      fscanf(listfile,"%s[^\n]",list[i]);
      i++;


    }
  i=0;
  print_d(1);
  while(1)
    {

      if(feof(infile)!=0)
        { break;};
      story[i]=malloc(sizeof(char [25]));
      fscanf(infile,"%s",story[i]);
      i++;

    }
  fclose(infile);
  fclose(listfile);
  i=0;

  print_d(2);
  while(1)
    {

      if(check(story[i])==1)
        { fprintf(outfile,"%s","censored");}
      else
        {
          fprintf(outfile,"%s",story[i]);
        };
    };
  print_d(3);
  fclose(outfile);
  i=0;
  while(list[i]!=NULL)
    { free(list[i]);};
  return 0;


}

以下问题随之而来

[1] 输出是一个 hello 后跟一个 seg 错误

这就是事情变得有趣的地方

如果我修改

printf("%s %s %s ",argv[1],argv[2],argv[3]);

printf("%s %s %s\n ",argv[1],argv[2],argv[3]);

输出是一个“你好”,后跟三个文件名,然后是一个段错误。

在用户 danfuzz 指出我应该将 print_d 调试更改为打印到 stderr 之后(我这样做了)..调试打印现在工作正常。所以我想一个更好的问题是为什么会首先发生这种情况和步骤防止此类事情发生?

对于经验丰富的程序员来说,这似乎是一个微不足道的问题,但请注意,早期版本(上面代码中的那个)未能在 seg fault 之前打印出任何消息,这使我得出结论,在命令行部分发生了一些事情东西/打开文件。

【问题讨论】:

  • 你应该经常检查 fopen() 的返回值是否为 0。
  • @CharlieBurns 你认为打开文件时可能发生了一些事情......这是第一次。但会的。
  • 关于if(strcmp(string,list[i])==0){return 1;};,删除最后一个;(不确定它会影响什么,只是一个混淆因素。
  • @ryyker 不好的做法?
  • 您可能想让print_d 打印一个换行符,也可能转到stderr 而不是stdout。似乎标准输出的默认缓冲让您感到困惑。

标签: c stdout


【解决方案1】:

无需在论坛中猜测或询问,只需运行调试器即可。如果在 linux 上工作,gdb 会直接带您到分段错误的位置。

假设您的应用名为“foo”:

> gdb foo
# run

当它崩溃时:

# bt

【讨论】:

  • 我对段错误以外的东西更感兴趣,
  • gdb 会给你更多。下断点,检查变量,找到你的问题。这比打印或猜测要容易得多
  • 您的建议是合理的,但请告诉我,先生,您的“评论”不应该在“评论”部分而不是“答案”部分吗?
  • 也许你是对的。但是 gdb 是调试的正确答案。
  • 现在您的答案已从相对于前一个问题的“评论”转换为相对于重构问题的有效“答案”。
【解决方案2】:

一些观察,

您应该阅读有关 fflush(stdout) 的信息,因为它将帮助您进行调试语句,

void print_d(int d)
{
    printf(" debug %d ",d); fflush(stdout);
};

您分配给 char 指针数组 list[20]story[100],但您有循环(由臭名昭著的“i”索引),很容易离开列表或故事的末尾。

您尝试打开文件名 argv[2]argv[3] 的文件,其中一个为读取,另一个为写入,将这些行更改为以下内容,

printf("%s %s %s ",argv[1],argv[2],argv[3]); fflush(stdout);

if( !(listfile=fopen(argv[1],"r")) )
{
    printf("cannot open %s\n",argv[1]); fflush(stdout);
    return(1);
}
print_d(1);
if( !(infile=fopen(argv[2],"r")) )
{
    printf("cannot open %s\n",argv[2]); fflush(stdout);
    return(2);
}
print_d(2);
if( !(outfh=fopen(argv[3],"w+")) ) //notice the "w+" to create missing file
{
    printf("cannot open %s\n",argv[3]); fflush(stdout);
    return(3);
}
print_d(3);

现在文件可以正常打开,因此将调试 print_d 参数更改为递增的数字顺序,这样您就可以按顺序发现哪个,并且由于您使用的是计数器,所以 for(;;) 循环有效,

int check(char*string)
{
    int i;
    for(i=0; list[i]!=NULL; i++)
    {
        if(strcmp(string,list[i])==0){return 1;};
    };
    return 0;
};

更改循环以成功读取两个文件,

for(i=0; i<20; ++i)
{
    if(feof(listfh)!=0) { break; };
    list[i]=malloc(sizeof(char [15]));
    fscanf(listfh,"%s[^\n]",list[i]);
}
fclose(listfh);
debug(4);

还有,

for(i=0; i<20; ++i)
{
    if(feof(infh)!=0) { break; };
    story[i]=malloc(sizeof(char [25]));
    fscanf(infh,"%s",story[i]);
}
fclose(infh);
debug(5);

现在对循环进行简单更改以扫描故事,检查审查(eek!),因此我们避免比较和打印空指针(您遇到的另一个问题),

for(i=0; i<100 && (story[i]); ++i)
{
    if(check(story[i])==1)
    {
        fprintf(outfh,"%s","censored"); fflush(outfh);
    }
    else
    {
        fprintf(outfh,"%s",story[i]); fflush(outfh);
    };
};

但是,请注意,您真的不需要将故事读入数组,您可以一次读取一行,然后打印结合这两个循环,您可以扫描任意大的文件,而无需分配大量空间,

for(i=0; 1; ++i)
{
    if(feof(infh)!=0) { break; };
    story[0]=malloc(sizeof(char [25]));
    fscanf(infh,"%s",story[0]);
    if(check(story[0])==1)
    {
        fprintf(outfh,"%s","censored"); fflush(outfh);
    }
    else
    {
        fprintf(outfh,"%s",story[0]); fflush(outfh);
    };
}
fclose(infh);
fclose(outfh);

您还需要确保只释放您分配的行,

for(i=0; list[i] && list<20; i++)
{
    free(list[i]);
}

这应该可以解决您的问题。


添加一个usage()函数,

void usage(char*progname)
{
    printf("need 3 files\n");
    printf("%s <restricted> <story> <censorted>\n",progname);
}

然后调用它,

if( argc < 4 )
{
    usage(argv[0]);
    return 0;
}

【讨论】:

  • 使用 assert 比使用 if 语句检查文件是否正确打开是不是很不寻常?顺便说一句,时机很好……我正要对着电脑开个斧头……
  • 个人偏好/习惯问题,因为我通常会尝试“修复”检测到的问题,而不是懒惰退出。我见过喜欢断言的 Windows 人。
猜你喜欢
  • 2013-11-28
  • 2014-06-19
  • 1970-01-01
  • 2014-12-07
  • 2016-10-25
  • 2017-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多