1023 (int) сохранить как 00 00 03 FF в памяти, когда приведено к char *, оно становится -1 3 0 0 - PullRequest
0 голосов
/ 30 апреля 2020

Я студент университета, наш учитель только что спросил нас, что является результатом этой программы и почему

#include <stdio.h>

int main(){
    int x = 1023;
    char *p = (char*)&x;

    printf("%d %d %d %d\n", p[0], p[1], p[2], p[3]);
}

- -1 3 0 0, но я не знаю почему. Я провел некоторое исследование и обнаружил, что целые числа в C / C ++ хранятся как HEX и помещаются в 4 байта памяти, например, 1023 хранится как 00 00 03 FF. Чего я не понимаю, так это почему FF становится -1, и почему это наоборот, я думаю, что это должно быть 0 0 3 -1. И еще, я не знаю, что происходит, когда вы приводите int-адрес к указателю на символ (или массив символов?)

char *p = (char*)&x;

Ответы [ 3 ]

4 голосов
/ 30 апреля 2020

В вашем примере многое зависит от плохо определенного поведения.

  • Выход зависит от CPU endianess , поэтому вы либо получите необработанные значения 00 00 03 FF на машине с прямым порядком байтов или FF 03 00 00 на машине с прямым порядком байтов.

  • Подпись char определяется реализацией, поэтому вы не можете определить, является ли необработанное значение переносимым. FF приведет к положительному или отрицательному числу при сохранении в char. Поэтому вы никогда не должны использовать char для отображения необработанных данных. Вместо этого используйте uint8_t.

  • И, наконец, если char действительно будет подписано, FF преобразуется в -1 в системе дополнения 2, но теоретически C допускает и другие формы подписи. (Формально программа может также отказаться от конвертации и подать сигнал, если сочтет подписанную стоимость из облигаций.)


В вашем случае происходит следующее: на небольшом порядке байтов 2-х дополняющая машина с подписанным компилятором char. Необработанные данные хранятся в формате FF 03 00 00 с прямым порядком байтов, а при интерпретации как знаковый символ FF на компьютерах, дополняющих 2, получается как -1.

Все параметры, передаваемые в printf, неявно преобразуются в (подпись) int, а %d указывает функции также обрабатывать их как int. Когда это происходит, отрицательное число -1 молча «расширяется от знака» из FF в FF FF FF FF для сохранения десятичного значения -1.

Таким образом, вы получаете -1 3 0 0 при печати данных в виде целых чисел.

3 голосов
/ 30 апреля 2020

Я провел некоторое исследование и обнаружил, что целые числа в C / C ++ хранятся как HEX

Неверно. Шестнадцатеричное является представлением значений.

и помещается в 4 байта памяти, например, 1023 сохраняется как 00 00 03 FF.

Это (частично ) правильно.

Предположим, у нас есть 32-битные целые числа. Тогда значение 1023, которое равно 512 + 256 + 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1, представляется как 0b0000001111111111 в двоичном формате и, следовательно, 0x000003FF в шестнадцатеричном виде.

Теперь мы имеем на выбор: если у нас есть прямой порядок байтов, он сохраняется как FF 03 00 00, в старшем порядке мы имеем 00 00 03 FF. (Обратите внимание, что существуют другие возможности для организации многобайтового значения, но это наиболее распространенные.)

Эти байты сейчас (представлены как char в самых обычных реализациях C ) может быть подписанным или неподписанным. Во многих реализациях char подписывается (если ему не предшествует unsigned, чтение unsigned char). В этих случаях установленный старший бит обозначает отрицательное число (если мы ограничиваем нас до двух дополнительных реализаций), а диапазон от 80 до FF отображается от -128 до -1. Таким образом, FF отображается как -1.

0 голосов
/ 30 апреля 2020

Значения хранятся в битах. Шестнадцатеричный формат - это один из способов отображения значения, хранимого в этих битах, как десятичного, восьмеричного и двоичного.

При условии 32-битного типа int, значение 1023<sub>10</sub> хранится в виде последовательности битов 00000000 00000000 00000011 11111111, шестнадцатеричное представление которой равно 0x000003FF.

Для такого значения требуется несколько байтов для хранения. Большинство систем, таких как x86, хранят многобайтовые значения, так что наименьший значащий байт идет первым, известный как порядок с прямым порядком байтов. Другие системы хранят многобайтовые значения, так что самый старший байт идет первым, известный как порядок с прямым порядком байтов. Предполагая, что наше целое число 1023 начинается с адреса p, его байты будут адресованы, как показано ниже в каждой системе:

   big-endian:  p[0] p[1] p[2] p[3]
               +----+----+----+----+
               | 00 | 00 | 03 | FF |
               +----+----+----+----+
little-endian:  p[3] p[2] p[1] p[0]

Именно поэтому в вашей системе дисплей отображается с -1 3 0 0 вместо 0 0 3 -1.

Что касается того, почему FF отображается как -1 ...

Существует несколько различных способов представления целочисленных значений со знаком, но их объединяет одно: самый левый бит используется для обозначения знака Если крайний левый бит равен 0, значение является положительным. Если крайний левый бит равен 1, значение является отрицательным. Предполагая 3-битный тип, они работают следующим образом:

Bits    Two's Complement    Ones' Complement    Sign-Magnitude    Unsigned
----    ----------------    ----------------    --------------    --------
 000                   0                   0                 0           0
 001                   1                   1                 1           1
 010                   2                   2                 2           2
 011                   3                   3                 3           3
 100                  -4                  -1                -0           4
 101                  -3                  -2                -1           5
 110                  -2                  -3                -2           6 
 111                  -1                  -0                -3           7

x86 (наряду с подавляющим большинством других систем) использует дополнение до двух для представления целочисленных значений со знаком, поэтому целое число со всеми установленными битами равно интерпретируется как -1.

Когда вы используете %d в вызове printf, вы говорите printf обрабатывать соответствующее значение как int со знаком и форматировать его как последовательность десятичных цифр. Следовательно, байт, содержащий FF, отформатирован как -1 в системе дополнения до двух. 1

Обратите внимание, что значение , сохраненное в x, равно 1023<sub>10</sub> (3ff<sub>16</sub>) независимо от того, как упорядочены байты или как представлены целые числа со знаком. Если вы распечатаете шестнадцатеричное представление значения или x с использованием

printf( "%08X\n", x ); // format output as hexadecimal

, оно будет отображаться как 0x000003FF, а не 0xFF030000.


Это на самом деле немного сложнее, чем это - значение в p[0] сначала преобразуется из char в int, и для сохранения знака это преобразованное значение равно 0xFFFFFFFF, что фактически передается в printf.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...