Безопасная инициализация массивов в C общим способом - PullRequest
1 голос
/ 06 августа 2011

Я написал некоторый код, который использует memset для инициализации массивов встроенных типов, таких как целые, шорты, числа с плавающей запятой и, что более важно, указатели , например

typedef void* slot_t;
#define EMPTY_SLOT (slot_t)NULL
...
int n = 10;
slot_t slots[] = (slot_t[])malloc(sizeof(slot_t)*n)
memset(slots,(int)EMPTY_SLOT,n*sizeof(slot_t));

этот код прекрасно работает в Linux32, где memset принимает 32-битные int s в качестве второго аргумента (т. Е. Инициализирующий элемент), но это не так для Linux64, где sizeof(slot_t)>sizeof(int), и IIRC, на других платформах, гдеmemset принимает char в качестве второго аргумента.Мне еще предстоит проверить, что любая из ошибок, с которыми я сталкиваюсь в моем проекте, связана с этим, но, во всяком случае, было бы лучше выбрать более безопасный, но все же «универсальный» метод, если он существует.Вы знаете кого-нибудь?

Ответы [ 5 ]

2 голосов
/ 06 августа 2011

Используйте

slot_t slots[] = (slot_t[])calloc(n,sizeof(slot_t))

это чистая память сама по себе

1 голос
/ 06 августа 2011

memset заполняет память байтами.См. здесь .

Если вы хотите универсальное решение - вы должны написать цикл, который будет повторяться и заполняться.Если вы заполняете 0, то не имеет значения, какой это тип данных и какого размера - просто заполните 0 siezof всего массива (sizeof(slot_t)*n).Так как вы используете NULL, который не должен быть 0 (хотя обычно это так), я предлагаю использовать более безопасный подход "loop".

1 голос
/ 06 августа 2011

memset действительно хочет символ в качестве значения для заполнения памяти - обратите внимание, что он заполняет в байтовом порядке . Так что просто скажи 0. Вы можете поместить это в свой макрос EMPTY_SLOT, если хотите. Или используйте calloc().

(Кроме того, тип возврата вашего malloc() вызова должен быть slot_t *.)

0 голосов
/ 07 августа 2011

Если вам нужна полностью универсальная функция, которая будет устанавливать для массива объектов значения, определенные неким «шаблоном» объекта, вы можете использовать функцию, подобную следующей:

void init_array( void* arr, size_t nmemb, size_t size, void const* initializer)
{
    size_t i = 0;

    char* p = (char *) arr;

    for (i = 0; i < nmemb; ++i) {
        memcpy( p, initializer, size);
        p += size;
    }
}

Тогда ваше распределение /Код инициализации может выглядеть следующим образом:

typedef void* slot_t;

static const slot_t empty_slot = NULL;    // or make this a global if that 
                                          //  works better for your scenario

int n = 10;

// note: your original `malloc()` line:
//
//      slot_t slots[] = (slot_t[])malloc(sizeof(slot_t)*n)
//
// wouldn't work, as you can't assign to an array as a whole.
// That line shouldn't even compile.

slot_t* slots = (slot_t*)malloc(sizeof(slot_t)*n);

// completely generic initialization
init_array( slots, n, sizeof(slot_t), &empty_slot);

Если вы хотите инициализировать массив указателей, у вас может быть другая функция, которая обрабатывает этот случай немного более непосредственно:

void init_ptr_array( void* arr, size_t nmemb, void* initializer)
{
    size_t i = 0;
    void* p;

    for (; p < arr + nmemb; ++p) {
        *p = initializer;
    }
}

// arrays of object pointers
init_ptr_array( slots, n, empty_slot);

I 'Я не уверен, что мне нравится, что две функции имеют слегка различное значение для последнего параметра.Если бы у меня была потребность в обоих видах инициализации в моей программе, я бы, вероятно, остановился на использовании общего для инициализации массивов указателей.Это может быть немного менее эффективно, но инициализация обычно не является узким местом.

0 голосов
/ 06 августа 2011

Второй аргумент memset() имеет тип int, но он указывает значение, которое будет храниться в каждом байте места назначения - это означает, что, если sizeof(int) == 4, вы обнуляете в четыре раза больше памяти, чем должно быть.

Путь к заполнению нулями массива slots будет

memset(slots, 0, n * sizeof *slots);

(при условии, что slots правильно объявлен как указатель, а не как массив), за исключением того, что представление нулевого указателя не обязательно должно быть все-бит-ноль (это, вероятно, так, но вы не должны зависеть от это).

Если вам нужна полная переносимость, вам нужно написать цикл, чтобы установить для каждого элемента значение NULL.

Если вы хотите предположить, что нулевые указатели все-ноль-бит, вы можете использовать memset, но обязательно вызовите его, как я указал.

...