В C "все работает нормально" недостаточно.Поскольку вашему компилятору разрешено делать это:
struct newData
{
int x;
char padding1[523];
int y;
char padding2[364];
int z;
char padding3[251];
};
Конечно, это крайний пример.Но вы получите общую идею;не гарантируется, что ваш цикл будет работать, потому что не гарантируется, что struct newData
эквивалентно int[3]
.
Так что нет, это невозможно в общем случае, потому что это не всегда возможно в конкретном случае!
Теперь вы можете подумать: «Какие идиоты решили это ?!»Ну, я не могу вам этого сказать, но я могу сказать вам, почему.Компьютеры сильно отличаются друг от друга, и если вы хотите, чтобы код работал быстро, компилятор должен иметь возможность выбирать, как скомпилировать код.Вот пример:
Процессор 8 имеет инструкцию для получения отдельных байтов и помещения их в регистр:
GETBYTE addr, reg
Это хорошо работает с этой структурой:
struct some_bytes {
char age;
char data;
char stuff;
}
struct some_bytes
может успешно занять 3 байта, и код работает быстро.Но как насчет процессора 16?Он не имеет GETBYTE
, но имеет имеет GETWORD
:
GETWORD even_addr, reghl
Он принимает только четный адрес и читает два байта;один в «верхнюю» часть регистра и один в «низкую» часть регистра.Чтобы сделать код быстрым, компилятор должен сделать следующее:
struct some_bytes {
char age;
char pad1;
char data;
char pad2;
char stuff;
char pad3;
}
Это означает, что код может работать быстрее, но это также означает, что ваш цикл не будет работать.Это нормально, хотя, потому что это называется неопределенным поведением;компилятору разрешается предполагать, что этого никогда не произойдет, и если это произойдет, поведение не определено.
Фактически, вы уже сталкивались с этим поведением!Ваш конкретный компилятор делал это:
struct newData
{
int x;
int pad1;
int y;
int pad2;
int z;
int pad3;
};
Поскольку ваш конкретный компилятор определяет long int
как удвоенную длину int
, вы смогли сделать это:
| x | pad | y | pad | z | pad |
| long no.1 | long no.2 | long no.3 |
| int | | int | | int |
ЭтоКод, как вы можете судить по моей ненадежной схеме, ненадежен.Это вероятно не будет работать где-либо еще.Что еще хуже, ваш компилятор, если бы он был умным, мог бы сделать это:
for (int i=0; i<3; i++)
{
printf("%d \n", *(addr+i));
}
Хмм ... addr
от data2
от data1
, чтоэто указатель на struct newData
.Спецификация C говорит, что только указатель на начало структуры будет разыменован, поэтому я могу предположить, что i
всегда 0
в этом цикле!
for (int i=0; i<3 && i == 0; i++)
{
printf("%d \n", *(addr+i));
}
Это означает, что он работает толькоодин раз!Ура!
printf("%d \n", *(addr + 0));
И все, что мне нужно для компиляции, это:
int main()
{
printf("%d \n", 10);
}
Ух, программист будет так рад, что мне удалось так быстро ускорить этот код!
Вам не понравится.На самом деле, вы получите неожиданное поведение и не сможете понять, почему.Но вы были бы рады, если бы вы написали код, свободный от неопределенного поведения, и ваш компилятор сделал нечто подобное.Так и остается.