1

I'm experimenting with the dynamic memory allocation of variable sized arrays. The function "ft_ultimate_range" seems to work, however, a problem arises when I try to access and print the values of each array's array. It signals either segmentation fault: 11, when compiled with GCC, or "subscripted value is neither array nor pointer nor vector" with [http://pythontutor.com/c][1]. I understand that this has to do with the use- or abuse of pointers... ehm

// allocate a grid of [n][n] with numbers ranging max-min
int   *ft_ultimate_range(int **range, int min, int max)
{
    int len, *ptr;
    int count = min;

    len = max - min;
    ptr = range;
    // allocate memory **arr
    range = malloc(len * sizeof * range);
    for(int i = 0; i < len; i++) {
          range[i] = malloc(len * sizeof * ptr);
    }

    // assign values to allocated memory location
    for(int i = 0; i < len; i++) {
      for(int j = 0; j < len; j++) {
        range[i][j] = count++;
      }
      count = min;
    }

    // free memory **range
    for(int i = 0; i < len; i++) {
      free(range[i]);
    }
    free(range);

    return ptr;
}

int main() 
{
  int n;

  n = 6 - 3;
  int **ptr, *arr;
  arr = ft_ultimate_range(ptr, 3, 6);
  // print 
  for(int i = 0; i < n; i++) {
    for(int j = 0; j < n; j++) {
        printf("%d", ptr[i][j]);
    }
  }
  return 0; 
} 

...a little pointer in the right direction would be very much appreciated.

mch
  • 9,424
  • 2
  • 28
  • 42
Mike
  • 61
  • 8
  • 3
    1. Function arguments are copied and assignments to that won't affect caller's local variables. 2. You cannot use freed buffer. If you want to let `main()` use buffer allocated in `ft_ultimate_range()`, do not free them. – MikeCAT Mar 04 '19 at 09:19
  • @Spinkoo I do not understand your comment. The use of `sizeof` in the snippet looks fine. – mch Mar 04 '19 at 09:21
  • It seems to work? "It signals either segmentation fault: 11" means it doesn't work. It's in fact thoroughly broken, starting with the signature. – n. m. could be an AI Mar 04 '19 at 09:22
  • @mch `ptr` and `range` are the same, but the sizes of allocated elements should not be the same. One is an integer and the other is a pointer. – n. m. could be an AI Mar 04 '19 at 09:25
  • @Spinkoo yes, it compiles: https://ideone.com/DTTw5Y – mch Mar 04 '19 at 09:26
  • @n.m. `range[i] = malloc(len * sizeof * ptr);` is correct, changing it to `range[i] = malloc(len * sizeof(ptr));`, what I think Spinkoo suggested with his comment, will make it incorrect. – mch Mar 04 '19 at 09:28
  • @mch no it is not. `ptr` is a pointer to a pointer to an `int`. `*ptr` is a pointer to an `int`. One needs `sizeof(int)`, not `sizeof(int*)` here. – n. m. could be an AI Mar 04 '19 at 09:30
  • @mch sorry my bad! I looked at `ptr = range;` and assumed they are the same type. In fact this assignment is incorrect, not the use of `sizeof`. – n. m. could be an AI Mar 04 '19 at 09:34
  • 1
    Consider [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) instead. – Lundin Mar 04 '19 at 09:55

3 Answers3

2

Well, it obvious you are quite lost regarding how to return an allocated grid (pointer-to-pointer-to-int) from ft_ultimate_range() to main().

To begin, you do not need to pass range as a parameter to ft_ultimate_range(). Instead, make the return type int ** and declare int **range within ft_ultimate_range() and then allocate len pointers, and then allocate len integers per-pointer, assign the values and then return range and assign it to arr in main(), e.g.

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

/* allocate a grid of [n][n] with numbers ranging max-min */
int **ft_ultimate_range (int min, int max)
{
    int len = max - min,
        **range = NULL,
        count = min;

    /* allocate len pointers */
    range = malloc (len * sizeof * range);
    if (!range) {           /* validate EVERY allocation */
        perror ("malloc-range");
        return NULL;
    }

    /* allocate len int per-pointer */
    for (int i = 0; i < len; i++) {
        range[i] = malloc (len * sizeof *range[i]);
        if (!range[i]) {    /* validate alloation */
            perror ("malloc-range[i]");
            while (i--)             /* free previously allocated rows */
                free (range[i]);    /* free pointers */
            free (range);
            return NULL;
        }
    }

    /* assign values to allocated memory location */
    for(int i = 0; i < len; i++) {
        for(int j = 0; j < len; j++) {
            range[i][j] = count++;
        }
        count = min;
    }

    return range;
}

(note: you MUST VALIDATE EVERY ALLOCATION...)

In main(), you don't need ptr, all you need is the int** pointer you will assign the return from ft_ultimate_range() to, e.g.

int main (void) {

    int n = 6 - 3;
    int **arr;

    arr = ft_ultimate_range (3, 6);
    if (!arr)       /* validate return */
        return 1;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            printf (" %d", arr[i][j]);
        }
        putchar ('\n');
        free (arr[i]);
    }
    free (arr);

    return 0; 
} 

