输入线冻结,分段故障

时间:2014-09-29 00:06:13

标签: c

我正在为一个班级做一个项目并且已经停留了很长一段时间。当我更早地对输入进行单元测试时,它接受了numOfDataSets和createDataSets的值而没有错误。但是,现在,在输入createDataSets的任何值集之后,代码在第一次输入后冻结,直到我输入任何字符(例如1或a),然后出现具有分段错误的错误。我不知道出了什么问题,我将不胜感激。

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

// Function to return the number of data sets the user wants.
int numOfDataSets(void) {
    int ret;
    printf("Enter number of data sets: ");
    scanf("%d", &ret);
    return ret;
}

// Function that creates the data sets in the input arrays. 
void createDataSets(float **inputArr, int inputLength, int *lengths) {
    int i = 0, j, k;
    float value, *currentSet;
    // For every element in inputArr...
    while (i < inputLength) {
        printf("Enter the number of values in this data set, followed by the values: ");
        scanf("%d", &j);
        *(lengths + i) = j;
        currentSet = (float*)calloc(j, sizeof(float));
        k = 0;
        while (k < j-1) {
            scanf("%f", &value);
            *(currentSet + k) = value;
            k++;
        }
        scanf("%f", &value);
        *(currentSet + j - 1) = value;
        *(inputArr + i) = (float*)&currentSet;
        i++;
    }
} 


// Function to get int value of data set to choose.
int chooseDataSet(void) {
    int ret;
    printf("Enter the number of the data set on which you wish to do calculations: ");
    scanf("%d", &ret);
    ret = ret - 1;
    return ret;
}

// Gets the number option of the operation that the user wants to do. 
int getOption(void) {
    int ret;
    printf("Enter one of the following numbers:\n");
    printf("1. Find the minimum value.\n");
    printf("2. Find the maximum value.\n");
    printf("3. Calculate the sum of all the values.\n");
    printf("4. Calculate the average of all the values.\n");
    printf("5. Sort the values in ascending order (i.e., from smallest to largest).\n");
    printf("6. Select a different data set.\n");
    printf("7. Exit the program.\n");
    scanf("%d", &ret);
    return ret;
}

// Function to find the minimum value of a dataset. 
void minimum(float *ptr, int length) {
    int i = 1;
    float min;
    min = *(ptr);
    while (i < length) {
        if (*(ptr + i) < min) {
            min = *(ptr + i);
        }
        i++;
    }
    printf("The minimum value of the set is: %d\n", min);
}

// Function to find the maximum value of a dataset. 
void maximum(float *ptr, int length) {
    int i = 1;
    float max;
    max = *(ptr);
    while (i < length) {
        if (*(ptr + i) > max) {
            max = *(ptr + i);
        }
        i++;
    }
    printf("The maximum value of the set is: %d\n", max);
}

// Function to find the sum of the values of a dataset. 
void sum(float *ptr, int length) {
    int i = 1;
    float sum;
    sum = *(ptr);
    while (i < length) {
        sum = sum + *(ptr + i);
        i++;
    }
    printf("The sum of the set is: %d\n", sum);
}

// Function to find the average of the values of a dataset. 
void average(float *ptr, int length) {
    int i = 1;
    float sum;
    sum = *(ptr);
    while (i < length) {
        sum = sum + *(ptr + i);
        i++;
    }
    sum = sum / length;
    printf("The average of the set is: %d\n", sum);
}

// Function to sort the values of a dataset.
void sort(float *ptr, int length) {
    int i = 1, j;
    float temp;
    while (i < length) {
        j = i;
        while ((j > 0) && (*(ptr + j - 1) > *(ptr + j))) {
            temp = *(ptr + j);
            *(ptr + j) = *(ptr + j - 1);
            *(ptr + j - 1) = temp;
            j--;
        }
        i++;
    }
    printf("The sorted array is: ");
    i = 0;
    while (i < length) {
        printf("%f\t", *(ptr + i));
        i++;
    }
    printf("\n");
}

