Срок действия кода - PullRequest
       5

Срок действия кода

14 голосов
/ 10 января 2010

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

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, потому что он нарушает

C ++ Standard ($ 5,7 / 5 [expr.add])

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

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

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

Ответы [ 4 ]

15 голосов
/ 10 января 2010

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

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

9 голосов
/ 10 января 2010

У вас есть массив массивов. 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 не определено, тогда как &a[1][0] + 1 допустимо. Если это кажется странным, это так, но в соответствии с цитатой, которую вы опубликовали в стандарте, вы можете рассчитывать только указатель, который находится либо внутри массива, либо не более одного после массива (без разыменования этого указателя «один прошлый») .

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

См. этот пост также comp.lang.c.

3 голосов
/ 10 января 2010

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

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

0 голосов
/ 10 января 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...