(note: similarly, you must validate the return of ft_ultimate_range() before blindly looping through the values (that will not be there if an allocation failed)).

Example Use/Output

$ ./bin/alloc_a_grid
 3 4 5
 3 4 5
 3 4 5

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 insure 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/alloc_a_grid
==29026== Memcheck, a memory error detector
==29026== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==29026== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==29026== Command: ./bin/alloc_a_grid
==29026==
 3 4 5
 3 4 5
 3 4 5
==29026==
==29026== HEAP SUMMARY:
==29026==     in use at exit: 0 bytes in 0 blocks
==29026==   total heap usage: 4 allocs, 4 frees, 60 bytes allocated
==29026==
==29026== All heap blocks were freed -- no leaks are possible
==29026==
==29026== For counts of detected and suppressed errors, rerun with: -v
==29026== 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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1

Here:

int **ptr, *arr;
arr = ft_ultimate_range(ptr, 3, 6);

ptr is NULL / uninitialised now. You cant allocate of this. malloc must be before call a function ft_ultimate_range. Here: printf("%d", ptr[i][j]); is NULL[i][j].

And you free **ptr in function ft_ultimate_range.

Hope this help.

Igor Galczak
  • 142
  • 6
0
int **ptr, *arr;

ptr is uninitialised at this point. It's value is indeterminate.

arr = ft_ultimate_range(ptr, 3, 6);

Passing an uninitialised value to a function is always wrong. If you hope the function will initialise it, you are mistaken. Read on pass by value.

ptr = range;

This assignment is wrong. You should have received a compiler warning about it. If you did not, upgrade your compiler ASAP. Never ignore warnings. Toying with a program that compiles with warnings is a waste of time.

for(int i = 0; i < len; i++) {
  free(range[i]);
}
free(range);

At this point, every pointer in the program is invalid. Dereferencing them is undefined behaviour.

It isn't at all clear what your program is supposed to return. You are not using the return value anyway.

I suggest changing the signature of your function to this:

int** ft_ultimate_range(int min, int max)

Try to fit whatever you are trying to do in this mold.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • @Lundin I'm not sure what you mean. How would you pass an ADT variable to a function? You normally cannot even create one in the caller. You pass a *pointer* to an ADT, and it should be initialised. Passing pointers to uninitialised memory around is of course OK. It's the pointer itself that must be initialised. – n. m. could be an AI Mar 04 '19 at 10:00
  • Ah now I understand, you mean that passing the uninitialized value of what's inside `int**` is always wrong, since the function can't do anything meaningful with that parameter. That's correct, of course. – Lundin Mar 04 '19 at 10:29