从C中的字符串中提取十进制数会导致分段错误

时间:2019-12-26 18:46:49

标签: c arrays string double

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

int Extract(char input[], double output[])
{
    int i, j, len;
    i=0;
    j=0;
    len=0;
    char s[50];
    while(i<strlen(input)-1)
    {
        if(input[i]==' ') i++;
        j=0;
        s[0]='\0';
        while(input[i]!=',')
        {
            if(input[i]==' ') i++;
            s[j]=input[i];
            i++;
            j++;
        }
        s[j]='\0';
        i++;
        printf("%s - ", s);
        output[len]=(double)atof(s);            
        printf("Element %d: %lf\n", len, output[len]);
        len++;
    }
    printf("%d", len);
    return len;
}

int main(){
    char s[120]="0.1,0.35,0.05,0.1,0.15,0.05,0.2.";
    double v[1000];
    int len = Extract(s, v);
    int i;
    for(i=0; i<len; i++)
    {
        printf("%d: %lf\n", i, v[i]);
    }
    return 1;
}

我尝试运行此代码,但是即使编译正确,我也会出现堆栈错误,有人可以帮助我吗? 请注意,该字符串由一些用逗号分隔的十进制数字组成,并且该字符串以。结尾。

更新:也许文件夹中有些脏东西,但是现在我有一个输出: 长度:32
0.1-元素0:0.000000
0.35-元素1:0.000000
0.05-元素2:0.000000
0.1-元素3:0.000000
0.15-元素4:0.000000
0.05-元素5:0.000000
分段错误(核心已转储)

自从我已经创建线程以来,我还能继续利用您的帮助将字符串转换为double,因为atof正在转换为float,这可能就是它打印所有0.0000的原因了吗?

2 个答案:

答案 0 :(得分:0)

我认为我发现了问题:

  • 您的循环仅检查字符是否不是','。在输入结束时,您没有','字符,而是有'.',这将导致循环永远持续下去,从而导致段错误。您可以通过将输入的最后一个字符更改为','来解决此问题。
  • 您的浮点输出格式不正确。如果只需要打印两个有效数字,则将%lf更改为%.2lf
  • 顺便说一句,您检查输入中是否有空格,但看起来输入中没有空格。也许把那些支票拿出来?

这完全取决于您对输入的调节程度。如果可以,请先处理输入,然后再将其输入函数。

让我们知道是否有帮助!

答案 1 :(得分:0)

在这里,我介绍3个代码,第一个解决段错误问题;第二个是对提取功能的详细说明(如所展示);第三,以许多可能的改进形式之一编写的Extract函数示例。

主要的收获应该是始终防止代码中的缓冲区(数组)溢出,并与调试器交朋友。

代码中printf的使用表明没有使用调试器。对任何琐碎的事情(您好,世界吗?)进行编码,都会使调试器的知识更加丰富。了解调试器之类的工具与了解该语言一样重要。

我希望这可以作为指导,甚至可以作为启发。祝您编程愉快。

这是原始代码,其中进行了最少的更改以修复segfault(数组溢出)

int Extract(char input[], double output[])
{
    int i, j, len;
    i = 0;
    j = 0;
    len = 0;
    char s[50];
    while (i<strlen(input) - 1)
    {       
        if (input[i] == ' ') i++;
        j = 0;
        s[0] = '\0';
        /* Primary bug fix; guard against input array overrun *and* check for separator */
        while (input[i] && input[i] != ',')
        {
            if (input[i] == ' ') i++;
            s[j] = input[i];
            i++;
            j++;
        }
        s[j] = '\0';
        /* bug fix; guard against input array overrun when incrementing */
        if (input[i]) {
            i++;
        }       
        printf("%s - ", s);
        output[len] = (double)atof(s);
        printf("Element %d: %lf\n", len, output[len]);
        len++;
    }
    printf("%d", len);
    return len;
}

这里是对原始代码的批评。

int Extract(char input[], double output[])
{
    /* Tedious variable declaration and initialization */
    int i, j, len;
    i = 0;
    j = 0;
    len = 0;
    /* why not 70? or 420? */
    char s[50];
    /* This is an exceedingly expensive way to determine end of string. */
    while (i<strlen(input) - 1)
    {
        /* Why test for space? There are no spaces in sample input.
        This increment risks overrunning the input array (segfault)
        */
        if (input[i] == ' ') i++;
        j = 0;
        s[0] = '\0';
        /* no guard against input array overrun */
        while (input[i] != ',')
        {
            /* Why test for space? There are no spaces in sample input.
            This increment risks overrunning the input array (segfault)
            */
            if (input[i] == ' ') i++;           
            s[j] = input[i];
            i++;
            j++;
        }
        s[j] = '\0';
        /* Bug - no guard against input array overrun when incrementing i */    
        i++;    
        /* these print statements suggest someone is NOT using a debugger - major fail if so. */
        printf("%s - ", s);
        output[len] = (double)atof(s);
        printf("Element %d: %lf\n", len, output[len]);
        len++;
    }
    /* again, this is easily seen in the debugger.  Use the debugger. */
    printf("%d", len);
    return len;
}

最后,另一种具有某些(樱桃选择)约定的摘录。

int Extract(double* output, const int output_max, const char* input, const char separator)
{
    /* declare variables in the scope they're needed and ALWAYS give variables meaningful names */
    int input_index = 0, output_count = 0;
    /* Detect end of string and guard against overrunning output buffer */
    while (input[input_index] && output_count < output_max)
    {   
        const int BUFFER_MAX = 50;
        /* let the compiler init buffer to 0 */
        char buffer[BUFFER_MAX] = { 0 };
        int buffer_index = 0;
        /* accumulate values into buffer until separator or end of string encountered */
        while (input[input_index] && input[input_index] != separator)
        {           
            buffer[buffer_index++] = input[input_index++];
            if (buffer_index == BUFFER_MAX) {
                /* Overrun, cannot process input; exit with error code. */
                return -1;
            }
        }           
        /* only convert buffer if it had accumulated values */
        if (buffer_index) {
            /* note atof will discard, say, a trailing period */
            output[output_count++] = atof(buffer);
        }   
        /* Guard against input_index increment causing an array overrun (possible segfault) */
        if (input[input_index]) {
            input_index++;
        }
    }
    return output_count;
}


int main() {
    const int OUTPUT_MAX = 1000;
    const char separator = ',';
    const char* input = "0.1 ,0.35,0.05,0.1,0.15,0.05,0.2.";    
    double output[OUTPUT_MAX];

    const int num_elems = Extract(output, OUTPUT_MAX, input, separator);
    /* print results to stdout */
    if (num_elems == -1) {
        fprintf(stdout, "\nElement too long to process error\n");
    }
    else {
        fprintf(stdout, "\nTotal number of elements: %d\n\n", num_elems);
        for (int i = 0; i < num_elems; i++) {
            fprintf(stdout, "Element %d: %lf\n", i, output[i]);
        }
    }   
    return num_elems;
}

祝您编程愉快。