2010-01-10 1 views
14

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

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])

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

Но я не вижу ничего плохого в коде, код для меня совершенно нормально.

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

+0

+1 хороший вопрос –

ответ

15

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

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

+0

Но когда вы обращаетесь к 'arr [2]', 'arr' не является« массивом [2] всего », это указатель. Я не уверен, применим ли этот аргумент. –

+3

@Alok: В C++ многомерные массивы - это нечто иное, чем массивы указателей. Java имеет только последний, но C и C++ имеют обе формы. –

+0

@Greg, ОК. В C нет ничего подобного «многомерным массивам», есть только массивы (которые, в свою очередь, могут состоять из массивов и т. Д.). Не уверен, что вы подразумеваете, что C++ имеет истинные многомерные массивы. –

3

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

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

+1

Массивы должны иметь непрерывную память (раздел 6.2.5, марка 10 в стандарте ISO C99). И если многомерные массивы определены как массивы массивов, то разве это не означает, что они тоже смежны (n + 1-й массив должен немедленно следовать за n-м массивом в памяти)? – jamesdlin

+0

К сожалению, я имел в виду пулю 20. – jamesdlin

+0

Они должны быть смежными, но между ними может быть определенная реализация. Я не знаю, что это проблема для 'int' в любом компиляторе, который я использовал. –

9

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

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

См. Также this post on comp.lang.c.

+0

Не должен ли последний элемент третьего изображения быть «[1] [1]»? – vobject

+0

Вопрос заключается не в том, является ли память смежной (это ясно), но является ли поведение корректным. Я не вижу, как это может быть «полуправым». Он спрашивает, является ли это UB, ответ «да». Тот факт, что он обычно работает, не делает его менее неопределенным. – jalf

+1

jalf, вы правы. Когда я сказал «полуправой», я имел в виду, что некоторые люди думали, что память не должна быть смежной, поэтому поведение не определено. С другой стороны, многие другие люди считали, что память должна быть непрерывной, поэтому поведение четко определено. –

0

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

+0

Нет - массивы НЕ должны храниться линейно по стандарту. Если он объявлен как двумерный массив, это двухмерный массив - не один массив достаточно большой, чтобы d все суб-массивы. –

+1

BillyONeal, массив массивов по-прежнему представляет собой массив, и его элементы хранятся смежно. – avakar

 Смежные вопросы

  • Нет связанных вопросов^_^