简体   繁体   English

可能的 Memory 分配问题?

[英]Possible Memory Allocation Issue?

G'day Coders, I'm having some issues with a code I'm writing for Uni and I'm looking for some advice. G'day Coders,我为 Uni 编写的代码遇到了一些问题,我正在寻找一些建议。 It seems to spit me out without going through the whole for loop, I can only get up to student 3. Any help would be appreciated.似乎没有经过整个 for 循环就把我吐出来了,我只能到学生 3。任何帮助将不胜感激。

#include<stdio.h>
#include <stdlib.h>//for the malloc function

int main()
{
    int num;//user input of number of students
    printf("enter the number of students: ");
    scanf("%d",&num);

    //user input of number of subjects
    int subjects;
    printf("enter the number of subjects: ");
    scanf("%d",&subjects);

    int *ptr, **arr;

    //making 2d dynamic array of size nX subjects with the help of malloc
    int len = sizeof(int *) * num + sizeof(int) * subjects * num;
    arr = (int **)malloc(len);//will allocate the memory of size len dynamically
    ptr = (int *)(arr + num);


    int sum=0;//total sum of marks of a student
    float average;//average of marks

    //iterating for each student
    for(int i=0;i<num;i++)
    {
        //user input the marks of each subject from a user
        printf("enter the marks of student %d: ",i+1);
        for(int j=0;j<subjects;j++)
        scanf("%d",&arr[i][j]);

        //summing up the total marks of the student
        for(int j=0;j<subjects;j++)//iterating for each subject
        sum+=arr[i][j];
        printf("the total marks of student %d is %d \n",i+1,sum);//printing the total marks

        //average of the marks of the student
        average=(float)sum/(float)subjects;//average is equal to total sum divided by the total subjects
        printf("and the average is %0.2f \n",average);

        //making sum and average again 0 for the next student
        sum=0;
        average=0;
    }

 return 0;
}

Tried adding gcc -Wl,--stack=268435456 -Wl,--heap=268435456 to linker settings but program would crash at same place Thanks in Advance!尝试将 gcc -Wl,--stack=268435456 -Wl,--heap=268435456 添加到 linker 设置,但程序会在同一个地方崩溃提前谢谢!

code output代码 output

You want to allocate a two dimensional array of size nxm .您想分配一个大小为nxm的二维数组。 You ask the user for the dimensions.您向用户询问尺寸。 Then you allocate the amount of memory.然后分配 memory 的数量。

But unfortunately the compiler does not know those dimensions and addressing as studentArray[i][j] will fail: how long is row i ?但不幸的是,编译器不知道这些维度和寻址,因为studentArray[i][j]将失败:第i行有多长?

In this case you must explicitly write the addressing as在这种情况下,您必须明确地将寻址写为

studentArray[i*subjects+j]

Paul Ogilvie's answer fixes the core issue right up – you're really working with a 2D array flattened into 1D, so you index it with [i * w + j] . Paul Ogilvie 的回答直接解决了核心问题——您实际上是在使用扁平化为 1D 的 2D 数组,因此您可以使用[i * w + j]对其进行索引。

However, if all you do is compute the sum and average of each student at a time, you don't even need arrays at all, since you can just accumulate marks into sum , then divide by the count.但是,如果您所做的只是一次计算每个学生的总和和平均值,那么您甚至根本不需要 arrays,因为您只需将分数累加到sum中,然后除以计数。 Assuming a further version of your code does really need the array, though, here's a slightly more cleaned-up version of your code that divides things into the input stage and the output/computation stage.假设您的代码的另一个版本确实需要数组,但是,这里有一个稍微更干净的代码版本,它将事物分为输入阶段和输出/计算阶段。

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

int main() {
  int students, subjects;
  printf("enter the number of students: ");
  scanf("%d", &students);
  printf("enter the number of subjects: ");
  scanf("%d", &subjects);
  // TODO: add bounds checks for students / subjects

  // Allocate memory for students X subjects integers;
  // calloc ensures the memory is zeroed too.
  int *marks = calloc(students * subjects, sizeof(int));

  // TODO: check the marks allocation succeeded

  for (int i = 0; i < students; i++) {
    printf("enter the marks of student %d: ", i + 1);
    for (int j = 0; j < subjects; j++) {
      scanf("%d", &marks[i * subjects + j]);
    }
  }

  for (int i = 0; i < students; i++) {
    int sum = 0;
    printf("Student %d: ", i + 1);
    for (int j = 0; j < subjects; j++) {
      int mark = marks[i * subjects + j];
      printf("%d ", mark);
      sum += mark;
    }
    printf(" - ");
    float average = sum / subjects;
    printf("Total: %d, average: %.2f\n", sum, average);
  }
  return 0;
}

