简体   繁体   中英

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. 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.

#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!

code output

You want to allocate a two dimensional array of size nxm . You ask the user for the dimensions. Then you allocate the amount of memory.

But unfortunately the compiler does not know those dimensions and addressing as studentArray[i][j] will fail: how long is row 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] .

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. 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. This is the 2-step allocation you would use allocating for a collection of strings, or structs, etc.. where you Simulate a 2D array. 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

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

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.

Memory Use/Error Check

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.

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.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. 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.

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.

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:

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] ...

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 **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 "**";

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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