【问题标题】:Segmentation fault in va_args parsingva_args 解析中的分段错误
【发布时间】:2011-07-17 09:57:18
【问题描述】:

为什么下面的代码会给出EXC_BAD_ACCESS, could not access memory

int combine_strings(char **outputStr,...)
{
    va_list ap;
    char *s, *out=0;
    int len=0;

    va_start(ap,outputStr);
    while(s=va_arg(ap,char *))
    {
      len+=strlen(s);
    }
    va_end(ap);


    if(!(out=malloc(len+1)))
        exit(1);

    *outputStr=out;

    va_start(ap,outputStr);
    while(s=va_arg(ap,char *))
    {
      len=strlen(s);
      memcpy(out,s,len);
      out+=len;
    }
    va_end(ap);

    *out=0;

    return 0;
}

【问题讨论】:

    标签: c malloc segmentation-fault variadic-functions


    【解决方案1】:

    我不得不不同意之前的其他海报。原始代码不会重复相同的va_list 两次。它创建了两个不同的列表并依次迭代它们中的每一个,即使使用相同的变量来保存两个列表。

    事实上,我设法正确地运行了这个函数。因此,我的猜测是问题在于函数的调用方式。我是这样称呼它的,注意后面的NULLoutput 参数的设置:

    #include <stdio.h>
    #include <stdarg.h>
    #include <stdlib.h>
    #include <string.h>
    
    // ... combine_strings() goes here...
    int main()
    {
      char * res;
      char * * output = &res;
      combine_strings(output, "FOO", "BAR", "BAZ", NULL);
      printf("%s\n", *output);
    }
    

    上面的代码按预期输出FOOBARBAZ

    【讨论】:

    • +1:标准 (7.15.1.4) 明确指出“va_start 宏初始化 ap 以供 va_arg 和 va_end 后续使用。”
    • 你是对的!它的调用方式有问题。如果我们将 ZERO 而不是 NULL 作为最后一个参数传递,它将给出分段错误。如果我们传递 0,那么我们应该将其类型转换为 (void*)0,我传递的是 0,因此它给出了分段错误。
    • 如果一个指针比一个普通的 int 大,那么这将说明需要显式指针转换或 NULL。附言。当您满意时,不要忘记将其中一个答案标记为已接受。
    【解决方案2】:

    您不能对同一个va_list 进行两次迭代。您需要使用va_copy() 创建一个副本。

    int combine_strings(char **outputStr,...)
    {
        va_list ap, ap2;
        char *s, *out=0;
        int len=0;
    
        va_start(ap,outputStr);
        va_copy(ap2, ap);
        while(s=va_arg(ap2,char *))
        {
          len+=strlen(s);
        }
        va_end(ap2);
    
    
        if(!(out=malloc(len+1)))
            exit(1);
    
        *outputStr=out;
    
        while(s=va_arg(ap,char *))
        {
          len=strlen(s);
          memcpy(out,s,len);
          out+=len;
        }
        va_end(ap);
    
        *out=0;
    
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      在一个函数中使用两次 va_start 很难在所有平台上工作。请参阅here 了解更多信息。

      最好使用va_copy

      【讨论】:

      • 您作为参考提供的链接描述了与“在一个函数中使用 va_start 两次”完全不同的问题。
      【解决方案4】:

      回答此类问题的简单方法是在调试器中运行它。您将获得完整的堆栈跟踪、代码指针,并且您将能够查看所有变量的值。

      要使用gdb,首先用调试符号编译程序(gcc 中的-g)。然后,运行它:

      gdb program_name
      (gdb) run
      

      它会崩溃,你会明白为什么。

      【讨论】:

        猜你喜欢
        • 2015-08-02
        • 2015-07-30
        • 2012-01-19
        • 2023-03-22
        • 1970-01-01
        • 2015-08-02
        • 1970-01-01
        • 2015-01-22
        • 1970-01-01
        相关资源
        最近更新 更多