Действительность кода

Рассмотрим следующий код:

void populate(int *arr)
{
   for(int j=0;j<4;++j)
       arr[j]=0;
}

int main()
{
   int array[2][2];
   populate(&array[0][0]);
}

Было обсуждено это в местном сообществе, действительно ли код действителен или нет (я должен упомянуть его имя?). Один парень говорил, что он вызывает UB, потому что он нарушает

Стандарт С++ ($ 5.7/5 [expr.add])

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

Но я не вижу ничего плохого в коде, код для меня отлично подходит.

Итак, я просто хочу знать, действительно ли этот код действителен или нет? Я что-то пропустил?

Ответы

Ответ 1

Ваш array - это два массива int[2], а ваша функция populate() рассматривает его как один массив из int[4]. В зависимости от того, как компилятор решает выравнивать элементы array, это может быть недействительным предположением.

В частности, когда j равно 2 и вы пытаетесь получить доступ к arr[2], это выходит за пределы main array[0] и поэтому недействительно.

Ответ 2

У вас есть массив массивов. array[1] следует за array[0] в памяти, потому что массивы смежны. Если p == array[0], то p[1] следует p[0], потому что массивы смежны. Итак, вы правы: вся память для array смежна.

В картинах array выглядит следующим образом.

+-----------------+-----------------+
|      [0]        |      [1]        |
+-----------------+-----------------+

Теперь, сломайте array[0] и array[1], они индивидуально выглядят следующим образом:

+--------+--------+
|  [0]   |  [1]   |        
+--------+--------+

Итак, последнее изображение:

+--------+--------+--------+--------+
| [0][0] | [0][1] | [1][0] | [1][1] |
+--------+--------+--------+--------+

Теперь, вопрос в том, можете ли вы получить доступ к этой непрерывной памяти так, как вы. Ответ заключается в том, что стандарт не гарантируется. Массивы смежны, но стандарт не позволяет индексировать то, как вы это делали. Другими словами:

&array[0][0]+2 == &array[1][0], но (&a[0][0] + 2) + 1 - undefined, тогда как &a[1][0] + 1. Если это кажется странным, это так, но в соответствии с цитированием, которое вы опубликовали на стандарте, вам разрешено вычислять указатель, который находится либо внутри массива, либо не более одного за ним (без разыменования этого указателя "один минус" ).

На практике я сомневаюсь, что это сработает где угодно, но по стандарту, по крайней мере, ваш код недействителен из-за поведения undefined.

Смотрите этот пост в comp.lang.c.

Ответ 3

Это не всегда будет работать. C имеет массивы массивов, а не двумерные массивы. Субмассивы не всегда указываются как смежные в памяти (могут быть статические массивы, проверьте стандарт C/С++). В этом конкретном примере я подозреваю, что он работает правильно. Однако, если вы динамически выделили передаваемую память, вы, возможно, потерпите неудачу, потому что malloc (или новый) мог бы поставить подмассивы довольно далеко друг от друга.

Если вы хотите линейно спуститься по памяти "2d", вы можете построить 2D-аксессор против массива 1D, и он будет работать нормально, и такие вещи, как memset, будут работать против массива 1D.

Ответ 4

В C все хранится в сегментах линейной памяти. Вы отправляете адрес a[0][0], который будет таким же, как адрес a[0], поэтому a[i][j] совпадает с a[i*ColSize+j], потому что все хранится линейно. Но если вы динамически распределяете память, это не сработает, потому что в это время все строки могут не храниться в смежном местоположении. то a[i][j] будет *(&a[i]+j).