Объединение C ++ для представления памяти данных и типа скалярной переменной C - PullRequest
6 голосов
/ 12 ноября 2011

Сегодня у меня странный вопрос.

Код (C ++)

#include <iostream>

union name
{
    int num;
    float num2;

}oblong;


int main(void)
{
    oblong.num2 = 27.881;

    std::cout << oblong.num << std::endl;

    return 0;
}

Код (С)

#include <stdio.h>

int main(void)
{
    float num = 27.881;

    printf("%d\n" , num);

    return 0;
}

Вопрос

  1. Как мы знаем, объединения C ++ могут содержать более одного типа элемента данных, но только один тип одновременно. Таким образом, в основном name oblong будет резервировать только одну часть памяти, которая является 32-битной (поскольку самый большой тип в объединении - 32-битная, int и float), и эта часть может содержать целое число или число с плавающей запятой.

  2. Так что я просто присваиваю значение 27,881 в oblong.num2 (как вы можете видеть из приведенного выше кода). Но из любопытства я обращаюсь к памяти, используя oblong.num, который указывает на ту же область памяти.

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

  4. Я знаю, что это явление также будет происходить в C, поэтому я инициализирую переменную типа с плавающей запятой значением, а затем читаю ее, используя %d. Поэтому я просто пробую ее, используя то же значение 27.881, который вы можете увидеть выше. Но когда я запускаю его, происходит нечто странное, то есть значение, которое я получаю в C, отличается от C ++.

  5. Почему это происходит? Из того, что я знаю, два значения, которые я получаю из двух кодов в конце, не являются мусорными значениями, но почему я получаю разные значения? Я также использую sizeof для проверки целочисленных и плавающих размеров как C, так и C ++, и оба они 32-битные. Таким образом, размер памяти не тот, который вызывает это, так что может подсказать разницу в значениях?

Ответы [ 2 ]

9 голосов
/ 12 ноября 2011

Прежде всего, неправильная строка формата printf() является неопределенным поведением.Теперь это говорит о том, что на самом деле происходит в вашем случае:

В функциях vararg, таких как printf(), целые числа, меньшие int, повышаются до int, а числа с плавающей точкой меньше doubleв double.

В результате ваш 27.881 преобразуется в 8-байтовый двойной, поскольку он передается в printf().Поэтому двоичное представление больше не совпадает с float.

Строка формата %d предполагает 4-байтовое целое число.Таким образом, вы будете печатать младшие 4 байта представления двойной точности 27.881.(предполагая little-endian)

* На самом деле (при условии строгого FP) вы видите нижние 4 байта 27.881 после того, как он приведен к float, а затем повышен до double.

2 голосов
/ 12 ноября 2011

В обоих случаях вы сталкиваетесь с неопределенным поведением. Ваша реализация просто делает что-то странное.

...