Небезопасное преобразование - PullRequest
5 голосов
/ 06 февраля 2010

Безопасно ли следующее преобразование?

int b[10][10];
char *x;
int a[]={0,1,2,3,4,5,6,7,8,9};

for(int i=0;i<10;i++)
  for(int j=0;j<10;j++)
    b[i][j]=a[i];

for(x=(char *)&b[0];x<=(char *)&b[9][9];x+=sizeof(a+1)) // Problem lies here!
    printf("%d\n",*x);

Я не думаю, что приведенное выше преобразование в цикле for является безопасным (я думаю, что оно зависит от платформы). Пожалуйста, поправьте меня, если я ошибаюсь. Я удивлен, потому что код компилируется без каких-либо предупреждений, даже когда компилируется с использованием параметров -Wall -pedantic в gcc.

Ответы [ 2 ]

7 голосов
/ 06 февраля 2010

У всего этого есть шанс быть законным по одной-единственной причине: объект массива 2D int переосмысливается как массив объектов char. В то время как в общем случае реинтерпретация памяти приводит к неопределенному поведению, спецификация языка явно допускает реинтерпретации «массива [подписанного / без знака] символа» для объектов любого типа.

Однако, одна формальная проблема безопасности все еще существует. Язык не гарантирует, что любая битовая комбинация является допустимым значением типа char. Ваша попытка прочитать переинтерпретированную память через тип char теоретически может вызвать неопределенное поведение, если встретится представление прерывания для char. Чтобы быть в безопасности здесь, вы должны использовать тип unsigned char, который является единственным типом, который не имеет представлений ловушек. Конечно, платформу с ловушкой char можно смело назвать «экзотической».

Между тем, ваш sizeof(a + 1), похоже, не имеет никакого смысла. a + 1 является выражением типа int *. Почему вы хотите использовать размер указателя для увеличения значения x в этом случае мне неясно. Чего вы пытались достичь?

Что касается отсутствия предупреждений ... Я бы не ожидал, что компилятор выдаст здесь какие-либо предупреждения. GCC часто предупреждает о пробуксовке типов (реинтерпретация памяти), но, поскольку char реинтерпретации явно разрешены (как я уже говорил выше), здесь нет никаких предупреждений. Более того, явное приведение обычно имеет тенденцию подавлять любые предупреждения, так как они являются способом сообщить компилятору, что вы действительно хотите что-то сделать независимо от того, насколько это неправильно и / или опасно.

1 голос
/ 06 февраля 2010

приведение любого типа указателя на char * явно разрешено языком Си. так что большая часть этого в порядке.

for(x=(char *)&b[0]; x <= (char *)&b[9][9]; x += sizeof(a+1))  

Первая часть в порядке. x = (char*)&b[0]; устанавливает символьный указатель на начало массива. С тестом тоже все в порядке x <= (char *)&b[9][9] будет верным, если х указывает внутри массива.

x += sizeof(a+1) - сомнительная часть. На большинстве 32-битных архитектур ЦП sizeof (int *) просто бывает таким же, как sizeof (int), поэтому этот код, вероятно, будет работать , но только случайно.

Я уверен, что предполагалось, было x += sizeof(a[0]) или x += sizeof(b[0]), но так как код фактически делал то, что предполагалось, никто не заметил ошибку.

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