Я хочу несколько раз обнулить большой 2-мерный массив в C. Вот что я делаю сейчас:
// Array of size n * m, where n may not equal m
for(j = 0; j < n; j++)
{
for(i = 0; i < m; i++)
{
array[i][j] = 0;
}
}
Я пробовал использовать memset:
memset(array, 0, sizeof(array))
Но это работает только для одномерных массивов. Когда я распечатываю содержимое 2D-массива, первая строка нули, но затем я получил загрузку случайных больших чисел, и она вылетела.
memset
, потому что вы упомянули сбой из-за обнуления только одной строки.int d0=10, d1=20; int arr[d0][d1]
, иmemset(arr, 0, sizeof arr);
работал, как ожидалось (gcc 3.4.6, скомпилирован с-std=c99 -Wall
флагами). Я понимаю, что «это работает на моей машине» означает неумелое приседание, но этоmemset(arr, 0, sizeof arr);
должно было сработать .sizeof arr
должен возвращать количество байтов, используемых всем массивом (d0 * d1 * sizeof (int)).sizeof array[0] * m * n
не даст вам правильный размер массива.int array[][10]
, тоsizeof(array) == sizeof(int*)
размер первого измерения неизвестен. OP не уточнил, как был получен массив.Если
array
это действительно массив, вы можете "обнулить его" с помощью:memset(array, 0, sizeof array);
Но вам следует знать два момента:
array
это действительно "двумерный массив", т.е. он был объявленT array[M][N];
для некоторого типаT
.array
был заявлен. Если вы передадите его функции, тогда имяarray
превратится в указатель иsizeof
не даст вам размер массива.Проведем эксперимент:
#include <stdio.h> void f(int (*arr)[5]) { printf("f: sizeof arr: %zu\n", sizeof arr); printf("f: sizeof arr[0]: %zu\n", sizeof arr[0]); printf("f: sizeof arr[0][0]: %zu\n", sizeof arr[0][0]); } int main(void) { int arr[10][5]; printf("main: sizeof arr: %zu\n", sizeof arr); printf("main: sizeof arr[0]: %zu\n", sizeof arr[0]); printf("main: sizeof arr[0][0]: %zu\n\n", sizeof arr[0][0]); f(arr); return 0; }
На моей машине это печатает:
main: sizeof arr: 200 main: sizeof arr[0]: 20 main: sizeof arr[0][0]: 4 f: sizeof arr: 8 f: sizeof arr[0]: 20 f: sizeof arr[0][0]: 4
Несмотря на то,
arr
что это массив, он распадается на указатель на свой первый элемент при передачеf()
, и поэтому размеры, напечатанные в нем,f()
являются «неправильными». Кроме того,f()
размерarr[0]
- это размер массиваarr[0]
, который является «массивом [5] изint
». Это не размерint *
, потому что «распад» происходит только на первом уровне, и поэтому нам нужно объявитьf()
как принимающий указатель на массив правильного размера.Итак, как я уже сказал, то, что вы делали изначально, будет работать только в том случае, если соблюдены два вышеуказанных условия. Если нет, вам нужно будет сделать то, что сказали другие:
memset(array, 0, m*n*sizeof array[0][0]);
Наконец,
memset()
иfor
цикл вы вывесили не эквивалентны в строгом смысле этого слова. Могут быть (и были) компиляторы, в которых «все нулевые биты» не равны нулю для определенных типов, таких как указатели и значения с плавающей запятой. Я сомневаюсь, что вам стоит об этом беспокоиться.источник
memset(array, 0, n*n*sizeof array[0][0]);
Я думаю, вы имеете в видуm*n
не такn*n
?memset
работает на уровне байтов (символов). Поскольку в базовом представлении есть1
или2
нет одинаковых байтов, вы не можете этого сделать с помощьюmemset
.int
в вашей системе 4 байта» где-то перед минимальным рабочим примером, чтобы читатель мог легко вычислить суммы.Что ж, самый быстрый способ сделать это - вообще этого не делать.
Звучит странно, я знаю, вот какой-то псевдокод:
int array [][]; bool array_is_empty; void ClearArray () { array_is_empty = true; } int ReadValue (int x, int y) { return array_is_empty ? 0 : array [x][y]; } void SetValue (int x, int y, int value) { if (array_is_empty) { memset (array, 0, number of byte the array uses); array_is_empty = false; } array [x][y] = value; }
Фактически, он все еще очищает массив, но только тогда, когда что-то записывается в массив. В этом нет большого преимущества. Однако, если 2D-массив был реализован с использованием, скажем, дерева квадратов (а не динамического разума) или набора строк данных, то вы можете локализовать эффект логического флага, но вам понадобится больше флагов. В дереве квадратов просто установите пустой флаг для корневого узла, в массиве строк просто установите флаг для каждой строки.
Это приводит к вопросу «почему вы хотите многократно обнулять большой двумерный массив»? Для чего используется массив? Есть ли способ изменить код, чтобы массив не нуждался в обнулении?
Например, если у вас были:
clear array for each set of data for each element in data set array += element
то есть использовать его в качестве буфера накопления, а затем изменить его, как это, без конца повысит производительность:
for set 0 and set 1 for each element in each set array = element1 + element2 for remaining data sets for each element in data set array += element
Это не требует очистки массива, но все равно работает. И это будет намного быстрее, чем очистка массива. Как я уже сказал, самый быстрый способ - это вообще не делать этого.
источник
Если вы действительно очень одержимы скоростью (а не столько переносимостью), я думаю, что самый быстрый способ сделать это - использовать встроенные функции вектора SIMD. например, на процессорах Intel вы можете использовать эти инструкции SSE2:
__m128i _mm_setzero_si128 (); // Create a quadword with a value of 0. void _mm_storeu_si128 (__m128i *p, __m128i a); // Write a quadword to the specified address.
Каждая инструкция сохранения устанавливает четыре 32-битных целых числа в ноль за одно обращение.
p должен быть выровнен по 16 байт, но это ограничение также полезно для скорости, потому что оно помогает кешу. Другое ограничение состоит в том, что p должен указывать на размер выделения, кратный 16 байтам, но это тоже круто, потому что позволяет легко развернуть цикл.
Сделайте это в цикле и несколько раз разверните цикл, и у вас будет безумно быстрый инициализатор:
// Assumes int is 32-bits. const int mr = roundUpToNearestMultiple(m, 4); // This isn't the optimal modification of m and n, but done this way here for clarity. const int nr = roundUpToNearestMultiple(n, 4); int i = 0; int array[mr][nr] __attribute__ ((aligned (16))); // GCC directive. __m128i* px = (__m128i*)array; const int incr = s >> 2; // Unroll it 4 times. const __m128i zero128 = _mm_setzero_si128(); for(i = 0; i < s; i += incr) { _mm_storeu_si128(px++, zero128); _mm_storeu_si128(px++, zero128); _mm_storeu_si128(px++, zero128); _mm_storeu_si128(px++, zero128); }
Существует также вариант
_mm_storeu
обхода кеша (т. Е. Обнуление массива не приведет к загрязнению кеша), что может дать вам некоторые дополнительные преимущества в производительности в некоторых случаях.См. Здесь ссылку на SSE2: http://msdn.microsoft.com/en-us/library/kcwz153a(v=vs.80).aspx
источник
Если вы инициализируете массив с помощью
malloc
, используйтеcalloc
вместо этого; он обнулит ваш массив бесплатно. (Очевидно, что та же производительность, что и у memset, только меньше кода для вас.)источник
int array[N][M] = {0};
... по крайней мере, в GCC 4.8.
источник
Как был объявлен ваш 2D-массив?
Если это что-то вроде:
int arr[20][30];
Вы можете обнулить его, выполнив:
memset(arr, sizeof(int)*20*30);
источник
memset(a, 0, sizeof(char)*10*10);
у меня все работает. , как это происходит?Используйте calloc вместо malloc. calloc установит все поля в 0.
int * a = (int *) calloc (n, размер (int));
// все ячейки в a были инициализированы 0
источник
Я думаю, что самый быстрый способ сделать это вручную - это выполнить код. Вы можете сравнить его скорость с функцией memset, но она не должна быть медленнее.
(измените тип указателей ptr и ptr1, если тип вашего массива отличается от int)
#define SIZE_X 100 #define SIZE_Y 100 int *ptr, *ptr1; ptr = &array[0][0]; ptr1 = ptr + SIZE_X*SIZE_Y*sizeof(array[0][0]);
while(ptr < ptr1) { *ptr++ = 0; }
источник
memset
для типов char.memset(array, 0, sizeof(int [n][n]));
источник
Вы можете попробовать это
int array[20,30] = {{0}};
источник
Это происходит потому, что sizeof (array) дает вам размер выделения объекта, на который указывает array . ( массив - это просто указатель на первую строку вашего многомерного массива). Однако вы выделили j массивов размера i . Следовательно, вам нужно умножить размер одной строки, которую возвращает sizeof (array), на количество выделенных вами строк, например:
bzero(array, sizeof(array) * j);
Также обратите внимание, что sizeof (array) будет работать только для статически распределенных массивов. Для динамически распределенного массива вы должны написать
size_t arrayByteSize = sizeof(int) * i * j; int *array = malloc(array2dByteSite); bzero(array, arrayByteSize);
источник
sizeof
Оператор Forarray
не является указателем (если он был объявлен как массив). См. Мой ответ для примера.