可能的内存分配问题?

Possible Memory Allocation Issue?

编码员们好, 我在为 Uni 编写的代码中遇到了一些问题,我正在寻找一些建议。好像没经过整个for循环就把我吐出来了,只能到student 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;
}

尝试将 gcc -Wl,--stack=268435456 -Wl,--heap=268435456 添加到链接器设置,但程序会在同一位置崩溃在此先感谢!

code output

您想分配一个大小为 nxm 的二维数组。您向用户询问尺寸。然后你分配内存量。

但不幸的是,编译器不知道这些维度,并且寻址为 studentArray[i][j] 将失败:行 i 有多长?

在这种情况下,您必须将寻址明确写为

studentArray[i*subjects+j]

Paul Ogilvie 的回答直接解决了核心问题——您实际上是在使用扁平化为一维的二维数组,因此您使用 [i * w + j].

对其进行索引

然而,如果你所做的只是一次计算每个学生的总和和平均值,你甚至根本不需要数组,因为你可以将分数累加到 sum,然后除以伯爵。假设你的代码的另一个版本确实需要数组,不过,这里有一个稍微更干净的代码版本,它将东西分为输入阶段和 output/computation 阶段。

#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;
}

你的另一个选择是为你的pointer-to-pointer-toint分配你首先分配numberOfStudentspointers 然后循环并分配一个 numberofSubjects int 的块并将起始地址依次分配给每个分配的指针。这是您将用于分配字符串集合或结构等的两步分配。您可以在其中 Simulate 二维数组。这看起来像你要去的方向。

为此,您必须首先分配(并验证)您的指针块,为每个学生分配一个指针:

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

接下来,在收集数据的循环中,分配一个 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];
        }

总而言之,您将拥有:

#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 */
}

(注意: 因为这是一个两步分配,它需要一个两步 free()。当你完成你的整数时,你循环并free (studentArray[i]); 这将释放所有整数存储。最后一步是释放分配的指针,例如 free (studentArray);

例子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

请注意,您只需将除法的一部分转换为(float),分子或分母都可以。

内存Use/Error检查

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。

对于Linux valgrind是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。

$ 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)

始终确认您已释放所有分配的内存并且没有内存错误。

检查一下,如果您还有其他问题,请告诉我。

您的代码混合了一个简单的二维数组分配,稍后将作为 arr[j + i * numberOfSubjects] 访问,以及一个 2 级指针数组和数据数组。

后者的防弹方法是分配2个数组,一个用于指针,一个用于数据,并初始化指针数组以正确使用二维数据数组:

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

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

您现在可以安全地使用 arr[i][j]...

一种相当高级(但不太健壮)的方法是在一次传递中分配两个数组。问题是,除非您确定 int 的实现没有比 int * 更严格的对齐方式,否则这是灾难的根源,而且我不确定它是否真的符合标准:

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

它曾经是一个常见的习惯用法,它是您初始代码中包含的内容,但除非您有非常充分的理由这样做,否则我建议您使用单独的分配。