0

I am trying to allocate the row dimension of an array (a) to the size of a user input value. Here is the code I have:

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

void FreeArray(int **arry, int N);

int main() {
    int **a;
    int **b;
    int N_a;
    int N;
    if (a != NULL) {
        FreeArray(a, N);
    }
    if (b != NULL) {
        FreeArray(b, N);
    }
    do {
        printf("Enter the size of the NxN array:\n");
        scanf("%d", &N);
        if (N < 2) {
            printf("Error, enter correct size.\n");
        }
    } while (N < 2);
    N_a = N;
    int errorInAlloc = 0;
    a = (int**)malloc(N_a * sizeof(int*));
    if (a != NULL) {
        errorInAlloc = 1;
    }
    return 0;
}

void FreeArray(int **arr, int N) {
    int i;
    if (arr == NULL)
        return;
    if (arr != NULL) {
        for (i = 0; i < N; i++) {
            if (arr[i] != NULL)
                free(arr[i]);
        }
        free(arr);
    }
}

The FreeArray function was provided to us, so I know it is correct. There is other code included in this, but I omitted is since it is just a menu. When I comment out a = (int**) malloc(N_a * sizeof(int*)); the program compiles and runs without crashing or problems. When I include this line, the program compiles, but when I put a number greater than or equal to 2, it crashes. I have also tried a = malloc(N_a * sizeof(int*)); and a = (int**)malloc(N * sizeof(int*)); AND a = malloc(N * sizeof(int*)); but all do the same thing.

I have been coding and running the program in Code::Blocks, but even when I compile it through the command prompt, it still crashes.