Your other option is to allocate for your pointer-to-pointer-to int where you first allocate numberOfStudents pointers and then loop and allocate a block of numberofSubjects int and assign the beginning address to each of your allocated pointers in turn.您的另一个选择是在您首先分配numberOfStudents指针的地方分配指向int的指针,然后循环并分配一个numberofSubjects int块,并依次将起始地址分配给每个分配的指针。 This is the 2-step allocation you would use allocating for a collection of strings, or structs, etc.. where you Simulate a 2D array.这是您将用于分配字符串或结构等集合的两步分配。您可以在其中模拟2D 数组。 This looks like the direction you were heading.这看起来像您前进的方向。

To do so, you must first allocate (and validate) your block of pointers allocating one pointer for each of the students:为此,您必须首先分配(并验证)您的指针块,为每个学生分配一个指针:

    /* allocate numberOfStudents pointers */
    studentArray = malloc (numberOfStudents * sizeof *studentArray);
    
    if (studentArray == NULL) { //check if memory allocated successfully
        perror ("malloc-studentArray pointers");
        return 1;
    }

Next, in your loop where you collect the data, you allocate a block of numberofSubjects integers and assign the starting address to your next open pointer, eg接下来,在您收集数据的循环中,您分配一个numberofSubjects整数块并将起始地址分配给您的下一个打开指针,例如

    for (i = 0; i < numberOfStudents; i++) {
        studentArray[i] = malloc (numberofSubjects * sizeof *studentArray[i]);
        if (!studentArray[i]) {
            perror ("malloc-studentArray[i]");
            return 1;
        }
        printf ("enter the marks of student %d: ", i + 1);
        for (j = 0; j < numberofSubjects; j++) {
            scanf ("%d", &studentArray[i][j]);
            sum += studentArray[i][j];
            totalMarks += studentArray[i][j];
        }

Putting it altogether, you would have:总而言之,您将拥有:

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

int main (void)
{
    int numberOfStudents, numberofSubjects, sum = 0, i, j, totalMarks = 0;
    int **studentArray;
    printf ("Enter the Number of Students and Number of Subjects: ");
    scanf ("%d%d", &numberOfStudents, &numberofSubjects);

    /* allocate numberOfStudents pointers */
    studentArray = malloc (numberOfStudents * sizeof *studentArray);
    
    if (studentArray == NULL) { //check if memory allocated successfully
        perror ("malloc-studentArray pointers");
        return 1;
    }

    for (i = 0; i < numberOfStudents; i++) {
        studentArray[i] = malloc (numberofSubjects * sizeof *studentArray[i]);
        if (!studentArray[i]) {
            perror ("malloc-studentArray[i]");
            return 1;
        }
        printf ("enter the marks of student %d: ", i + 1);
        for (j = 0; j < numberofSubjects; j++) {
            scanf ("%d", &studentArray[i][j]);
            sum += studentArray[i][j];
            totalMarks += studentArray[i][j];
        }
        
        float average = (float)sum / numberofSubjects;
        printf ("The average for student %d is %0.2f \n", j + 1, average);
        average = 0;
        sum = 0;

    }
    printf ("The Total Marks of Students is %d \n", totalMarks);
    
    for (i = 0; i < numberOfStudents; i++)
        free (studentArray[i]);             /* free block of integers */
    free (studentArray);                    /* free pointers */
}

( note: since this is a 2-step allocation, it requires a 2-step free() . When you are done with your integers, you loop and free (studentArray[i]); which will free all integer storage. The last step is freeing the allocated pointers, eg free (studentArray); 注意:由于这是一个 2 步分配,它需要一个 2 步free() 。当你处理完整数后,循环并free (studentArray[i]);这将释放所有 integer 存储。最后一个步骤是释放分配的指针,例如free (studentArray);

Example Use/Output示例使用/输出

$ ./bin/ptrissue
Enter the Number of Students and Number of Subjects: 3 4
enter the marks of student 1: 91 82 81 78
The average for student 5 is 83.00
enter the marks of student 2: 94 92 96 88
The average for student 5 is 92.50
enter the marks of student 3: 81 79 73 84
The average for student 5 is 79.25
The Total Marks of Students is 1019

Note, you need only cast one part of the division to (float) , either the numerator or denominator will do.请注意,您只需要将除法的一部分转换为(float) ,分子或分母都可以。

Memory Use/Error Check Memory 使用/错误检查

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.在您编写的任何动态分配 memory 的代码中,对于分配的 memory 的任何块,您有两个责任:(1)始终保留指向 ZCD69B4957F06CD818D7BF3D21980 块的起始地址的指针,所以它可以被释放,更需要。

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.您必须使用 memory 错误检查程序,以确保您不会尝试访问 memory 或写入超出/超出分配块的边界,尝试读取或基于未初始化值的条件跳转,最后确认释放所有已分配的 memory。

For Linux valgrind is the normal choice.对于 Linux valgrind是正常的选择。 There are similar memory checkers for every platform.每个平台都有类似的 memory 检查器。 They are all simple to use, just run your program through it.它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/ptrissue
==14645== Memcheck, a memory error detector
==14645== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14645== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==14645== Command: ./bin/ptrissue
==14645==
Enter the Number of Students and Number of Subjects: 3 4
enter the marks of student 1: 91 82 81 78
The average for student 5 is 83.00
enter the marks of student 2: 94 92 96 88
The average for student 5 is 92.50
enter the marks of student 3: 81 79 73 84
The average for student 5 is 79.25
The Total Marks of Students is 1019
==14645==
==14645== HEAP SUMMARY:
==14645==     in use at exit: 0 bytes in 0 blocks
==14645==   total heap usage: 6 allocs, 6 frees, 2,120 bytes allocated
==14645==
==14645== All heap blocks were freed -- no leaks are possible
==14645==
==14645== For counts of detected and suppressed errors, rerun with: -v
==14645== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.始终确认您已释放所有已分配的 memory 并且没有 memory 错误。

Look things over and let me know if you have further questions.如果您还有其他问题,请仔细查看并告诉我。

Your code was a mix of a simple 2D array allocation which is to be later accessed as arr[j + i * numberOfSubjects] , and a 2 level array of pointer and array of data.您的代码混合了一个简单的 2D 数组分配,稍后将作为arr[j + i * numberOfSubjects]访问,以及一个 2 级指针数组和数据数组。

The bullet proof way for the latter is to allocate 2 arrays, one for the pointers and one for the data, and initialize the array of pointers to correctly use the 2D data array:后者的防弹方法是分配2个arrays,一个用于指针,一个用于数据,并初始化指针数组以正确使用二维数据数组:

int **arr = malloc(numberOfStudends * sizeof(int*));
int *data = malloc(numberOfStudents * numberOfSubjects * sizeof(int));

for(i=0; i<numberOfStudents; i++) {
    arr[i] = data + i * numberOfSubjects;
}

You can now safely use arr[i][j] ...您现在可以安全地使用arr[i][j] ...

A rather advanced (but less robust) way is to allocate both array in one single pass.一种相当先进(但不太健壮)的方法是一次分配两个数组。 The problem is that unless you are sure that the implementation has not a stricter alignment for int than int * it is a recipe for disaster, and I am not really sure whether it is really standard conformant:问题是,除非您确定实现对于int没有比int *更严格的 alignment,否则这是灾难的根源,我不确定它是否真的符合标准:

int **arr = malloc(numberOfStudends * sizeof(int*)
                   + numberOfStudents * numberOfSubjects * sizeof(int))
int *data = (int *)(arr + numberOfStudents);

It used to be a common idiom, and it is what you initial code contains, but unless if you have a very strong reason to do so, I advise you to use separate allocations.它曾经是一个常见的习惯用法,它是您的初始代码包含的内容,但除非您有非常充分的理由这样做,否则我建议您使用单独的分配。

why don't use "[]" instead of "**";为什么不使用“[]”而不是“**”;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM