C:vsprintf覆盖数组

时间:2012-05-21 10:01:07

标签: c overwrite printf

使用vsprintf函数时遇到问题。

我有3个函数来打开,关闭和写入XML文件。 open函数将输入文本的第一个单词存储在一个数组中,close函数用该单词关闭标记。问题是我存储要使用的关闭标记的数组在每次调用open或write函数时都会被覆盖(即使write函数没有引用用于存储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>

谢谢。

2 个答案:

答案 0 :(得分:1)

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

您需要为strtok返回的字符串分配存储空间,并确保稍后将其丢弃,例如:改变:

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

为:

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

(稍后在不再需要这些字符串时将它们作为练习留给读者。)

答案 1 :(得分:1)

<强>解决方案

更改此行:

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

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

请记住在程序退出时释放xml_headers。

<强> [UPDATE] 当然,您还需要检查可能的角落案例,例如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 1}}将打印出一些不可读的混乱代码。

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