2015-12-01 3 views
2

Я столкнулся с некоторым кодом, который использовал оба эти, казалось бы, взаимозаменяемые.Разница между литьем (char *) и * (char **) в C (реализация malloc)

Я дам пример проблемы, что я запутались:

Скажем, у меня есть некоторые данные в дважды связанный список, и я хочу, чтобы изменить некоторые символы/адреса в блоке. Итак, мы имеем:

void* ptr; //points to beginning of block 
void* prevPtr; //prev item in LL 
void* nextPtr; //next item in LL 
*((char *)ptr) = prevPtr; 
*(*(char **)(ptr) + sizeof(char*)) = nextPtr; 

В этом примере, в чем разница между двумя нижними линиями (кроме явно движется к месторасположению nextPtr «s в памяти)?

ответ

1

В первом, ptr приводится к указателю char и затем prevPtr, а не значение prevPtr указывает на адрес, но он хранит, присваивается символьной типизированных ячейку памяти ptr в данный момент указывает. Во втором случае ptr преобразуется в указатель на char и затем разыменовывается перед добавлением смещения, что приводит к тому, что значение nextPtr (а не значение, на которое оно указывает) записывается на одно смещение размера указателя после того, как указана точка на ptr, который в вашем примере должен быть символом. Другими словами, ваш примерный код не имеет смысла, но, надеюсь, моя разработка помогла вам понять.

+0

Так, чтобы уточнить, значение 'ptr' - это некоторый 8-байтовый адрес памяти. Я пишу в адрес памяти в местоположении 'ptr', а затем записываю в адрес памяти в месте расположения' ptr + sizeof (char *) '. – kzs

+0

@kzs в этом случае 'ptr' всегда следует интерпретировать как указатель на 8-байтовый адрес памяти, а не как указатель на' char'. – JAB

+0

Важно признать, что в каждом случае символ * 'char' * по указанному адресу обновляется значением указателя. Это задание почти наверняка переполняется (без изменения любого другого, кроме указанного байта). В любом случае результат не может быть тем, что было предназначено. –

3

*((char *)ptr) = prevPtr; так же, как:

char *temp = ptr; 
*temp = prevPtr; 

Это плохо формируется, потому что вы пытаетесь присвоить указатель на символ.

*(*(char **)(ptr) + sizeof(char*)) = nextPtr; так же, как:

char **t1 = ptr; 
char *t2 = *t1; 
t2[4] = nextPtr; // assuming 4-byte pointers 

Это также плохо формируется, как он присваивает указатель на символ.

Вы должны получить сообщения об ошибках об этой ошибке в обоих случаях (не следует вводить в заблуждение gcc, который, как правило, говорит «предупреждение» о несовместимых преобразованиях типов, даже если код на самом деле является ошибкой).


void * - общий тип указателя. Он используется там, где у вас есть интерфейс, который используется для транспортировки различных типов указателей. Тем не менее, вам разрешено конвертировать void * обратно в тип указателя, из которого он пришел. Любое другое преобразование указателя не гарантировано работает, и это особенно вероятно не будет делать то, что было предназначено, если вы конвертируете в тип указателя с другим числом *.

Чтобы решить, что делать в коде, вам нужно будет вернуться назад и посмотреть, как было назначено ptr.

+0

Мое предположение о том, почему GCC генерирует предупреждения, а не ошибки, из-за устаревшего кода, который использует такие несовместимые в настоящее время, но, возможно, однократно допустимые назначения (и в некоторых случаях такие присвоения могут быть действительными, если значения фактически находятся в правильном диапазоне , но присвоение адреса памяти символу, вероятно, не очень важно, кроме как на машинах с малым объемом памяти или многобайтовой 'char', и в любом случае явный приведение, вероятно, будет наиболее подходящим для современных стандартов). – JAB

+1

@JAB, даже если вы принимаете бессловесное назначение значения указателя на значение l типа 'char' - это не так необоснованно, поскольку указатели могут быть преобразованы в целые типы - результат данного кода будет по-прежнему ошибочны из-за того, что 'char' вряд ли будет достаточно широким, чтобы представить преобразованное значение указателя. Я не вижу, чтобы результат мог быть фактическим намерением автора кода. –

+0

@JohnBollinger Действительно, следовательно, последняя часть моего скобочного утверждения. Для M.M: «вам разрешено преобразовать void * обратно в тип указателя, который он пришел из« Что произойдет, если исходный тип был «void *», например, для возвращаемого значения 'malloc()'? – JAB

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