2013-09-18 4 views
0

Предположим, что у меня есть следующий код СУказатели и тип литья в C

#include<stdio.h> 

int main() 
{ 
    float a=3.14; 
    char j= a; 
    printf("%d\n", j); 
    return 0; 
} 

OUTPUT: 3 (как и ожидалось)

Теперь предположим, что я делаю то же самое, используя указатели.

#include<stdio.h> 

int main() 
{ 
    float a=3.14; 
    char *j= (char*)&a; 
    printf("%d\n", *j); 
    return 0; 
} 

ВЫВОД: -61

Теперь, почему я получаю -61 вместо

EDIT Из ответов до сих пор, я понял, что с полукокса (обычно) длиной 1 байт, поэтому указатель char выбирает только первый байт и дает -61 вместо печати полный поплавок.

Но вот, как компилятор определяет размер типа данных, чтобы он знал, где он должен прекратить печать. Будет ли он определяться типом указателя, каким он кажется в результате ответов до сих пор?

Но в идеале, во время лексической фазы компиляции создается таблица символов и тип данных также понимается в то время.

Вопрос Итак, если я говорю компилятора с помощью указателя обугленного, «Эй Бадди !! Просто распечатайте данные (типа), которые вы найдете в месте, указанном указателем полукокса заданного мною.». Это должен затем напечатать полный float, как компилятор знает теперь, что его float.?

+0

Редактирование только что сделал это Боюсь, вопрос еще более запутанный. – Lundin

ответ

0

Первый фрагмент вовлекает преобразование из один тип к другому; когда вы назначаете 3.14 на j, компилятор меняет представление значения из формата float (0xc3f54840 в моей системе) на char (0x03). Помните, что когда значения с плавающей запятой преобразуются в целые значения, дробная часть просто усекается.

Во втором фрагменте вы меняете, как вы интерпретируете значение, хранящееся в a. Вы передаете указатель на значение первого байта (значение 0xc3). Поскольку printf является вариационной функцией, он повышает значения типа char до int. Так как высокий бит 0xc3 установлен, он под знаком - увеличивает значение до 0xffffffc3, что составляет -65.

Для хихиканье, я написал следующий код, чтобы показать, как различные значения представлены в памяти :

#include <stdio.h> 

int main(void) 
{ 
    float a = 3.14; 
    char j = a; 
    unsigned char ju = a; 
    char *jp = (char *) &a; 
    unsigned char *jpu = (unsigned char *) &a; 

    union { float a; unsigned char b[sizeof (float)]; } bytes; 

    bytes.a = a; 

    printf("a = %f\n", a); 
    printf("j = %d\n", j); 
    printf("ju = %u\n", ju); 
    printf("*jp = %d (%02x) \n", (char) *jp, (char) *jp); 
    printf("*jpu = %u (%02x) \n", (unsigned char) *jpu, (unsigned char)*jpu); 
    printf("a representation = "); 
    for (size_t i = 0; i < sizeof bytes.a; i++) 
    { 
    printf("%02x ", bytes.b[i]); 
    } 
    putchar('\n'); 
    printf("j representation = %02x\n", j); 
    printf("ju representation = %02x\n", ju); 

    return 0; 
} 

который дал следующие результаты на моей системе:

 
a = 3.140000 
j = 3 
ju = 3 
*jp = -61 (ffffffc3) 
*jpu = 195 (c3) 
a representation = c3 f5 48 40 
j representation = 03 
ju representation = 03 


1. Я технически совершаю no-no, читая из другого члена профсоюза, чем тот, который я последний назначил; не может вспомнить, действительно ли это неопределенное поведение или нет.

+0

Re: читающий член, отличный от последнего написания: в C99 не было ясно (он был указан как ** Unspecified ** Поведение в ненормативном приложении J), но это была ошибка, был дефект Отчет, и он больше не был определен как C99-TC3. C11 также изменяет Приложение J, чтобы указать _ «значение байтов, которое соответствует членам профсоюза, отличным от последнего, сохраненного в« _ как Unspecified Behavior ». Отчет о дефектах: http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm – ninjalj

0

Поскольку вы перешли от правильного преобразования типов (вы должны были получить предупреждения об усечении) в поразрядной ситуации. Все ставки отключены, когда вы просто выбираете первый байт представления в памяти без какого-либо семантического преобразования.

