2013-04-03 3 views
7

Я попытался запустить этот код,В чем разница между указателем float и адресом int указателя?

int *p; 
float q; 
q = 6.6; 
p = &q; 

Хотя это будет предупреждение, но я думаю, что &q и p имеют одинаковый размер, поэтому p может иметь адрес q. Но когда я печатаю &q и p, я получаю разные результаты. Это мой выход

*p = 6.600000 
q = 0.000000, p = 0x40d33333, &q = 0x7fffe2fa3c8c 

Что такое, что мне не хватает? И p и &q - это то же самое, когда оба указателя и тип переменной одинаковы.

Мой полный код

#include<stdio.h> 
void main() 
{ 
    int *p; 
    float q; 
    q = 6.6; 
    p = &q; 
    printf("*p = %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q); 
} 
+3

Пожалуйста, покажите код, который вы используете, чтобы получить этот результат. –

+0

Если я понял, вы можете использовать объединение ... –

+4

Это 'int main (void)', а не 'void main()'. 'void main()' полезен в основном для обнаружения книг, написанных авторами, которые не очень хорошо знают C. –

ответ

7

Вам нужно более серьезно относиться к предупреждениям компилятора.

C не требует от компиляторов отклонить недействительные программы, для этого требуется просто «диагностика» для нарушений правил. Диагностика может быть либо фатальным сообщением об ошибке, либо предупреждением.

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

void main() 

Это не так; это должно быть int main(void). Ваш компилятор может позволить вам уйти от него, и это может не вызвать никаких видимых проблем, но нет смысла не писать его правильно. (Это не совсем просто, но это достаточно близко.)

int *p; 
float q; 
q = 6.6; 

Это нормально.

p = &q; 

p имеет тип int*; &q имеет тип float*. Присвоение одного другому (без литья) является нарушением ограничения . Самый простой способ взглянуть на это - это просто незаконно.

Если вы действительно хотите сделать это задание, вы можете использовать бросок:

p = (int*)&q; /* legal, but ugly */ 

но редко хороший повод, чтобы сделать это. p - указатель к int; он должен указывать на объект int, если у вас есть очень. Это повод, чтобы он указывал на что-то еще. В некоторых случаях само преобразование может иметь неопределенное поведение.

printf("*p = %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q); 

Формат %f требует double аргумента (а float аргумента назначен double в этом контексте так float будет хорошо). Но *p имеет тип int. Вызов printf с аргументом неправильного типа приводит к тому, что поведение вашей программы не определено.

%p требует аргумента типа void*, а не только любого типа указателя. Если вы хотите, чтобы напечатать значение указателя, вы должны привести его к void*:

printf("&q = %p\n", (void*)&q); 

Это, скорее всего, работать без броска, но опять же, поведение не определено.

Если вы получаете какие-либо предупреждения при компиляции программы, даже не пытайтесь ее запустить. Сначала исправьте предупреждения.

Что касается вопроса в вашем заголовке, указатели типа int* и float* бывают разных типов. int* должен указывать на объект int; a float* должен указывать на объект float. Ваш компилятор может позволить вам их смешивать, но результат этого - либо определенный реализацией, либо неопределенный. Язык C, и особенно многие компиляторы C, позволят вам уйти со многими вещами, которые не имеют большого смысла.

Причина, по которой они являются различными типами, состоит в том, чтобы (попытаться) предотвратить или, по крайней мере, обнаружить ошибки в их использовании. Если вы объявляете объект типа int*, вы говорите, что намерены указать ему объект int (если он не является нулевым указателем). Хранение адреса объекта float в вашем объекте int* почти наверняка является ошибкой. Обеспечение безопасности типа позволяет обнаруживать такие ошибки как можно раньше (когда ваш компилятор печатает предупреждение, а не когда ваша программа вылетает во время демонстрации для важного клиента).

Скорее всего (но не гарантировано), что int* и float* имеют одинаковый размер и имеют одинаковое внутреннее представление. Но значение , означающее объекта int*, не является «набором из 32 (или 64) битов, содержащих виртуальный адрес», а «что-то, что указывает на объект int».

+5

Поскольку вы бросаете книгу на него, чтение '* p' нарушает строгий псевдоним, поэтому он сам UB независимо от того, что вы делаете со значением. –

+3

'p = (int *) & q;' не всегда является законным. C 2011 (n1570) 6.3.2.3 7 говорит, что поведение не определено, если результат не правильно выровнен. Это будет на типичных текущих платформах, так что это нормально, если вы планируете свою цель соответственно, но не, если вы планируете стандартное C или хотите понять семантику спецификации C. –

+0

@EricPostpischil: Правильно - за исключением того, что неясно, что означает «законный» в этом контексте (стандарт никогда не использует это слово). Я обновил свой ответ. –

7

Вы получаете неопределенное поведение, потому что вы передаете неправильные типы, printf. Когда вы скажете, что он ожидает поплавок, он действительно ожидает double - но вы передаете int.

В результате он печатает неверную информацию, потому что printf полностью использует строку формата для доступа к аргументам, которые вы передаете.

0

В дополнение к тому, что сказано teppic,

Рассмотрим,

int a = 5; 
int *p = &a; 

В этом случае мы укажем компилятору, что p собирается указать на целое число. Поэтому известно, что когда мы делаем что-то вроде *p, во время выполнения, нет. байт, равных размеру int.

Если присвоить адрес типа данных, занимающей x число байтов, указатель которого объявлен держать адрес типа данных меньшего числа байтов, чем x, вы читаете неверное число байтов при использовании оператора разыменования.

+1

Кто говорит, что 'float' больше, чем' int'? Это разрешено, но, вероятно, нет. –

+0

@Steve Да, я согласен, что это не всегда так. Но в некоторых системах это так. Чем больше я имел в виду, тем больше байтов. Может быть, мой пример не самый лучший, чтобы выразить свою точку зрения. Я просто хотел указать, что мы определяем тип указателя по причине [точно так же, как мы должны указывать указатель «void» перед использованием косвенности на нем] –

+0

Я предлагаю, чтобы размер не был точечным. Я работал над системами, где 'sizeof (int) == sizeof (double)'; поведение программы в такой системе также не определено. Вы рискуете создать впечатление, что типы указателей смешивания подобны этому, если размеры совпадают. И 'p' необходимо указывать на' int', а не только на любое целое число. –

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