【问题标题】:String segmentation fault with va_arg带有 va_arg 的字符串分段错误
【发布时间】:2016-02-25 07:41:21
【问题描述】:

我正在制作一个类似于 printf 的函数,它需要接受一个字符串和如下参数:

form("Integer %d, String %s", 54, "STRING");

并创建一个字符串"Integer 54, String STRING"。 我正在使用stdarg.h 库,因为我的函数需要根据字符串具有可变数量的参数。

问题是我收到了Segmentation fault。我发现只有当我使用传递给va_arg 的字符串(char*) 执行strlenstrcpy 时才会发生这种情况。这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

char* form(char *format, ...)
{
    va_list ap;char sign;int br=0,lasti,memo=0;char* help;
    int ints; float floats; double doubles; char chars; char* strings;
    va_start(ap,format);
    char* result=(char*)calloc(100,strlen(format));
    strcpy(result,format);
    for(int i=0; result[i] ;i++)
        if(result[i]=='%')
        {   
            switch (result[i+1])    
            {
                case 'd': {
                    ints=va_arg(ap,int);
                    int b,save=ints,dec=1,j;char *p=result+i;
                    for(b=0;save;b++) {save/=10;dec*=10;}
                    for(dec/=10,j=0; dec ; j++) { p[j]=((ints/dec)%10)+0x30; dec/=10; }
                    strcpy(result+i+b,format+i-memo+2);memo+=b-2;
                } break;

                case 'f': {
                    floats=va_arg(ap,double);

                } break;

                case 'l': {
                    doubles=va_arg(ap,double);

                } break;

                case 'c': {
                    chars=va_arg(ap,int);
                    result[i]=chars;
                } break;

                case 's': {
                    strings=va_arg(ap,char*);
                    strcpy(result+i+strlen(strings),format+i-memo+2);memo+=(strlen(strings)-2);
                } break;

                default: printf("Unknown type.\n"); break;
            }
            i=0;
        }

    return result;
}

int main()
{
    char a[100];
    scanf("%s",a);
    char*s=form("treci %s peti",a);
    printf("%s", s);

    printf("\n");
    free(s);
    return 0;
}

strlen 或 strcpy 可能导致段错误的唯一方法是字符串不是以 null 结尾的,但我的是。那么这里出了什么问题,我该如何解决呢?

编辑:添加代码。

【问题讨论】:

  • 您能尝试创建一个Minimal, Complete, and Verifiable Example 并展示给我们看吗?或者至少向我们展示form 函数的大部分内容?我们确实需要查看变量声明、定义和初始化等内容。
  • 还有你不使用的原因吗? vsnprintf 格式化字符串?您可以让它告诉您它需要的确切长度,因此您可以在需要时为字符串动态分配内存。 If 还会为您提供所有标准格式,包括您不处理的格式、字段宽度等。
  • 我的课程中还没有学习 vsnprintf。
  • if(result[i]=='%') 末尾的 i=0 不正确。
  • strcpy 电话看起来可能是嫌疑人。尝试改为例如memcpy(result + i, strings, strlen(strings)); strcpy(result + i + strlen(strings), format + i + 2);

标签: c string segmentation-fault variadic-functions


【解决方案1】:

仅针对 %s 案例进行测试

char* form(char *format, ...)
{
    va_list ap;char sign;int br=0,lasti,memo=0;char* help;
    int ints; float floats; double doubles; char chars; char* strings;
    va_start(ap,format);
    char* result=(char*)calloc(100,strlen(format));
//    strcpy(result,format);
    for(int i=0; format[i] ;i++)
    {
        if(format[i]=='%')
        {
            switch (format[i+1])
            {
                case 'd': {
                    ints=va_arg(ap,int);
                    int b,save=ints,dec=1,j;char *p=result+i;
                    for(b=0;save;b++) {save/=10;dec*=10;}
                    for(dec/=10,j=0; dec ; j++) { p[j]=((ints/dec)%10)+0x30; dec/=10; }
                    strcpy(result+i+b,format+i-memo+2);memo+=b-2;
                } break;

                case 'f': {
                    floats=va_arg(ap,double);

                } break;

                case 'l': {
                    doubles=va_arg(ap,double);

                } break;

                case 'c': {
                    chars=va_arg(ap,int);
                    result[i]=chars;
                } break;

                case 's': {
                    strings=va_arg(ap,char*);
                    strcpy(result+memo,strings);
                    memo+=strlen(strings);
                } break;

                default: printf("Unknown type.\n"); break;
            }
            i++;
        }
        else
        {
            result[memo++] = format[i];
        }
    }

    return result;
}

很多问题:

  1. strcpy(result+i+strlen(strings),format+i-memo+2); 是对结果的处理格式。您需要将作为变量参数传递的字符串复制到结果字符串。此外result+i+strlen(strings) 没有任何意义:你为什么写这个?
  2. 每次找到格式说明符%时都会有一个i=0:为什么?这是分段错误的根本原因。每次找到 % 时循环都会重新启动,并且请求了一个新的变量参数,但没有其他需要检索。
  3. 您必须有 2 个不同的索引来解析输入格式字符串并索引结果字符串,因为输出字符串可能具有不同的长度。

【讨论】:

  • 因为它不是完整的代码。有了那行,我想为要放在那里的字符串腾出空间。
  • 只有i=0 是问题所在。现在我只需 case 's': {strings=va_arg(ap,char*); strcpy(result+i+strlen(strings),format+i-memo+2);memo+=(strlen(strings)-2);memcpy(result+i,strings,strlen(strings));} break; 就可以了。
  • strcpy(result+i,strings) 应该是 strcpy(result+memo,strings) 以便代码适用于多个格式说明符。
【解决方案2】:

使用vsnprintf 非常简单:

char *form(const char *format, ...)
{
    va_list va;
    va_start(va, format);

    int result = vsnprintf(NUll, 0, format, va);

    // Error checking
    if (result < 0)
        return NULL;

    // Here result is the number of bytes we need to allocate (excluding terminator)
    char *string = malloc(result + 1);

    // Now do the actual formatting
    vsnprintf(string, result + 1, format, va);

    return string;
}

重要提示:记住free你返回的字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多