// Main method...
int main(void) {
    int *lengths, outerLength, userChoiceSet = 0, userChoiceOption = 0, breakOutterLoop = 0;
    float **outer;
    outerLength = numOfDataSets();
    outer = (float**)calloc(outerLength, sizeof(float*));
    lengths = (int*)calloc(outerLength, sizeof(int));
    createDataSets(outer, outerLength, lengths);
    while (breakOutterLoop == 0) {
        userChoiceSet = chooseDataSet();
        while ((userChoiceOption != 6) || (userChoiceOption != 7)) {
            userChoiceOption = getOption();
            switch (userChoiceOption)
            {
                case 1:
                    minimum(*(outer + userChoiceSet), *(lengths + userChoiceSet));
                    break;
                case 2:
                    maximum(*(outer + userChoiceSet), *(lengths + userChoiceSet));
                    break;
                case 3:
                    sum(*(outer + userChoiceSet), *(lengths + userChoiceSet));
                    break;
                case 4:
                    average(*(outer + userChoiceSet), *(lengths + userChoiceSet));
                    break;
                case 5:
                  sort(*(outer + userChoiceSet), *(lengths + userChoiceSet));
                    break;
                case 7:
                    breakOutterLoop = 1;
                default:
                    break;
            }
        }
    }
    return (0);
}

用户期望的输入类型如下:

2
3 1.2 2.3 3.4
4 4.5 5.6 6.7 7.8

1 个答案:

答案 0 :(得分:3)

你的主要问题是createDataSets()

*(inputArr + i) = (float*)&currentSet;

这实际上是将 currentSet地址分配给inputArr的每个元素。此地址在每次迭代时都不会更改,因此inputArr的每个元素都设置为完全相同的值。此外,此地址引用createDataSets()的本地变量,该变量将在该函数返回时被销毁,因此该地址将无效。您正在动态创建的所有阵列都被丢弃,因为您没有存储地址。

你应该拥有的是:

inputArr[i] = currentSet;

正如您在评论中提到的那样,您的编译器会警告您这一点,因为您正在尝试将float **存储在float *中,这很少是一个好主意。通过添加演员你沉默警告,但你没有解决它警告你的问题。 C中实际上你想要做的事情的次数相对较少。你的程序中的所有演员都不是必要的,也不是明智的。