+0

Я использую gcc и, на удивление, я не получил никаких предупреждений. –

1
char *j= (char*)&a; 

Теперь j указывает на первый байт a, который сам по себе, вероятно, длиной четыре байта.

printf("%d\n", *j); 

Здесь вы передаете значение первого байта в базовом представлении a, как внутр.

По IEEE 754 это первый байт 0x4048f5c3 (отметьте его here). Предполагая, что ваша машина немного ориентирована, вы получаете 0xc3. Ваш char подписан, поэтому вы получаете 0x100-0xc3 = 61.


Что касается вашего редактирования: вы не просите компилятор напечатать тип данных, на который указывает ваш указатель. Единственная информация, которую компилятор может достоверно иметь о содержимом этого адреса, - это статический тип, который он имеет, а именно: char. В C. информация о типе отсутствует.

Компилятор может accidentaly, знай, что эта область памяти случается быть float (потому что он умный и/или вы дали вырожденный пример), но это может помочь только для предупреждения и оптимизации; Как насчет этого идентичного случая?

void foo(char* j) 
{ 
    printf("%d\n", *j); 
} 

// perhaps in a translation unit: 

int main() 
{ 
    float a=3.14; 
    foo((char*)&a); 
    return 0; 
} 

В этом случае, вы явно попросили его рассматривать эту область памяти как символ, поэтому он делает именно это, и не имеет ничего, чтобы предупредить о.

+2

Не думайте, что он не определен, так как 'char *' разрешено псевдониму, а значение должно быть просто повышено до 'int'. –

+0

Значит ли это реализация? – Elazar

+0

Я так думаю ... Я все еще думаю об этом –

0

Вы указали указатель, но указали данные в переменной «a» все еще плавать. Таким образом, печать неверна. Вы должны отбрасывать данные перед печатью:

printf("%d\n", (int)(*(float*)(void*)j)); 
+0

Сэр, это задача. –

+0

Итак ... вы в основном делаете следующее: '(int) * (float *) (void *) (char *) & a'. Поздравляю, это то же самое, что писать '(int) a' очень очень запутанным образом. – Lundin

+0

@ InsaneCoder Он делает? То, что вы искали все время, было просто способом распечатать float как int? Этот код идентичен 'printf ("% d \ n ", (int) a);' – Lundin

0

Вот описание кода:

#include<stdio.h> 

int main() 
{ 
    float a=3.14; 

Вы объявляете переменную a типа float и поставить 3.14 внутри, что 0x4048f5c3 согласно IEEE754 представлению плавающие числа. (http://www.binaryconvert.com/result_float.html?decimal=051046049052) Так в основном, в памяти, у вас есть 4 байта: 0x4048f5c3

char *j= (char*)&a; 

Вы объявляете указатель, который указывает на первый байт a, который содержит 0xC3 (в зависимости от внутреннего представления данных вашей системы) , Поскольку j - это char*, вы забудете обо всех остальных байтах.

printf("%d\n", *j); 

0xC3 является -61 для знакового полукокса (http://www.binaryconvert.com/result_signed_char.html?decimal=045054049)

return 0; 
} 

Однако, когда в первом случае вы писали char j = a;, a был правильно литым.

И, конечно же, если вы накладываете j в float*, вы можете получить доступ к полному номеру:

#include <stdio.h> 

int main() 
{ 
    float a=3.14; 
    char *j= (char*)&a; 
    printf("%d\n", *j); 

    printf("%f\n", *((float*)j)); 

    return 0; 
} 

Напечатает:

-61 
3.140000 
0

Значение переменной с плавающей запятой должно интерпретироваться с использованием команд с плавающей запятой, поскольку оно хранится в памяти с использованием некоторых стандартов с плавающей точкой, таких как IEEE 754.

Но типажей к полукокса * будет интерпретировать его, используя общие целочисленные инструкции &, которых не знает о нотации, поэтому результат не определен.

Приведение к символ приведет использовать с плавающей точкой в ​​целое число инструкции преобразования, и результат будет мантисса часть переменной с плавающей точкой (если это не переполняет переменный размер назначения)

Смежные вопросы