Search code examples
carraysfunctionc99memset

Why does memset work differently in functions?


I'm trying to figure out why memset doesn't work when I call it from inside a function. Of course I could use a for loop to reset all the values, but I'm here to learn and this thing leaves me with no explanations.

/* First source code */
#include <stdio.h>
#include <string.h>

int main(void) {
    int i, j, num;

    printf("Digit a number :");
    scanf("%d", &num);

    int magic[num][num];

    printf("First\n");
    for(i = 0; i < num; ++i) {
        for(j = 0; j < num; ++j) {
            printf("%4d", magic[i][j]);
        }
        printf("\n\n");
    }

    memset(magic, 0, sizeof(magic));

    printf("Before\n");
    for(i = 0; i < num; ++i) {
        for(j = 0; j < num; ++j) {
            printf("%4d", magic[i][j]);
        }
        printf("\n\n");
    }

    return 0;
}

/* Second source code */
#include <stdio.h>
#include <string.h>

void create_magic_square(int n, int magic_square[][n]);
void print_magic_square(int n, int magic_square[][n]);

int main(void) {
    int num;
    printf("Digit a number :");
    scanf("%d", &num);
    int magic[num][num];
    create_magic_square(num, magic);
    return 0;
}

void create_magic_square(int n, int magic_square[][n]) {
    int i, j;

    printf("First\n");
    for(i = 0; i < n; ++i) {
        for(j = 0; j < n; ++j) {
            printf("%4d", magic_square[i][j]);
        }
        printf("\n\n");
    }

    //memset(magic_square, 0, n * sizeof(magic_square[0]));
    memset(magic_square, 0, sizeof(magic_square));

    printf("Before\n");
    for(i = 0; i < n; ++i) {
        for(j = 0; j < n; ++j) {
            printf("%4d", magic_square[i][j]);
        }
        printf("\n\n");
    }
}

In the second code (with the functions), I should put the line: memset (magic_square, 0, n * sizeof (magic_square [0])); for the two-dimensional array to be cleared, while with: memset (magic_square, 0, sizeof (magic_square)); does not work. Why is this happening?


Solution

  • When you pass arrays to functions in C, they decay to a pointer to the first element in the array, even if you specify an array type in the function prototype (a). Hence the sizeof will then give you the size of a pointer, not the size of the array.

    Common practice is to pass the array size along with the array (probably as a size_t) and use that to weave your magic, something like:

    int zeroArray(void *address, size_t sz) { // void* makes it clear
        memset(address, 0, sz);               // but int thing[][] would be same.
    }
    :
    int xyzzy[42][7];
    zeroArray(xyzzy, sizeof(xyzzy));
    

    However, you already pass in enough information to your current function to do this, specifically n. You should be able to just use that to form a type for the sizeof operator (which can take a variable or a type), and it will do what you need:

    memset(magic_square, 0, sizeof(int[n][n]));
    

    (a) Covered in the latest standard C11 under the section 6.3.2.1 Lvalues, arrays, and function designators /3:

    Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue.

    I've referenced the latest iteration of the standard despite your c99 tag, the only difference is that the C99 standard does not mention Alignof since it did not exist in that iteration. Other than that, the quote is identical.

    This goes way back, even to K&R first edition, 5.3 Pointers and arrays:

    In fact, a reference to an array is converted by the compiler to a pointer to the beginning of the array.