其他几点......

  1. 您在许多printf()来电中使用了错误的格式说明符。这里有%d

    printf("The minimum value of the set is: %d\n", min);
    
    例如,

    应该是%f,因为minfloat

  2. 您过度使用指针表示法,这使您的代码很难遵循。这对也很困难。例如,您的minimum()函数可以更好地编写:

    void minimum(float *ptr, int length) {
        float min = ptr[0];
        for ( int i = 0; i < length; ++i ) {
            if ( ptr[i] < min ) {
                min = ptr[i];
            }
        }
        printf("The minimum value of the set is: %f\n", min);
    } 
    

    同样,在switch语句中,例如:

    average(*(outer + userChoiceSet), *(lengths + userChoiceSet));
    

    写得更清楚:

    average(outer[userChoiceSet], lengths[userChoiceSet]);
    
  3. 您在一些地方错过了对fflush(stdout)的调用,在那里您提示输入但不以'\n'结束提示。当我在我的系统上运行此代码时,提示在它等待输入之前没有显示。交互式输出默认为行缓冲,在C中,如果您希望事物可预测,则需要输出'\n'或在需要显示输出时调用fflush(stdout)

  4. 您可以从更接近使用时间定义变量中受益。将变量范围限制在最小可行范围内通常是好的。例如,在main()函数中,您的变量userChoiceSet永远不会在外部while循环之外使用,因此请在内部使用以下内容进行定义:

    while (breakOutterLoop == 0) {
        int userChoiceSet = chooseDataSet();
    
  5. 您不会在任何地方检查calloc()的回复 - 您必须执行此操作,因为分配可能会失败。 malloc()和朋友在失败时返回NULL。使用calloc()也没有实际意义,这里 - malloc()会更正常。

  6. 您似乎在while循环更自然的地方使用for循环。

  7. 你对这个问题的工作并不是很糟糕,但如果你让每个函数只做一件事,你会发现编写更大的程序更容易。例如,您的minimum()函数应该只计算最小值,但现在它计算它并且打印它。特别是在处理错误格式的输入时(参见下面的第9点)将它包装在一个单独的函数中会使使用该输入的函数更加混乱,并且很容易获得正确的函数并可以直观地调试它如果它没有同时做一堆不同的事情。此外,当您执行此操作时,您重新使用代码的机会会增加(例如,您现在无法在任何想要计算最小值的地方使用minimum()函数而不打印它。)

    < / LI>
  8. 总的来说,为您的值设置一个数组,为其长度设置第二个数组,这不是一个好方法。更好的是拥有一个struct s数组,每个struct都有一个数组成员,以及一个长度成员,所以这两个相关的数据被打包在一起。

  9. 此外,您对scanf()的使用可能很麻烦。如果输入的输入不是预期的,则程序不会正常失败。例如,如果您在主菜单中输入除数字以外的任何内容,那么您将进入无限循环。通常更好的方法是使用fgets()读取整行,并使用sscanf()来解析其内容。至少,您应该检查scanf()的返回值,看它是否成功读取了值,如果没有,请采取适当的补救措施(比如读取输入缓冲区中的所有字符并返回询问更多输入)。

  10. 总的来说,除了最后两点之外,考虑到以上所有因素,您的createDataSets()函数会更好看这样:

    void createDataSets(float **inputArr, const int inputLength, int *lengths) {
        for ( int i = 0; i < inputLength; ++i ) {
            printf("Enter the number of values in this data set, "
                   "followed by the values: ");
            fflush(stdout);
            scanf("%d", &lengths[i]);
    
            float * currentSet = malloc(lengths[i] * sizeof *currentSet);
            if ( !currentSet ) {
                perror("Couldn't allocate memory in createDataSets()");
                exit(EXIT_FAILURE);
            }
    
            for ( int j = 0; j < lengths[i]; ++j ) {
                scanf("%f", &currentSet[j]);
            }
    
            inputArr[i] = currentSet;
        }
    }
    

    首先更容易调试,更容易理解,更容易出错。

    由于我手上有一点时间,所以这就是我的想法:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <ctype.h>
    
    
    /*  Maximum length of input buffer  */
    
    #define MAX_LINE 1024
    
    
    /*  Dataset structure  */
    
    struct dataset {
        float * data;
        size_t length;
    };
    
    
    /*  Gets a single integer from user  */
    
    int getInteger(const char * prompt)
    {
        int value;
        bool first_try = true;
        char buffer[MAX_LINE];
    
        do {
            printf("%s%s: ", first_try ? "" : "Try again - ", prompt);
            fflush(stdout);
            fgets(buffer, MAX_LINE, stdin);
            first_try = false;
        } while ( sscanf(buffer, "%d", &value) != 1 );
    
        return value;
    }
    
    
    /*  Gets a bounded integer from user  */
    
    int getBoundedInteger(const char * prompt, const int min, const int max)
    {
        bool bad_input;
        int value;
    
        do {
            bad_input = false;
            value = getInteger(prompt);
            if ( value < min ) {
                printf("Too low, try again - ");
                bad_input = true;
            }
            else if ( value > max ) {
                printf("Too high, try again - ");
                bad_input = true;
            }
        } while ( bad_input );
    
        return value;
    }
    
    
    /*  Gets a list of floats from user - caller must free  */
    
    float * getFloats(const char * prompt, const int num)
    {
        float * values = malloc(num * sizeof *values);
        if ( !values ) {
            perror("Couldn't allocate memory in getFloats()");
            exit(EXIT_FAILURE);
        }
    
        bool bad_input = false;
    
        do {
            printf("%s%s: ", bad_input ? "Try again - " : "", prompt);
            fflush(stdout);
    
            char buffer[MAX_LINE];
            fgets(buffer, MAX_LINE, stdin);
    
            char * ptr = buffer;
            int num_read = 0;
            bad_input = false;
    
            while ( *ptr && num_read < num ) {
    
                /*  Skip leading whitespace  */
    
                while ( *ptr && isspace(*ptr) ) {
                    ++ptr;
                }
    
                /*  Get and check input  */
    
                char * endptr;
                float val = strtof(ptr, &endptr);
                if ( ptr == endptr ) {
                    bad_input = true;
                    break;
                }
    
                /*  Advance ptr and store input if good  */
    
                ptr = endptr;
                values[num_read++] = val;
            }
    
            if ( num_read < num ) {
                bad_input = true;
            }
        } while ( bad_input );
    
        return values;
    }
    
    
    /*  Returns the number of data sets the user wants.  */
    
    int numOfDataSets(void)
    {
        return getInteger("Enter number of data sets");
    }
    
    
    /*  Creates the data sets  */
    
    void createDataSets(struct dataset ** sets, const int set_length)
    {
        for ( int i = 0; i < set_length; ++i ) {
            struct dataset * new_set = malloc(sizeof *new_set);
            if ( !new_set ) {
                perror("Couldn't allocate memory for dataset");
                exit(EXIT_FAILURE);
            }
    
            new_set->length = getInteger("Enter number of values in set");
            new_set->data = getFloats("Enter values", new_set->length);
            sets[i] = new_set;
        }
    } 
    
    
    /*  Gets the number of data set to choose  */
    
    int chooseDataSet(const int min, const int max)
    {
        return getBoundedInteger("Choose data set", min, max) - 1;
    }
    
    
    /*  Gets a menu choice from the user  */
    
    int getOption(void)
    {
        printf("Enter one of the following numbers:\n");
        printf("1. Find the minimum value\n");
        printf("2. Find the maximum value\n");
        printf("3. Calculate the sum of all the values\n");
        printf("4. Calculate the average of all the values\n");
        printf("5. Sort the values in ascending order\n");
        printf("6. Output the data set\n");
        printf("7. Select a different data set\n");
        printf("8. Exit the program\n");
    
        return getInteger("Choose option");
    }
    
    
    /*  Returns the minimum value in a data set  */
    
    float minimum(const struct dataset * set)
    {
        float min = set->data[0];
        for ( size_t i = 0; i < set->length; ++i ) {
            if ( set->data[i] < min ) {
                min = set->data[i];
            }
        }
        return min;
    }
    
    
    /*  Returns the maximum value in a data set  */
    
    float maximum(const struct dataset * set)
    {
        float max = set->data[0];
        for ( size_t i = 0; i < set->length; ++i ) {
            if ( set->data[i] > max ) {
                max = set->data[i];
            }
        }
        return max;
    }
    
    
    /*  Returns the sum of the data in a dataset  */
    
    float sum(const struct dataset * set)
    {
        float sum = 0;
        for ( size_t i = 0; i < set->length; ++i) {
            sum += set->data[i];
        }
        return sum;
    }
    
    
    /*  Returns the arithmetic average of the data in a dataset  */
    
    float average(const struct dataset * set)
    {
        float sum = 0;
        for ( size_t i = 0; i < set->length; ++i ) {
            sum += set->data[i];
        }
        return set->length > 0 ? sum / set->length : sum;
    }
    
    
    /*  Sorts the elements of a dataset in place  */
    
    void sort(struct dataset * set)
    {
        for ( size_t i = 0; i < set->length; ++i ) {
            for ( size_t j = i; j && set->data[j-1] > set->data[j]; --j ) {
                float temp = set->data[j];
                set->data[j] = set->data[j-1];
                set->data[j-1] = temp;
            }
        }
    }
    
    
    /*  Prints a dataset  */
    
    void print_set(const struct dataset * set) {
        for ( size_t i = 0; i < set->length; ++i ) {
            printf("%.4f ", set->data[i]);
        }
        putchar('\n');
    }
    
    
    /*  Main function  */
    
    int main(void)
    {
        /*  Get and initialize sets  */
    
        const int num_sets = numOfDataSets();
    
        struct dataset ** sets = malloc(num_sets * sizeof *sets);
        if ( !sets ) {
            perror("Couldn't allocate memory for sets");
            return EXIT_FAILURE;
        }
    
        createDataSets(sets, num_sets);
    
    
        /*  Main menu  */
    
        int chosen_set = chooseDataSet(1, num_sets);
        bool keep_going = true;
    
        while ( keep_going ) {
            switch ( getOption() )
            {
                case 1:
                    printf("Minimum value is %f\n\n",
                           minimum(sets[chosen_set]));
                    break;
    
                case 2:
                    printf("Maximum value is %f\n\n",
                           maximum(sets[chosen_set]));
                    break;
    
                case 3:
                    printf("Sum of values is %f\n\n",
                           sum(sets[chosen_set]));
                    break;
    
                case 4:
                    printf("Average of values is %f\n\n",
                           average(sets[chosen_set]));
                    break;
    
                case 5:
                    sort(sets[chosen_set]);
                    break;
    
                case 6:
                    print_set(sets[chosen_set]);
                    break;
    
                case 7:
                    chosen_set = chooseDataSet(1, num_sets);
                    break;
    
                case 8:
                    keep_going = false;
                    break;
    
                default:
                    break;
            }
        }
    
        /*  Free memory for sets  */
    
        for ( int i = 0; i < num_sets; ++i ) {
            free(sets[i]->data);
            free(sets[i]);
        }
        free(sets);
    
        return 0;
    }