Законно / безопасно отбрасывать `const` для объекта, выделенного в куче? - PullRequest
0 голосов
/ 01 декабря 2018

Мой пример использования следующий:

Я разрабатываю библиотеку, в которой некоторые загруженные плагины могут создавать объекты (выделяемые библиотекой с помощью malloc()), а некоторые другие плагины могут читать свойства этих объектов, ноне изменяйте их.

Для меня это случай использования не-1006 * API для стороны создания / записи и const API для стороны читателя, например:

// writer API
struct obj *obj_create(void);
void obj_set_some_property(struct obj *obj, int property);

// reader API
int obj_get_some_property(const struct obj *obj);

Библиотека преобразует struct obj * (созданный на стороне писателя) в const struct obj * (доступный на стороне читателя).

Моя проблема в том, что эти объекты также имеют счетчики ссылок, ичитатель может вызывать ваши типичные функции увеличения / уменьшения счетчика ссылок.Эти функции должны модифицировать объект.

Мой вопрос: в этом конкретном контексте безопасно ли для функций увеличения / уменьшения счетчика ссылок принимать const struct obj * и отбрасывать const внутренне?Обратите внимание, что функция уменьшения количества ссылок может также уничтожить (освободить) объект, если счет достигнет нуля.

Я знаю, что §6.7.3¶5 говорит:

Если попыткасделано для изменения объекта, определенного с помощью const-квалифицированного типа, с помощью lvalue с не-const-квалифицированным типом, поведение не определено .

Я просто могу 't выяснить, что означает , определенный с помощью константного типа .Применимо ли это, если мой объект выделен в куче?Я полностью понимаю, почему было бы UB сделать это с указателем литеральной строки (.rodata), например.Но что, если объект создан, чтобы быть не const в начале?

strchr() - это хорошо известный пример отбрасывания const: он принимает const char *и возвращает char *, что указывает на параметр const char *.Как это законно, учитывая §6.7.3¶5?

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

В этом контексте определено относится к программному оператору, определяющему переменную, например const struct obj x = {};.Это в отличие от оператора, который просто объявляет его, например const struct obj* x;.

. В C память, возвращаемая malloc, является неинициализированным хранилищем, в которое можно безопасно записать.Фактически, ваша библиотека должна была сделать это хотя бы один раз перед передачей структуры клиенту!

Теоретически, у вас может возникнуть проблема, если клиент каким-то образом объявит const obj x = OBJ_INITIALIZER;, а затем передаст его вашей библиотеке.,Ваш компилятор мог вставить это определение переменной на страницу памяти, доступную только для чтения, или мог бы слишком агрессивно оптимизировать, предполагая, что она никогда не будет изменена.Поэтому вам необходимо указать, что любая библиотечная функция, которая отбрасывает const внутренне, работает только на объектах своих собственных фабрик.

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

Вы также можете столкнуться с проблемами, если клиентский код использует изменяемые вами поля и если компилятор предполагает, чтофункция, которая принимает параметр const struct obj*, не может изменить его через этот параметр.Вы могли бы обойти это, возвращая ссылку, которая псевдоним объекта, который компилятор не будет считать тем же неизмененным объектом (или, я думаю, злоупотребляя volatile).

В C ++ вы бы имеливозможность объявления полей, которые необходимо изменить mutable.

0 голосов
/ 01 декабря 2018

Объекты, выделенные malloc, не имеют какого-либо типа, не говоря уже о константном.Их можно модифицировать независимо от того, указывали ли вы на объект с указателем на const на каком-то этапе.

«определено с помощью типа с константным типом» требует, чтобы у объекта было определение (malloc это вызов функции, а не определение).

...