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

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

31 голосов
/ 05 апреля 2011

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

Ответы [ 2 ]

31 голосов
/ 05 апреля 2011

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

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

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

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

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

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

7 голосов
/ 05 апреля 2011

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

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

Пример: (обратите внимание, что строки, помеченные ОПАСНОСТЬ должны выдавать ошибку компилятора)

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 лет.

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