Подтвердить что ты не робот

C Вопрос: (const void *) vs (void *)

В чем разница между const void * и void *? При каких обстоятельствах можно указывать указатель void на указатель const void?

4b9b3361

Ответ 1

A const void * указывает на память, которая не должна быть изменена.

A void * (не const) указывает на память, которая может быть изменена (но не через void *; вам придется сначала ее перенести).

Когда вы используете memmove(), адрес источника преобразуется в const void *:

void *memmove(void *dst, const void *src, size_t nbytes);

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

Преобразование другого пути (от постоянного указателя на непостоянный указатель) - гораздо более опасное упражнение. Там нет гарантии, что память, указанная на самом деле, может быть изменена; например, строковый литерал может быть сохранен в readonly (постоянной) памяти, и если вы потеряете constness с литой и попытаетесь изменить строку, вы, скорее всего, получите ошибку сегментации или ее эквивалент - ваша программа внезапно прекратится и не под вашим контролем. Это не хорошая вещь. Таким образом, не изменяйте указатели от константы до непостоянной, не будучи уверенным, что на самом деле это нормально, чтобы лгать вашему компилятору. Имейте в виду, что компиляторам не нравится, когда им лгут и могут получить свою собственную спину, как правило, в самый неудобный момент (например, когда вы демонстрируете свою программу важному потенциальному клиенту перед вашим боссом, боссом босса и боссом босса босса).

Ответ 2

Совершенно разумно отбрасывать void * в const void *, и компилятор должен делать это неявно за кулисами, не задумываясь о своей стороне, но наоборот - опасно и его следует избегать.

Помните, что если функция принимает указатель const, то вы можете передать ей значение const или не const. Говорить, что вы берете указатель const, просто объявляете, что память не будет изменена вашей функцией.

Пример: (обратите внимание, что строки, помеченные DANGER, должны вызывать ошибку компилятора)

const void *p_const;
void *p_buffer;

// const pointers are allowed to hold static memory
p_const = "Foo"; // allowed
p_buffer = "Foo"; // DANGER!!!

// casting to const is implicit
p_const = malloc(LEN); // allowed - implicit cast
p_buffer = malloc(LEN); // allowed

// casting to const implicit again
write(STDOUT, p_const, LEN); // allowed
write(STDOUT, p_buffer, LEN); // also allowed - implicit cast

// const memory cannot be used where mutable memory is expected
read(0, p_buffer, LEN); // allowed
read(0, p_const, LEN); // DANGER!!

// To make the above more obivous, we'll skip the intermediate variable
// and place instead what it holds
read(0, malloc(LEN), LEN); // allowed - useless but at least no crashes
read(0, "foo", 4); // DANGER!!!

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

Другой пример:

void do_something(const void* ptr, int length);

// Since the signature is a const pointer, I know I can call it like this:
do_something("foo",4);

И наоборот, функция вызывает не постоянный указатель, тогда я должен разрешить это:

void do_something(void* ptr, int length);

// This tells me that the function may overwrite my value.
// The safe solution therefore looks more like this:

char *myptr = char[4];
memcpy(myptr,"foo",4);    
do_something(myptr,4);

Аналогично, если вы когда-нибудь окажетесь в ситуации, когда вам нужно указать указатель const на не-t22 > , вы должны дублировать значение point-to в изменяемую часть памяти и передать свой дубликат к функции, а не оригиналу. Если это звучит как головная боль, это так и есть. Если вы окажетесь в этой ситуации, вы наверняка сделали что-то не так.

Концептуально, если переменная содержит "значение", то она скорее всего указатель const. Если вместо этого он содержит "буфер", то это указатель не const.

Указатели в ваших сигнатурах функций должны быть объявлены всегда const, если вы не собираетесь писать в эту память. Следуя этому правилу, вы сможете избежать катастрофических проблем в своей программной логике.

Я не понял этого простого правила, пока я не программировал в течение 6 лет.