В чем разница между const void *
и void *
? При каких обстоятельствах можно указывать указатель void на указатель const void
?
C Вопрос: (const void *) vs (void *)
Ответ 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 лет.