2014-09-29 55 views
0

我正在为一个类的项目工作,并且已经停留了很长一段时间。当我的单元先前测试了输入时,它接受了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

一个问题是,你写的复杂上下的代码页没有必要的调试技能得到它工作。请学习如何调试您编写的软件。这是一项绝对必要的技能。令人遗憾的是'用调试器遍历代码并检查每个步骤的值'可能是C标签中一半问题的答案。 – 2014-09-29 00:20:19

+1

'*(inputArr + i)=(float *)&currentSet;'显然显然是错误的。为什么不必要的演员是一个坏主意的另一个例证。如果没有这种强制转换,你的编译器会立即告诉你,你正试图将一个'float **'填充到'float *'中。当语言为您提供免费的类型检查时,通常利用它是个好主意。 – 2014-09-29 00:25:59

+0

@PaulGriffiths我没有意识到这是一个问题,我实际上是从gcc那里得到了一个关于这个的警告,并认为这个剧组是一种修复它的方法。我认为通过在左侧参数前面有解引用运算符,我将存储在该地址的指针的值赋给我刚刚创建的浮点指针,但根据您的注释,它会显示我需要在那里做别的事,虽然我不确定是什么。 – brostone51 2014-09-29 01:01:10

回答

3

你的主要问题是这样的,在createDataSets()

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

什么这实际上做的是分配地址currentSetinputArr的每个元素。该地址在每次迭代中都不会改变,因此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)的电话,在这些地方您提示输入,但不要以01结束提示。当我在系统上运行此代码时,在等待输入之前没有显示提示。交互式输出在默认情况下为行缓冲,以C为单位,如果您希望事情可预测,则需要输出'\n'或在需要显示输出时调用fflush(stdout)

  4. 您将从更接近使用时间的变量中获益。将变量的范围限制在可行的最小范围内通常是很好的。例如,在你的main()功能,您的变量userChoiceSet从不外while循环之外使用,所以里面定义它:

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

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

  7. 你还没有做过这么糟糕的工作,但是如果你让每个功能只做一件事,你会发现写更大的程序更容易。例如,您的minimum()函数应该只计算最小值,但现在它计算它将其打印出来。特别是当处理输入格式错误(参见下面的第9点)时,将其包含在单独的函数中会使得使用该输入的函数更加混乱,并且很容易获得正确的函数并对其进行可视化调试如果它一次不做一堆不同的事情。此外,当您这样做时,您重复使用代码的机会会增加(例如,现在您无法在任何想要计算最小值但不打印它的地方使用该函数)。总的来说,对于你的值有一个数组,而对于它们的长度只有一个数组,并且不是一个好的方法。更好的办法是有一个struct的数组,每个struct都有一个数组的成员,并且有一个成员的长度,所以两个相关的数据片段被打包在一起。

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

总体来看,轴承都在考虑到上述的除了最后两个点,你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; 
} 
+1

很棒的回答。你有更多的时间在你手中:) – 2014-09-29 06:54:03

+0

非常感谢你的帮助,我对C真的很陌生。至于指针混淆的表示法,这是这项任务所需要的。 – brostone51 2014-09-29 18:58:44