C Вопрос: (const void *) против (void *)
В чем разница между const void *
а также void *
? При каких обстоятельствах пустой указатель может быть приведен к const void
указатель?
2 ответа
const void *
указывает на память, которая не должна быть изменена.
void *
(неконстантный) указывает на память, которая может быть изменена (но не через void *
; Вы должны были бы бросить это сначала).
Когда вы используете memmove()
адрес источника приведен к const void *
:
void *memmove(void *dst, const void *src, size_t nbytes);
Это иллюстрация, когда пустой указатель может быть приведен к постоянному пустому указателю. По сути, вы можете сделать это (преобразовать в константу) в любое время, когда вы знаете, что не собираетесь изменять память, на которую указывает указатель. Это относится к любому указателю, а не только к пустым указателям.
Преобразование другим способом (из постоянного указателя в непостоянный указатель) является гораздо более опасным упражнением. Нет никакой гарантии, что память, на которую указывает память, является изменяемой; например, строковый литерал может быть сохранен в постоянной (постоянной) памяти, и если вы потеряете постоянство при преобразовании и попытаетесь изменить строку, вы, скорее всего, получите ошибку сегментации или ее эквивалент - ваша программа внезапно остановится и не под вашим контролем. Это не хорошая вещь. Поэтому не меняйте указатели с константных на непостоянные, не будучи уверенным, что на самом деле все в порядке, чтобы лгать вашему компилятору. Имейте в виду, что компиляторам не нравится, когда им лгут, и они могут получить ответ, как правило, в самый неудобный момент (например, при демонстрации вашей программы важному потенциальному клиенту перед вашим боссом, боссом вашего босса и боссом босса вашего босса).
Совершенно разумно бросить 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
указатель на неconst
Во-первых, вы должны продублировать указанное значение в изменяемой части памяти и передать свой дубликат функции, а не оригиналу. Если это звучит как головная боль, это потому, что это так. Если вы окажетесь в такой ситуации, значит, вы, вероятно, сделали что-то не так.
Концептуально, если переменная содержит "значение", то, скорее всего, const
указатель. Если вместо этого он содержит "буфер", то это неconst
указатель.
Указатели в сигнатурах вашей функции всегда должны быть объявлены const
если вы не собираетесь писать в эту память. Следование этому правилу поможет вам избежать катастрофических проблем в логике вашей программы.
Я не понимал это простое правило, пока не программировал 6 лет.