Действительно ли следующее преобразование безопасно?
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.
Все это имеет шанс быть законным по одной и только одной причине: объект массива 2D int
интерпретируется как массив из char
объектов. В то время как в общем случае переинтерпретация памяти приводит к неопределенному поведению, спецификация языка явно разрешает переинтерпретации "массив [подписанных / беззнаковых] символов" для объектов любого типа.
Однако одна формальная проблема безопасности все еще существует. Язык не гарантирует, что какой-либо битовый шаблон является допустимым значением типа char
. Ваша попытка прочитать переинтерпретируемую память с помощью типа char
теоретически может вызвать неопределенное поведение, если она встречает представление прерывания для char
. В целях безопасности вы должны использовать тип unsigned char
, который является единственным типом, не имеющим представления прерывания. Конечно, платформу с треппингом char
смело можно назвать «экзотической».
Между тем, ваш sizeof (a + 1)
, похоже, не имеет никакого смысла. a + 1
является выражением типа int *
. Почему вы хотите использовать размер указателя для увеличения значения x
в этом случае, мне не ясно. Чего вы пытались достичь?
Что касается отсутствия предупреждений ... Я не ожидал, что компилятор выдаст здесь какие-либо предупреждения. GCC часто предупреждает о каламбурах типов (также известных как переинтерпретация памяти), но поскольку переинтерпретации char
явно разрешены (как я сказал выше), здесь нет предупреждения. Более того, явное приведение типов обычно подавляет любые предупреждения, поскольку они являются способом сообщить компилятору, что вы действительно хотите что-то сделать, независимо от того, насколько это неправильно и / или опасно.
приведение любого типа указателя к char * явно разрешено языком C. так что в большинстве случаев это нормально.
for(x=(char *)&b[0]; x <= (char *)&b[9][9]; x += sizeof(a+1))
Первая часть в порядке x = (char *) & b [0];
устанавливает указатель char на начало массива. Тест также хорош x <= (char *) & b [9] [9]
будет истинным, пока x точек внутри массива.
x + = sizeof (a + 1)
- сомнительная часть. На большинстве 32-битных архитектур ЦП sizeof (int *) просто бывает таким же, как sizeof (int), поэтому этот код, вероятно, будет работать , но только случайно.
Я уверен, что предполагалось x + = sizeof (a [0])
или x + = sizeof (b [0])
, но поскольку код на самом деле сделал то, что задумал, бага никто не заметил.