В 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) не определено.