C - Поведение назначения литералов символов против литералов массива - PullRequest
0 голосов
/ 10 января 2020

Код:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    union {
        int theInt;
        char theChar;
    } u1;

    u1.theChar = 'A';
    printf("%i\n", u1.theInt);
    printf("%c\n\n", u1.theChar);

    u1.theChar = "A";
    printf("%i\n", u1.theInt);
    printf("%c\n\n", u1.theChar);

}

Дает вывод:

65
A

45
-

В первом назначении назначается символ 'A', а во втором массиве "A" назначены. Почему эти два назначения приводят к различным значениям объединения?

Ответы [ 2 ]

3 голосов
/ 10 января 2020

В вашем коде

 u1.theChar = "A";

неверно, так как RHS, "A" является строковым литералом, который сводится к указателю на массив, содержащий символ 'A' и завершающий ноль. Указатель не может быть назначен на char, это нарушение ограничения.

Если код компилируется и генерирует двоичный файл, выполнение вызовет неопределенное поведение.

2 голосов
/ 10 января 2020

Дает вывод: 65 A

Нет, не обязательно, потому что код вызывает неопределенное поведение, поскольку u1 не инициализируется и не ' Если его адрес занят, см. (Почему) использует неинициализированную переменную с неопределенным поведением?

Неопределенное поведение = не известно, что произойдет. В 32-разрядной системе с прямым порядком байтов в лучшем случае она печатает 3 байта неопределенного мусора, за которым следует 4-й байт со значением ASCII. Я получаю немного мусора 2005012545. Чтобы разобраться с мусором, я мог бы вместо этого напечатать его в шестнадцатеричном виде: 0x77821041, где 41 - младший бит, содержащий значение ASCII 0x41 / 65.

Это потому, что вы не инициализировали union и затем вы, вероятно, запускаете отладочную сборку, которая просто обнуляет все значения стека, так что программа, кажется, работает для вас.

Исправьте это, инициализируя объединение известным значением: ... } u1 = {0};.


Что касается u1.theChar = "A";, он даже не компилируется на правильно настроенном компиляторе C (см. «Указатель из целого числа / целое число из указателя без преобразования») вопросы ). Поскольку строковый литерал "A" в конечном итоге становится типом char* при назначении. но theChar имеет тип char. Это также неопределенное поведение, которое бессмысленно понимать. В лучшем случае вы получите один из байтов из адреса указателя символов, хранящегося в theChar, но код недействителен, поэтому нет никаких сведений о том, что он будет делать.


Необходимо исправления:

#include <stdio.h>

int main (void)
{
    union {
        int theInt;
        char theChar;
    } u1 = {0};

    u1.theChar = 'A';
    printf("%d\n", u1.theInt);
    printf("%c\n\n", u1.theChar);

    u1.theChar = "A"[0];
    printf("%d\n", u1.theInt);
    printf("%c\n\n", u1.theChar);
}

Вывод теперь предсказуем, пока мы придерживаемся систем с прямым порядком байтов:

65
A

65
A

32-битная система с большим порядком байтов должна вместо этого выводить:

1090519040
A

1090519040
A

1090519040 = 0x41000000

...