2014-11-25 54 views
2

我给出一个程序如下功能:vfprintf()抛出SegFault - 但输入定义良好?

void log(char* arg1, ...) 
{ 
    time_t  t; 
    struct tm *tm; 
    va_list  args; 
    char  *fmt; 
    char  curtime[TIME_STR_SIZE]; 
    FILE  *fd; 

    va_start(args, arg1); // linux stdarg va_start req 2 args - va_lilst, parm_n 


    fmt = va_arg(args, char*); 

    if (lfd == NULL) 
     fd = stderr; 
    else 
     fd = lfd; 

    (void) time(&t); 
    tm = localtime(&t); 

    sprintf(curtime, "%02d.%02d.%d, %02d:%02d:%02d", tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec); 

    fprintf(fd, "%s: ", curtime); 
    vfprintf(fd, arg1, args); 
    fprintf(fd, "\n"); 
    fflush(fd); 

    va_end(args); 
} 

显然它需要的输入参数列表,并将其写入到文件中。

但运行的代码,这部分抛出我已经退步到vfprintf分段错误:

(gdb) backtrace 
#0 0x00007ffff4799db2 in __strlen_sse2() from /lib64/libc.so.6 
#1 0x00007ffff476220d in vfprintf() from /lib64/libc.so.6 
#2 0x0000000000401b03 in log (arg1=0x40529d "%s gestartet: PID = %d") at logging.c:73 
#3 0x00000000004045d4 in main (argc=10, argv=0x7fffffffdc98) at abgleich.c:589 

在运行GDB和打印参数是罚款:

(gdb) print fd 
$9 = (FILE *) 0x607010 
(gdb) print fmt 
$10 = 0x7fffffffe1a7 "/srv/workspace/abgleich/abgleich" 
(gdb) print args 
$11 = {{gp_offset = 16, fp_offset = 48, overflow_arg_area = 0x7fffffffda10, reg_save_area = 0x7fffffffd950}} 

我很好奇,为什么在这种情况下,由于它看起来像定义明确的输入,因此引发了分段错误。

函数的调用部分如下:

log("%s gestartet: PID = %d", argv[0], getpid()); 

编辑:

我已经以下列方式使用va_copy改写:

va_start(args, arg1); // linux stdarg va_start req 2 args - va_lilst, parm_n 
va_copy(c_args, args); 

if (lfd == NULL) 
    fd = stderr; 
else 
    fd = lfd; 

(void) time(&t); 
tm = localtime(&t); 

/* strftime(curtime, TIME_STR_SIZE, "%d.%m.%y, %H:%M:%S\0", time_ptr); */ 
sprintf(curtime, "%02d.%02d.%d, %02d:%02d:%02d", tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec); 
fprintf(fd, "%s: ", curtime); 
vfprintf(fd, arg1, c_args); 
fprintf(fd, "\n"); 
fflush(fd); 

va_end(args); 
+1

在编辑的部分解决方案是正确的,但如果这是你的完整代码,拷贝不需要...如果之前需要,因为您使用了'va_arg()',它修改了参数。但是现在没有'va_arg()'调用,所以'args == c_args'。 ''va_arg()'通常是一个宏,为了讨论的缘故,你可以这样想:'#define va_list int *'和'#define va_arg(参数,类型)(类型)(*参数++)'' - 这当然是一个巨大的简化,但最终的效果是相似的。 – 2014-11-25 12:58:33

回答

3

当您使用va_arg() “功能”在va_listva_start()获得,va_list被修改,所以当传递到另一个fu它不像你在开始时那样。您可以将va_list作为指针,使用va_arg()就像增量运算符(++)。你叫你的函数是这样的:

log("%s gestartet: PID = %d", argv[0], getpid()); 

但vfprint最有可能看到这样的:

vfprintf(fd, "%s gestartet: PID = %d", /* argv[0], <-- REMOVED */ getpid()); 
+2

我认为解决方法是在'va_arg()'之前调用'va_copy()'。 – user694733 2014-11-25 12:23:52

+2

@ user694733 - 或者根本不调用'va_arg()' - 结果不会被使用(除了在GDB中调试和打印)(;但是复制原始的'va_list'当然也会解决这个问题 – 2014-11-25 12:26:41

+0

我没有注意到'fmt'没有被使用 – user694733 2014-11-25 12:28:27