Указатели, указывающие на неверное местоположение - PullRequest
3 голосов
/ 23 октября 2010
#include <stdio.h>

int main(void)
{ 
   int x = 1000;
   char *ptr = &x;
   printf("%d\n",*ptr);
   return 0;
 }

Output: -24 /*In gcc 4.4.3 in Ubuntu 10.04 OS*/ С warning: initialization from incompatible pointer type

Что я думаю, если бы базовый тип указателя имел тип int, то он бы получил 4 байта из местоположения, на которое он указывает. Тогда O / P был бы 1000. Но, так как я изменил базовый тип на char он извлечет 1 байт из местоположения, на которое он указывает, когда я разыменую его. Но как ответ -24. Снова, когда я изменил программу, как показано ниже

#include <stdio.h>

    int main(void)
    { 
       int x = 1000;
       float *ptr = &x;
       printf("%f\n",*ptr);
       return 0;
     }

Вывод становится 0,000000 с тем же предупреждением. Когда я разыменую указатель, он получит 4 байта из того места, куда он указывает. Но как O / P равен 0,000000. Я немного запутался. новичок в программировании на C, поэтому любая ошибка в задании вопроса, пожалуйста, прости меня. Thanx

Ответы [ 3 ]

3 голосов
/ 23 октября 2010

Разыменование символьного указателя дает вам первый символьный фрагмент внутреннего представления 1000 как целое число.Затем этот символ был переведен в int (согласно правилам для аргументов varidac) и интерпретирован как целое число.

На вашем компьютере с этим компилятором результат был -24.

* 1005Блок символов размером вероятно 8-битный байт.

Целое число было вероятно , представленное 4 или 8 из этих байтов.

внутреннее представление было вероятно 2s-дополнением, и вероятно хранилось в порядке с прямым порядком байтов.

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


То же самое с помощью указателя с плавающей точкой вернуло чак памяти размером float вместо char, и теперь у вас может быть real (хех!) проблема, потому что мы не знаем, на данный момент, будет ли представление float даже соответствовать представлению int, поэтому вы можете обращаться к неинициализированной памяти.

В любом случае, когда вы разыменовываетеfloat* он интерпретировал эту память как float (который имеетболее сложную внутреннюю структуру, чем символ или даже целое число, дополняющее 2s), и преобразовал ее в double (эти правила для аргументов varidac снова) , затем была предпринята попытка интерпретировать это представление как целое число(и если float соответствует вписывается в int, double, вероятно, не , так что вы интерпретируете часть удваивается как int!).Тьфу.

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

Итак, урок здесь такой:

Не делайте этого.

И вспомогательный урок, только для экспертов, это

Все же не делайте этого.

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

1 голос
/ 23 октября 2010

1000 в шестнадцатеричном формате - 0x3E8. Если ваша система хранит int как 32-разрядное значение с прямым порядком байтов, то x сохраняется в памяти следующим образом:

+------+------+------+------+
| 0xE8 | 0x03 | 0x00 | 0x00 |
+------+------+------+------+
    ^
    |      ---increasing addresses--->
   &x

Для первого случая:

Если ваша система имеет 8-битный char, когда вы присваиваете &x для char *ptr и затем разыменовываете его, вы получаете значение 0xE8, интерпретируемое как char.

Если ваша система воспринимает тип char как подписанный, и , если подписанное представление является дополнением к 2, то 0xE8 интерпретируется как -24.

Для второго случая:

Если ваша система имеет 32-разрядный float с прямым порядком байтов, когда вы присваиваете &x float *ptr, а затем разыменовываете его, вы получаете значение с битовой комбинацией 0x000003E8 интерпретируется как float.

Если ваша система представляет float в формате IEEE 754, то самый старший бит представляет знак (0 означает, что он положительный), следующие 8 старших бит представляют показатель степени (0 означает, что число равно нулю или денормализовано (очень мало), а оставшиеся 23 бита представляют собой «значение» или «мантиссу» (0x3E8 или 1000). Это на самом деле имеет значение 1000 * 2 -149 , что примерно равно 1,4013 * 10 -42 - вы можете увидеть это значение, используя %g вместо %f в printf формат строки.

0 голосов
/ 23 октября 2010

Суть вашей проблемы в том, что вы пытались напечатать символ (*ptr) как целое число (%d).

Поскольку вы указали целое число для printf с помощью %d,для отображения потребуется 4 байта из стека.

Однако, поскольку вы передали char в printf, вы пропустили только 1 байт.Оставшиеся 3 байта, которые printf извлекает, будут случайными данными стека и могут быть чем угодно.

В результате вы получите -24 или другое непоследовательное значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...