2016-07-09 2 views
0

Я не понимаю, выхода в следующем коде C:Неожиданный выход в коде C с накидной

#include <stdio.h> 
int main() 
{ 
    union U 
    { 
     int i; 
     char s[3]; 
    } u; 
    u.i=0x3132; 
    printf("%s", u.s); 
    return 0; 
} 

Исходная память 32 бит и является двоичным значением 0x3132 которое

0000 0000 0000 0000 0011 0001 0011 0010 ,

Если последние три байта 0x3132 - это значение s (без начальных нулей), то s[0]=0011,s[1]=0001,s[2]=0011.

Приводятся значения s=0011 0001 0011=787.

Вопрос: Почему выход 21, а не 787?

+2

'u.s' не является строкой, и вы не можете использовать'% s' для печати. Это технически неопределенное поведение. Сказав это, учтите, что '0x31 == 49 == '1'' и' 0x32 == 50 ==' 2'' и, надеюсь, это должно быть ясно для вас. – kaylum

+0

Последние две группы из 8 бит (в малочисленных концах) '0011 0010' и' 0011 0001', которые в шестнадцатеричном виде являются '32' и' 31', которые в ASCII являются '' 2'' и '' 1'' , –

+1

Предполагая, что sizeof (int) равно 4 без каких-либо битов заполнения, а порядок байтов - немногочисленный и ascii-кодирование, то это поведение определено. – 2501

ответ

2

первым увидеть этот образец кода:

#include <inttypes.h> 
#include <stdio.h> 
#include <stdint.h> 
int main() 
{ 
    union{ 
     int32_t i32; 
     uint32_t u32; 

     int16_t i16[2]; 
     uint16_t u16[2]; 

     int8_t i8[4]; 
     uint8_t u8[4]; 
    } u; 

    u.u8[3] = 52; 
    u.u8[2] = 51; 
    u.u8[1] = 50; 
    u.u8[0] = 49; 

    printf(" %d %d %d %d \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); // 52 51 50 49 
    printf(" %x %x %x %x \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); // 34 33 32 31 
    printf(" 0x%x \n", u.i32); // 0x34333231 

    return 0; 
} 

объединение здесь только для доступа к памяти u в 6 различных способов.
вы можете использовать u.i32 для чтения или записи, как int32_t или
вы можете использовать u.u32 для чтения или записи, как uint32_t или

вы можете использовать u.i16[0] или u.i16[1] для чтения или записи, как int16_t или вы можете использовать u.u16[0] или u.u16[1] читать или писать, как uint16_t или

или как это написать, как uint8_t:

u.u8[3] = 52; 
u.u8[2] = 51; 
u.u8[1] = 50; 
u.u8[0] = 49; 

и читать, как это, как int8_t:

printf(" %d %d %d %d \n", u.u8[3], u.u8[2], u.u8[1], u.u8[0]); 

затем выход:

52 51 50 49 

и читать как int32_t:

printf(" 0x%x \n", u.i32); 

затем выход:

0x34333231 

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

в примере кода u.i=0x3132; это пишет 0x3132 внутри u.i памяти, и в зависимости от порядка байтов вас система, которая является прямой порядок байтов здесь, то вы спросили printf("%s", u.s); от компилятора, так что нам есть массив типа char означает константный указатель на символ типа , так что это printf("%s", u.s); будет читать u.s[0] и печатает, что на выходе stdout затем читает u.s[1] и печатает это на выходе stdout и так далее ..., пока один из этих u.s[i] не будет равен нулю.
Это то, что делает ваш код, поэтому, если ни один из нас [0], нас [1], нас [2], нас [3] не равен нулю, тогда память за пределами вашего союза будет считана до тех пор, пока не будет найден один нуль или система ошибка памяти error бывает.

4

Значение 0x3132 представлено в памяти как: 0x32, 0x31, 0x0, 0x0, так как порядок байтов не имеет большого значения.

Вывод printf выводит строку, представленную членом союза s. Строка печатается байтом байтом. Сначала 0x32 и затем 0x31, которые являются значениями ascii для символов: '2' и '1'. Затем печать останавливается, так как третий элемент является нулевым символом: 0x0.

Обратите внимание, что представление int является реализацией, и может не состоять из 4 байтов и может иметь отступы. Таким образом, член объединения s может не представлять строку, и в этом случае вызов printf с спецификатором %s приведет к неопределенному поведению.

+0

Я случайно должен был нажать кнопку «вниз», отредактируйте свой пост, чтобы я мог изменить это в upvote =) – Cyclonecode

+0

@Cyclone Done ... – 2501

+0

Извините, что =) – Cyclonecode

1

Это означает, что вы машина прямой порядок байтов, поэтому байты хранятся в обратном порядке, как это:

32 31 00 00 

Итак: s[0] = 0x32, s[1] = 0x31, s[2] = 0x00.

Даже если в теории печати массив символов, использующих , это неопределенное поведение, это работает, оно печатает 0x32 (character '2'), 0x31 (character '1'), а затем останавливает 0x00.

0

если вы пишете код, как это:

#include <stdio.h> 

int main(void) 
{ 
    union U 
    { 
     int i; 
     char s[3]; 
    } u; 

    u.i=0x3132; 
    printf("%s", u.s); 
    printf("%8x\n", (unsigned)u.i); 
} 

Тогда вы увидите, что содержимое пользовательского интерфейса 0x0000000000003132, который фактически будет храниться как: 0x3231000000000000 из-за порядка байтов

и 0x00 не печатаемый символ, поэтому выход из второго вызова printf() является <blank><blank><blank><blank><blank><blank>3132, как можно было бы ожидать

и ASCII символ 1 является 0x31 и ASCII символ 2 - 0x32, а первое 0x00 останавливает операции% s, поэтому первые printf() выходы 21.

Смежные вопросы