【问题标题】:C: vsprintf overwriting an arrayC: vsprintf 覆盖一个数组
【发布时间】:2012-05-21 10:01:06
【问题描述】:

我在使用函数 vsprintf 时遇到问题。

我有 3 个函数来打开、关闭和写入 XML 文件。 open 函数将输入文本的第一个单词存储在一个数组中,close 函数关闭带有该单词的标签。问题是我存储要使用的关闭标记的数组在每次调用打开或写入函数时都会被覆盖(即使写入函数没有对用于存储关闭标记的数组的任何引用)。

int xml_level = 0;
char *xml_header[64];
FILE *xml_out;

void xmlopen(const char *format, ...){
   char buffer[256];
   va_list arglist;
   va_start(arglist,format);
   vsprintf(buffer,format,arglist);
   va_end(arglist);
   int i;
   for(i=0; i<xml_level; i++){
      fprintf(xml_out,"\t");  
   }
   fprintf(xml_out,"<%s>\n",buffer);
   xml_header[xml_level] = strtok (buffer, " ");
   xml_level++;
}

void xmlclose(){
   xml_level--;
   int i;
   for(i=0; i<xml_level; i++){
      fprintf(xml_out,"\t");  
   }
   fprintf(xml_out,"</%s>\n",xml_header[xml_level]);
}

void xmlwrite(const char *format, ...){
   char buffer[256];
   va_list arglist;
   va_start(arglist,format);
   vsprintf(buffer,format,arglist);
   va_end(arglist);
   int i;
   for(i=0; i<xml_level; i++){
      fprintf(xml_out,"\t");  
   }
   fprintf(xml_out,"<%s/>\n",buffer);
}

使用示例:

xmlopen("Hello Word");
xmlopen("Foo Bar");
xmlwrite("Potato");
xmlwrite("Sentence longer than the other ones");
xmlclose();
xmlclose();

输出示例:

<Hello Word>
        <Foo Bar>
                <Potato/>
                <Sentence longer than the other ones/>
        </Sentence longer than the>
</Sentence longer than the>

它应该在哪里:

<Hello Word>
        <Foo Bar>
                <Potato/>
            <Sentence longer than the other ones/>
        </Foo>
</Hello>

谢谢。

【问题讨论】:

    标签: c overwrite printf


    【解决方案1】:

    解决方案

    改变这一行:

    xml_header[xml_level] = strtok (buffer, " ");
    

    xml_header[xml_level] = strdup (strtok(buffer, " "));
    

    记得在程序退出时释放 xml_headers。

    [更新] 当然,你还需要检查可能的极端情况,例如strtok返回NULL等...

    说明

    strtok 不会为返回的令牌分配额外的存储空间。我个人的怀疑是,它将用\0 就地替换分隔符,并每次都返回指向下一个标记开始的指针。

    请注意,在您的代码中,您一开始就在xmlopenxmlwrite 中分配了256 字节的缓冲区,并记得这个缓冲区将在堆栈中分配。所以在你调用xmlopenxmlwrite 时,buffer 实际上会指向同一个地址(你可以打印它的值来验证这个printf("buffer is %p\n", buffer),它在我的机器上是0xbff2481c)。

    首先,你调用xmlopen("Hello World")xml_header[0]会指向"Hello",这也是buffer的开始。然后调用xmlopen("Foo Bar")xml_header[1] 将指向"Foo",这也是buffer 的开始。然后你打电话给xmlwrite("Portato")xmlwrite("Sentence longer than the other ones")。请注意,此时xml_header[1] 仍然指向buffer 的开头,现在是"Sentence longer than the other ones"。因此,当您调用 xmlclose() 时,它会打印出该句子而不是您预期的令牌,该令牌会被您以后的缓冲区覆盖。

    有趣的是,如果你在xmlopenxmlwrite 分配不同的缓冲区大小,比如xmlopen 256 字节和xmlwrite 128 字节,那么你会看到xmlclose 会打印出一些乱码不可读。

    您可以通过检查(例如打印)xml_header[0], xml_header[1]buffer 的值来验证所有这些。

    【讨论】:

    • 请注意,如果 strtok 返回 NULL,strdup 将失败。
    • @PaulR:感谢您的提醒!绝对需要错误检查。为了强调问题本身,我在这里省略了它们。我已经相应地更新了我的帖子。
    【解决方案2】:

    您的问题是您在 xmlopen 中只有一个名为 buffer 的局部变量,并且您正在 (a) 存储指向它的指针以供在函数外部使用(未定义的行为)和 (b) 尝试跨多次调用(逻辑错误)。

    您需要为strtok 返回的字符串分配存储空间,并确保稍后处理它们,例如改变:

    xml_header[xml_level] = strtok (buffer, " ");
    

    到:

    char * s = strtok(buffer, " ");
    if (s != NULL)
    {
        xml_header[xml_level] = strdup(s);
    }
    

    (以后不再需要这些字符串时如何处理这些字符串,留给读者作为练习。)

    【讨论】:

      猜你喜欢
      • 2013-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-11
      • 1970-01-01
      • 2012-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多