Вставить между указателем struct в C
Пожалуйста, рассмотрите следующий код.
typedef struct{
int field_1;
int field_2;
int field_3;
int field_4;
uint8_t* data;
uint32_t data_size;
} my_struct;
void ext_function(inalterable_my_struct* ims, ...);
Я хочу разрешить ext_function
(написанный третьей стороной) изменять только field_3
и field_4
в my_struct
. Поэтому я делаю следующее:
typedef struct{
const int field_1;
const int field_2;
int field_3;
int field_4;
const uint8_t* data;
const uint32_t data_size;
} inalterable_my_struct;
void ext_function(inalterable_my_struct* ims, ...);
Безопасно ли указывать указатели между my_struct
и inalterable_my_struct
перед вызовом ext_function
(как показано ниже)?
void call_ext_function(my_struct* ms){
inalterable_my_struct* ims = (inalterable_my_struct*)ms;
ext_function(ims, ...);
}
Ответы
Ответ 1
Я не думаю, что это хорошая идея.
Вызываемая функция всегда может отбрасывать любые const
: ness и изменять данные, если она хочет.
Если вы можете контролировать контрольные точки, было бы лучше создать копию и вызвать функцию с указателем на копию, а затем скопировать два поля, о которых вы заботитесь:
void call_ext_function(my_struct* ms)
{
my_struct tmp = *ms;
ext_function(&tmp, ...);
ms->field_3 = tmp.field_3;
ms->field_4 = tmp.field_4;
}
намного чище, и если вы не делаете это тысячи раз в секунду, то штраф за производительность должен быть незначительным.
Возможно, вам придется подделать данные на основе указателей, если функция коснется его.
Ответ 2
В соответствии со стандартом C99 два struct
не будут иметь совместимые типы, даже если их объявления были идентичны. Из раздела 6.7.7.5:
ПРИМЕР 2 После деклараций
typedef struct s1 { int x; } t1, *tp1;
typedef struct s2 { int x; } t2, *tp2;
type t1
и тип, на который указывает tp1
, совместимы. Тип t1
также совместим с типом struct s1
, но не совместим с типами struct s2
, t2
, с типом, на который указывает tp2
или int
.
Кроме того, два типа с разными квалификаторами не считаются совместимыми:
Для двух квалифицированных типов, которые должны быть совместимы, оба должны иметь идентичную версию совместимого типа; порядок классификаторов типов в списке спецификаторов или классификаторов не влияет на указанный тип.
Более чистый подход состоял бы в том, чтобы полностью скрыть ваш struct
, замените его неясным дескриптором (a typedef
поверх void*
) и предоставите функции для управления элементами struct
. Таким образом, вы сохранили бы полный контроль над структурой вашего struct
: вы могли бы переименовать свои поля по своему усмотрению, изменить макет столько и сколько захотите, изменить основные типы полей и сделать другие вещи что вы обычно избегаете, когда ваш макет struct
известен вашим клиентам.
Ответ 3
Я не думаю, что это хорошая идея, потому что трудно отследить, была ли построена структура или нет (особенно, если код большой). Кроме того, включение его в const не гарантирует, что он не будет передан в non-const structure
позже.
Решение, предоставляемое разматыванием, является очень хорошим. Альтернативным (и более очевидным) решением было бы разбить структуру на две более мелкие части.
typedef struct{
const int field_1;
const int field_2;
const uint8_t* data;
const uint32_t data_size;
} inalterable_my_struct;
typedef struct{
int field_3;
int field_4;
} my_struct;
void ext_function(const inalterable_my_struct* const ims, my_struct* ms ...);
Я сделал указатель также постоянным в вышеуказанном вызове, но это необязательно.
Ответ 4
Вероятно, это будет работать на большинстве участников, хотя стандарт ничего не говорит об этом. Возможно, вы даже можете сделать что-то более портативное с союзом, если вам действительно нужно. Кроме того ничего не изменит.
Вот почему это ничего не изменит:
$ cat foo.c
struct foo {
const int a;
int b;
};
void
foo(struct foo *foo)
{
foo->a = 1;
}
$ cc -c foo.c
foo.c: In function ‘foo’:
foo.c:9: error: assignment of read-only member ‘a’
$ cc -Dconst= -c foo.c
$
Ответ 5
Запись в члены, которые раньше были const
, может быть небезопасной. Возможно, они были помещены в постоянное запоминающее устройство компилятором/компоновщиком и операционной системой. См. Как доступна память только для чтения в C?.