I have a hard time with arrays on a good day, so dynamically allocating arrays is so confusing to me, any help is greatly appreciated!

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Reily R
  • 11
  • 5
    In `if(a != NULL)` and `if(b != NULL)` you are testing *uninitialised variables*. Please turn on compiler warnings. If those variables happened to be not `NULL` then the `free`ing can crash the system, since the pointers are not to memory that was allocated by `malloc` or its cousins. – Weather Vane Aug 01 '17 at 19:54
  • Note: Consider allocation simplification: `a = (int**)malloc(N_a * sizeof(int*));` --> `a = malloc(sizeof *a * N_a);`. Easier to code, review and maintain. – chux - Reinstate Monica Aug 01 '17 at 19:57
  • @chux My personal preference is `a = malloc(N_a * sizeof *a)` because it mimics the order of the arguments in `calloc`. – melpomene Aug 01 '17 at 19:58
  • 1
    @melpomene [That](https://stackoverflow.com/questions/45446573/c-code-involving-malloc-for-dynamic-allocation-of-2d-array-crashes-after-compili?noredirect=1#comment77854406_45446573) does have that advantage. I prefer starting with the `size_t` type as that insures the following product is calculated using `size_t` math: `sizeof *a * int_width * int_row)` that might overflow in another order. Of course, make no product difference with only 2. – chux - Reinstate Monica Aug 01 '17 at 20:01
  • Why are you freeing the array before starting the program / allocating the array? – Felix Guo Aug 01 '17 at 20:02
  • 2
    This is the obligatory, "Don't cast the return value of Malloc because it can conceal errors" comment. – David Hoelzer Aug 01 '17 at 20:12
  • `The FreeArray function was provided to us, so I know it is correct.` wrong assumption – 0___________ Aug 01 '17 at 22:46
  • Here is the obligatory link. See: [**Do I cast the result of malloc?**](http://stackoverflow.com/q/605845/995714) – David C. Rankin Aug 07 '17 at 00:44

3 Answers3

2

You have multiple errors in your program:

  • variables a and b are uninitialized, comparing them to NULL is meaningless and passing them to FreeArray has undefined behavior.

  • given how the FreeArray function is written, allocation must be performed in 2 steps:

    • allocate an array of of pointers, which you do.
    • initialize each element of the array with the address of an allocated array of int. which you don't.

Here is how a function AllocArray would be written:

int **AllocArray(int N) {
    int i;
    int **arr = calloc(sizeof(*arr), N);
    if (arr != NULL) {
        for (i = 0; i < N; i++) {
            arr[i] = calloc(sizeof(*arr[i]), N);
            if (arr[i] == NULL) {
                /* allocation failed: free allocated portion and return NULL */
                while (i-- > 0) {
                    free(arr[i]);
                }
                free(arr);
                return NULL;
            }
        }
    }
    return arr;
}

Note that the FreeArray function can be simplified this way:

void FreeArray(int **arr, int N) {
    int i;
    if (arr != NULL) {
        for (i = 0; i < N; i++) {
            free(arr[i]);
        }
        free(arr);
    }
}

It is OK to pass a null pointer to free(), and a single test on arr is sufficient.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • @WeatherVane: good catch. I used an explicit `return NULL;` that is provably less error prone ;-) – chqrlie Aug 01 '17 at 20:19
  • I notice, you usually go one better than recommendations! (mine was `arr = NULL;` – Weather Vane Aug 01 '17 at 20:19
  • @WeatherVane: Your recommendation was good, and might help produce less code and comply with local coding rules (single exit point), and it was my intent to use this approach, but I forgot the necessary `arr = NULL;` statement and ruined the error detection. An explicit `return NULL;` is less error prone and more readable for beginners... – chqrlie Aug 01 '17 at 20:24
  • I prefer the early exit, it simplifies the code structure and the need for unnecessary `else` code blocks. Might depend on any clean-up required when the proper one-exit solution will be needed. – Weather Vane Aug 01 '17 at 20:28
  • 1
    One calloc for the whole array. Easier (quicker) dereferencing, smaller overhead. In the complex matrix calculations every instruction counts – 0___________ Aug 01 '17 at 22:51
  • @PeterJ: I agree, but this simplification would require a change in the `FreeArray` function. One might as well use a C99 2D array declared and allocated in one step with `int (*arr)[N] = calloc(sizeof(*arr), N);` – chqrlie Aug 02 '17 at 18:32
  • @chqrlie double dereferences terribly lower the quality of the optimisation. Even with single malloc and values of number of the rows and collums stored in the table - compilers do now absolutely amazing job. Just to see how it works (plain curiosity) : https://godbolt.org/g/V9ZjUL - and I am amazed. Another thing which amazes me is the ARM instruction set. – 0___________ Aug 02 '17 at 18:46
  • @PeterJ: amazing indeed. For consistency with `CreateArray()`, it seems the code should be: `inline int ArrayGet(int *p, int row, int column) { return *(p + 2 + row * p[1] + column); }` and the same difference in `ArrayPut()`. – chqrlie Aug 02 '17 at 18:55
  • You right - but it is just the 5 min "sketch" - did not care about the consistency :) – 0___________ Aug 02 '17 at 19:00
1

First problem is that you free an unallocated pointer:

 if(a != NULL)
 {
     FreeArray(a, N);
 }

You've never allocated memory for a, so when you try to access to a position of the vector, it generates a segmetation fault (same occurs with b). If you want to make sure that your pointer is NULL, just use a = NULL;. This will fix your problem.

There is another logical problem:

if(a != NULL)
{
    errorInAlloc = 1;
}

You return an error in allocation if a differs from NULL. You should return an error if a is NULL wich means that an error occurs in the memory allocation.

0

Here's what gcc reported when your code was compiled with more warnings.

gcc -o xx xx.c -Wall -Wextra -pedantic -std=c99 -O2 -Wmissing-declarations -Wmissing-prototypes -Wconversion
xx.c: In function ‘main’:
xx.c:26:26: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
a = (int **) malloc(N_a * sizeof(int *));
                      ^
xx.c:25:6: warning: variable ‘errorInAlloc’ set but not used [-Wunused-but-set-variable]
int errorInAlloc = 0;
  ^
xx.c:10:5: warning: ‘a’ is used uninitialized in this function [-Wuninitialized]
if (a != NULL) {
 ^
xx.c:13:5: warning: ‘b’ is used uninitialized in this function [-Wuninitialized]
if (b != NULL) {
 ^
xx.c:11:3: warning: ‘N’ may be used uninitialized in this function [-Wmaybe-uninitialized]
FreeArray(a, N);
^

The formatting is a little bit off. The point is crystal clear: Let your compiler help you locate errors.

Bjorn A.
  • 1,148
  • 9
  • 12
  • 1
    The reason for "warning: `N` may be used uninitialized" is that the return value from `scanf` is not checked, to make sure that an input was properly scanned. *Always* check the result of user input functions, and if necessary, their value is in the expected valid range. – Weather Vane Aug 01 '17 at 20:08