В C, если я приведу и разыменую указатель, имеет ли значение, какой я делаю первым? - PullRequest
12 голосов
/ 08 января 2011

В 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

Таким образом, приведение указателя работает иначе, чем приведение значения.

Почему это? Почему вторая версия не работает так, как мне кажется?

И это зависит от платформы, или я как-то вызываю неопределенное поведение?

Ответы [ 6 ]

14 голосов
/ 09 января 2011

Далее написано, что нужно получить число с плавающей точкой в ​​pf и преобразовать его в целое число.Приведение здесь - это запрос на преобразование числа с плавающей точкой в ​​целое число.Компилятор создает код для преобразования значения с плавающей точкой в ​​целочисленное значение.(Преобразование значений с плавающей точкой в ​​целые числа - это «нормальная» вещь.)

int i1 = (int) (*pf);

Следующее говорит сначала FORCE, что компилятор думает, что pf указывает на целое число (и игнорирует тот факт, что pf является указателем наfloat), а затем получить целочисленное значение (но это не целочисленное значение).Это странная и опасная вещь.Приведение в этом случае отключает правильное преобразование.Компилятор выполняет простое копирование битов в памяти (создание мусора).(И могут быть проблемы с выравниванием памяти!)

int i2 = *((int*) pf);

Во втором утверждении вы не «конвертируете» указатели.Вы говорите компилятору, на что указывает память (что в данном примере неверно ).

Эти два оператора делают очень разные вещи!

Помните, что c иногда использует один и тот же синтаксис для описания различных операций.

=============

Обратите внимание, что double - этотип с плавающей точкой по умолчанию в C (математическая библиотека обычно использует двойные аргументы).

4 голосов
/ 08 января 2011

Если вы разыменовываете сначала, приводите к int позже, вы получите обычное (усеченное) поведение приведения типов с плавающей точки к int.Если вы приведете сначала к указателю на int, а затем к разыменованию, поведение не определено стандартом.Обычно это проявляется в интерпретации памяти, содержащей float, как int.См. http://en.wikipedia.org/wiki/IEEE_754-2008, как это работает.

3 голосов
/ 08 января 2011

Конечно, это так! Приведение говорит компилятору, как смотреть на некоторый раздел памяти. Когда вы затем обращаетесь к памяти, она пытается интерпретировать данные, основанные на том, как вы сказали, что они смотрят на нее. Это проще понять, используя следующий пример.

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.

2 голосов
/ 08 января 2011

В ролях, тогда разыскивание означает «притворись, что я указываю на эту другую вещь, а затем получаю другую вещь, на которую якобы указывается».указывая на, а затем преобразовать его в эту другую вещь по обычным правилам ".

" Обычные правила "могут изменять биты, чтобы получить значение другого типа, которое логически представляет то же значение.Притворяться, что вы указываете на то, на что вы на самом деле не указываете, не может.

1 голос
/ 08 января 2011

int не представлен в памяти так же, как float.То, что вы видите, это то, что компилятор думает, что указатель на int, и он смотрит на 32 бита памяти, думая, что он найдет int, когда он на самом деле находит неопределенную часть float, которая появляется в некоторых оченьбольшое количество.

0 голосов
/ 08 января 2011

Согласно 6.5 / 7 стандарта, грубо говоря, сохраненное значение должно быть совместимо с действующим типом или тип символа. Итак, я думаю, что утверждение int i2 = *((int*) pf) не является четко определенным.

...