В C, если я бросаю и разыскиваю указатель, имеет ли значение то, что я делаю первым?
В C вы можете использовать как простые типы данных, такие как int
, float
, так и указатели на них.
Теперь я бы предположил, что если вы хотите конвертировать из указателя в один тип в значение другого типа (например, от *float
до int
), порядок литья и разыменования не имеет значения. То есть что для переменной float* pf
у вас есть (int) *pf == *((int*) pf)
. Подобная коммутация в математике...
Однако, похоже, это не так. Я написал тестовую программу:
#include <stdio.h>
int main(int argc, char *argv[]){
float f = 3.3;
float* pf = &f;
int i1 = (int) (*pf);
int i2 = *((int*) pf);
printf("1: %d, 2: %d\n", i1, i2);
return 0;
}
а в моей системе выход
1: 3, 2: 1079194419
Таким образом, приведение указателя, похоже, будет отличаться от значения.
Почему? Почему вторая версия не делает то, что я думаю, что она должна?
И это зависит от платформы, или я каким-то образом вызываю поведение undefined?
Ответы
Ответ 1
Следующее говорит, что получает float в pf и преобразует его в целое число. Кастинг здесь - это запрос на преобразование float в целое число. Компилятор создает код для преобразования значения float в целочисленное значение. (Преобразование значений float в целые числа - это "нормальная" вещь.)
int i1 = (int) (*pf);
Следующее говорит, что сначала FORCE компилятор должен думать, что pf указывает на целое число (и игнорирует тот факт, что pf является указателем на float), а затем получает целочисленное значение (но это не целочисленное значение). Это странная и опасная вещь. Кастинг в этом случае ОТКЛЮЧАЕТ правильное преобразование. Компилятор выполняет простую копию бит в памяти (создавая мусор). (И могут быть проблемы с выравниванием памяти тоже!)
int i2 = *((int*) pf);
Во втором утверждении вы не являетесь "конвертирующими" указателями. Вы сообщаете компилятору, что указывает память (что в этом примере неверно).
Эти два утверждения делают очень разные вещи!
Имейте в виду, что c несколько раз использует один и тот же синтаксис для описания различных операций.
=============
Обратите внимание, что double - это тип с плавающей запятой по умолчанию в C (математическая библиотека обычно использует двойные аргументы).
Ответ 2
Если вы сначала разыщите, затем перейдете к int позже, вы получите обычное (усечение) поведение приведений от float до int. Если вы сначала указали на int, а затем разыменовали, поведение не определено стандартом. Обычно это проявляется в интерпретации памяти, которая содержит float как int. См. http://en.wikipedia.org/wiki/IEEE_754-2008, как это работает.
Ответ 3
Конечно! Casting сообщает компилятору, как смотреть на какой-то раздел памяти. Когда вы получаете доступ к памяти, он пытается интерпретировать данные там, основываясь на том, как вы сказали, что смотрите на нее. Это проще понять, используя следующий пример.
int main()
{
char A[] = {0, 0, 0, 1 };
int p = *((int*)A);
int i = (int)*A;
printf("%d %d\n", i, p);
return 0;
}
Вывод на 32-разрядной машине с маленькими концами будет 0 16777216
. Это связано с тем, что (int*)A
сообщает компилятору рассматривать A как указатель на целое число, и, следовательно, при разыменовании A он смотрит на 4 байта, начиная с A (as sizeof(int) == 4)
. После учета конечности содержимое 4 байтов оценивается до 16777216. С другой стороны, *A
разыменования A для получения 0
и (int)*A
показывают, что для получения 0.
Ответ 4
В ролях, затем разыгрывание означает "притворяться, что я указываю на эту другую вещь, а затем получаю другую вещь, на которую, как предполагается, указывают".
Разнообразное выражение, а затем бросок означает "получить то, на что я на самом деле указываю, а затем преобразовать его в эту другую вещь по обычным правилам".
"Обычные правила" могут изменять биты вокруг, чтобы получить значение другого типа, логически представляющего одно и то же значение. Притворяясь, что вы указываете на то, на что вы на самом деле не указываете, не может.
Ответ 5
int не представляется в памяти таким же, как float. То, что вы видите, - это компилятор, считающий, что указатель относится к int, и он смотрит на 32 бита памяти, думая, что он найдет int, когда он фактически найдет часть undefined поплавка, которая выходит на очень большое количество.
Ответ 6
В соответствии с 6.5/7 стандарта, грубо говоря,
сохраненное значение должно быть совместимо с эффективным типом
или тип символа.
Итак, я думаю, что выражение int i2 = *((int*) pf)
не определено.