Разыменование и приведение типов - PullRequest
1 голос
/ 08 марта 2011

Я построил следующие разделы кода, чтобы помочь себе понять разыменование указателя и приведение типов в C.

char a = 'a';
char * b = &a;
int i = (int) *b;

Для вышеизложенного я понимаю, что в 3-й строке я разыменовал b иgot 'a' и (int) приведут значение типа a к соответствующему значению 97, которое сохраняется в i.Но для этого раздела кода:

char a = 'a';
char * b = &a;
int i = *(int *)b;

Это приводит к тому, что я являюсь произвольным большим числом, например, 792351. Я предполагаю, что это адрес памяти, но мой вопрос - почему?Когда я вписываю b в целочисленный указатель, это фактически заставляет b указывать на другую область в памяти?Что происходит?

РЕДАКТИРОВАТЬ : Если вышеупомянутое не работает, то почему бы что-то вроде этой работы:

char a = 'a';
void * b = &a;
char c = *(char *)b;

Это правильно назначает 'a'к.

Ответы [ 6 ]

3 голосов
/ 08 марта 2011

Ваш int больше, чем ваш char - вы получаете значение 'a' + некоторые случайные данные, следующие за ним в памяти.

Например, при условии, что это расположение в памяти:

'a'
0xFF
0xFF
0xFF

Ваши char * и int * оба указывают на 'a'. Когда вы разыменовываете char *, вы получаете только первый байт, 'a'. Когда вы разыменовываете int * (при условии, что ваш int 32-битный), вы получаете 'a' и 3 байта неинициализированных данных , следующих за ним.

РЕДАКТИРОВАТЬ: В ответ на обновленный вопрос:

В char c = *(char *)b;, b все еще указывает на значение 'a'. Вы приводите его к char *, а затем разыменовываете его, получая символ , на который указывает char *

2 голосов
/ 08 марта 2011

Последняя строка, о которой вы беспокоитесь, делает очень плохую вещь. Во-первых, он обрабатывает b как int*, тогда как b является char*. То есть указатель памяти на b предполагается как 4 байта (обычно) вместо 1 байта. Поэтому, когда вы разыменовываете его, он переходит к 1 байту, указанному фактическим b, также принимает следующие 3 байта, обрабатывает эти 4 байта как одно целое и выдает результат. Вот почему это мусор.

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

1 голос
/ 08 марта 2011

Поскольку char имеет длину 1 байт, а int 4, когда вы читаете int с адреса одного символа, вы читаете символ и еще 3 байта. Содержимое этих байтов соответствует тому, что происходит в памяти (указатели, значение b) и даже может быть нераспределенным (что приводит к ошибке сегментации ).

1 голос
/ 08 марта 2011

Вы используете char указатель на указатель типа int. Символы (обычно) хранятся как 8 бит. int s, с другой стороны, 32-битные (или 64-битные в 64-битных системах). Поэтому, если вы посмотрите на остальные 24 бита памяти рядом с 8-битным значением b, вы получите кучу дополнительных битов, которые не были инициализированы. Даже положение *b в i зависит от архитектуры.

big-endian:    **** ****|**** ****|**** ****|0110 0001
little-endian: 0110 0001|**** ****|**** ****|**** ****

Когда вы разыгрываете персонажа, хранящегося в приведенном выше, все звездочки становятся релевантными.

0 голосов
/ 08 марта 2011

Во втором случае вы обрабатываете тот же адрес, как если бы он указывал на int.Официально, результатом является просто неопределенное поведение.

Реально, то, что происходит в том, что все, что происходит в четырех байтах 1 , начинающихся с этого адреса, интерпретируется как int.1006 * 1 4 байта, предполагая 32-битное целое число - если ваша реализация имеет, например, 64-битное целое число, это будет 8 байтов.

0 голосов
/ 08 марта 2011

Когда вы вводите приведение к типу (int *), он будет ссылаться на 4 байта (размер, если int ) в памяти.

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