неожиданный порядок байтов после приведения указателя к символу в указатель на int - PullRequest
0 голосов
/ 19 сентября 2018
unsigned char tab[4] = 14;

Если я печатаю как отдельные байты ...

printf("tab[1] : %u\n", tab[0]); // output: 0
printf("tab[2] : %u\n", tab[1]); // output: 0
printf("tab[3] : %u\n", tab[2]); // output: 0
printf("tab[4] : %u\n", tab[3]); // output: 14

Если я печатаю как целое число ...

unsigned int *fourbyte;
fourbyte = *((unsigned int *)tab);
printf("fourbyte : %u\n", fourbyte); // output: 234881024

Мой вывод в двоичном виде: 00001110 0000000000000000 00000000, то есть данные, которые я хотел, но в этом порядке вкладка [3] вкладка [2] вкладка [1] вкладка [0].Любое объяснение того, почему указатель типа unsigned int указывает на последний байт вместо первого?

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Правильный ответ здесь заключается в том, что вы не должны были ожидать каких-либо отношений, заказов или иным образом.За исключением объединений, стандарт C не определяет линейное адресное пространство, в котором могут перекрываться объекты разных типов.Во многих комбинациях архитектура / компилятор-инструмент-цепочка имеет место случайное совпадение, но вы никогда не должны полагаться на них.Тот факт, что приведение указателя к подходящему скалярному типу дает число, сравнимое с другими значениями того же типа, никоим образом не подразумевает, что число является каким-либо конкретным адресом памяти.

Итак:

int* p;
int z = 3;
int* pz = &z;
size_t cookie = (size_t)pz;
p = (int*)cookie;
printf("%d", *p); // Prints 3.

Работает, потому что стандарт говорит, что он должен работать, когда cookie получен из того же типа указателя, в который он конвертируется.Преобразование в любой другой тип является неопределенным поведением.Указатели не представляют память, они ссылаются на «хранилище» в аннотации.Они являются просто ссылками на объекты или NULL, и стандарт определяет, как должны вести себя указатели на один и тот же объект и как их можно преобразовать в скалярные значения и обратно.

Дано:

char array[5] = "five";

Стандарт гласит, что &(array[0]) < &(array[1]) и (&(array[0])) + 1) == &(array[1]), но при этом не учитывается порядок элементов в array в памяти.Авторы компилятора могут свободно использовать любые машинные коды и схемы памяти, которые они считают подходящими для целевой архитектуры.

В случае объединения, которое предусматривает некоторое перекрытие объектов в хранилище, стандарт только говорит, чтокаждое из его полей должно быть соответствующим образом выровнено для их типов, но почти все остальное в них определяется реализацией.Ключевое предложение: 6.2.6.1 p7 :

Когда значение хранится в элементе объекта типа объединения, байты представления объекта, которые не соответствуютэтот член, но соответствует другим членам, принимает неопределенные значения.

Суть всего этого в том, что стандарт C определяет абстрактную машину.Компилятор генерирует архитектуру этой машины на основе вашего кода.Вы не можете понять абстрактную машину C с помощью простых эмпирических средств, потому что детали реализации стекаются в ваш набор данных.Вы должны ограничить свои наблюдения теми, которые имеют отношение к абстракции.Следовательно, избегайте неопределенного поведения и очень внимательно относитесь к поведению, определяемому реализацией.

0 голосов
/ 19 сентября 2018

Ваш пример кода работает на компьютере с прямым порядком байтов.Этот термин означает, что «первый байт» целого числа содержит младшие значащие биты.В противоположность этому компьютер с Big-Endian хранит старшие значащие биты в первом байте.

Отредактировано, чтобы добавить: способ, которым вы продемонстрировали это, явно небезопасен, поскольку он опирается на неопределенное поведение чтобы получить «прямой доступ» к памяти.Более безопасная демонстрация здесь

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