Это хорошая практика, чтобы скрыть определение структуры в C?

Я думаю, что скрытое определение структуры делает код более безопасным, так как вы применяете с помощью компилятора, что ни один член структуры не может быть доступен напрямую. Недостатком является то, что пользователи не могут объявлять переменные типа структуры в стеке, потому что размер структуры неизвестен, хотя иногда желательно избегать использования malloc(), Это может быть (с частичным успехом) решено с alloca(3) который присутствует во всех основных реализациях libc, хотя эта функция не соответствует POSIX. Учитывая эти маленькие плюсы и минусы, может ли такой дизайн вообще считаться хорошим?

В lib.h:

struct foo;
extern size_t foo_size;
int foo_get_bar (struct foo *);

В lib.c:

struct foo {
  int bar;
};

size_t foo_size = sizeof foo;

int foo_get_bar (struct foo *foo)
{
  return foo->bar;
}

В example.c:

#include "lib.h"

int bar(void) {
  struct foo *foo = alloca (foo_size);
  foo_init (foo);
  return foo_get_bar (foo);
}

UPD: обновил вопрос, заявив, что идея использования alloca() должен иметь возможность объявлять структуру в стеке, скрывая ее определение.

2 ответа

Решение

Да, это хорошая практика, чтобы скрыть данные.

Альтернатива alloca(foo_size); должен объявить выровненный массив символов и выполнить преобразование указателя.

Хотя преобразование указателя не полностью переносимо.

Массив символов должен быть VLA, если размер должен быть переменной, а не постоянной времени компиляции.

extern size_t size;

struct sfoo;

#include <stddef.h>

int main(void) {
   unsigned char _Alignas (max_align_t) cptr[size];
   // or unsigned char _Alignas (_Complex  long double) cptr[size];  // some widest type
   struct sfoo *sfooptr = (struct sfoo *) cptr;

Если VLA не желательны / недоступны, объявите размер как константу, например #define foo_N 100 это наверняка будет как минимум столько, сколько нужно.

Функция bar вызывает неопределенное поведение: структура, на которую указывает foo неинициализирован.

Если вы собираетесь скрыть детали структуры, предоставьте foo_create() который выделяет один и инициализирует его и foo_finalize который освобождает любые ресурсы и освобождает его.

То, что вы предлагаете, можно заставить работать, но оно подвержено ошибкам и не является общим решением.

Другие вопросы по тегам