Почему memset работает по-разному в функциях? - PullRequest
2 голосов
/ 22 октября 2019

Я пытаюсь выяснить, почему memset не работает, когда я вызываю его изнутри функции. Конечно, я мог бы использовать цикл for для сброса всех значений, но я здесь, чтобы узнать, и это не дает мне объяснений.

/* 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");
    }
}

Во втором коде (с функциями), я должен поставить строку: memset (magic_square, 0, n * sizeof (magic_square [0]));для очистки двумерного массива с помощью: memset (magic_square, 0, sizeof (magic_square));не работает. Почему это происходит?

1 Ответ

5 голосов
/ 22 октября 2019

Когда вы передаете массивы функциям в C, они распадаются на указатель на первый элемент в массиве, , даже если вы указываете тип массива в прототипе функции (а) . Следовательно, sizeof даст вам размер указателя, , а не размера массива.

Обычная практика - передавать размер массива вместе с массивом (вероятно, в видеsize_t) и используйте , что , чтобы сплести свою магию, что-то вроде:

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

Однако, вы уже передаете достаточно информации вашей текущей функции, чтобы сделатьэто, в частности, n. Вы должны просто использовать это для формирования типа для оператора sizeof (который может принимать переменную или тип), и он будет делать то, что вам нужно:

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

(a) Покрыт по последнему стандарту C11 в разделе 6.3.2.1 Lvalues, arrays, and function designators /3:

За исключением случаев, когда он является операндом оператора sizeof, оператор _Alignof, или унарный оператор &, или строковый литерал, используемый для инициализации массива, выражение с типом «массив типа» преобразуется в выражение с типом «указатель на тип», которое указываетк начальному элементу объекта массива и не является lvalue.

Я ссылался на последний итерацию стандарта, несмотря на ваш тег c99, единственное отличие состоит в том, чточто стандарт C99 не упоминает Alignof, так как он не существовал в этой итерации. Кроме этого, цитата идентична.

Это идет путь назад, даже в первое издание K & R, 5.3 Pointers and arrays:

На самом деле, ссылкав массив преобразуется компилятором в указатель на начало массива.

...