Без типа данные, на которые ссылается array
, не имеют размера элемента, поэтому арифметика указателя не определена. Чтобы выражение было допустимым, вы должны привести array
к соответствующему типу данных, например:
*((int*)array + i) = data;
Но это побеждает цель иметь неопределенный тип. Простое и наиболее эффективное решение - определить отдельные функции для массива каждого типа, который вы хотите заполнить. Можно определить функцию, которая будет обрабатывать несколько целых типов таким образом:
int array_fill(void* array, size_t array_length, long long data, size_t data_size )
{
if( data_size > sizeof(data) )
{
data_size = sizeof(data) ;
}
for( size_t i = 0; i < array_length; i++)
{
for( int b = 0; b < data_size; b++ )
{
((char*)array)[i * data_size + b] = (data >> (b * 8)) & 0xff ;
}
}
return 0;
}
Вышесказанное делает два предположения:
- цель использует порядок байтов в младшем порядке,
- цель имеет 8-битный
char
тип.
Модификации необходимы там, где эти предположения не соответствуют действительности. Обратите внимание, что я также использовал обозначение индекса массива, а не арифметику указателей - это приводит к меньшему количеству скобок, поэтому его легче читать.
Затем можно вызвать функцию, в вашем случае, например, так:
array_fill( array, array_length(array), 0, sizeof(*array) ) ;
и array
могут иметь любой тип.
Однако заполнение массива нулями - это особый случай, который не требует этой сложности (то есть для вашего примера использования он не имеет смысла). Следующее:
memset( array, sizeof(array), 0 ) ;
имеет тот же эффект, что все байты целого числа 0 в любом случае равны нулю. Функция более полезна для значений, где каждый байт отличается.
array_fill( array, array_length(array), 0x01234ABCD, sizeof(*array) ) ;
Теперь, если array
имеет тип uint8_t
, например, он будет заполнен 0xCD
, если это uint16_t
, тогда 0xABCD
. Если это было long long
и для цели, которая является 64-битным типом, она будет заполнена 0x0000000001234ABCD
.
Возможно, если несколько громоздко, использовать эту функцию для заполнения массива float
или double
, например:
double array[ARBITRARY_SIZE];
double x = 0.5 ;
array_fill(array, ARBITRARY_SIZE, *(long long*)(&x), sizeof(array) );
Другой подход, который позволяет использовать в качестве заливки также агрегированные типы или даже последовательности произвольной длины:
int array_fill( void* array, size_t array_length,
const void* fill_pattern, size_t fill_pattern_length )
{
for( size_t i = 0; i < array_length; i++)
{
for( int b = 0; b < fill_pattern_length; b++ )
{
((char*)array)[i * fill_pattern_length + b] = ((char*)fill_pattern)[b] ;
}
}
return 0;
}
Тогда его можно использовать действительно для любого типа. Примеры:
Double
double array[ARBITRARY_SIZE], x = 0.5 ;
array_fill( array, ARBITRARY_SIZE, &x, sizeof(x) );
INT
int array[ARBITRARY_SIZE], x = 123456 ;
array_fill( array, ARBITRARY_SIZE, &x, sizeof(x) );
структура
struct S{ int x; double f ; } array[ARBITRARY_SIZE], x = {1234, 0.5};
array_fill( array, ARBITRARY_SIZE, &x, sizeof(x) );
2D массив
int array[ARBITRARY_SIZE][2], x[2] = { 12, 98 } ;
array_fill( array, ARBITRARY_SIZE, &x, sizeof(x) );
Реализация избегает проблем с порядком байтов, но не может принимать инициализаторы с константами букв, потому что вы не можете взять адрес.
Эта последняя реализация может быть улучшена (упрощена и сделана более эффективной); например:
int array_fill( void* array, size_t array_length,
const void* fill_pattern, size_t fill_pattern_length )
{
for( size_t i = 0, byte_index = 0;
i < array_length;
i++, byte_index += fill_pattern_length )
{
memcpy( &((char*)array)[byte_index], fill_pattern, fill_pattern_length ) ;
}
return 0;
}
Это версия, с которой я бы пошел.