2015-12-15 3 views
8

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

В 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(), должен иметь возможность объявлять структуру в стеке, но скрывать ее определение.

+4

Более распространенный выбор - использовать что-то вроде 'foo_create' и' foo_destroy', что означает, что вы не раскрываете * каких-либо * деталей своей структуры и не можете делать более сложные вещи, например, хранить внутренние указатели 'malloc''d , Там очень мало ситуаций, когда вы * на самом деле * хотите использовать 'alloca', кроме, возможно, встроенных систем, где' malloc' и друзья супер ограничены. –

+2

Если структура непрозрачна, было бы плохой дизайн, чтобы клиентский код нуждался в распределении или объявлении любых переменных этого типа, как показано в примере. Все экземпляры структуры должны поступать из самой библиотеки. – kaylum

+3

'VLA []' разрешено? (C99)? Объявление символьного массива 'foo_size' (с использованием' alignas') может работать. Тем не менее, в gerneral, соглашайтесь с @kaylum – chux

ответ

3

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

Альтернатива 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; 

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

+0

Эта идея интересна, но вряд ли квалифицируется как хорошая практика. – chqrlie

+0

@chqrlie Должны ли вы подробно рассказать о нехорошей практике, возможно, мы сможем их решить. «вряд ли квалифицируется как хорошая практика». сродни OP, говорящий, что «код не работает». – chux

+0

что такое * хорошая практика *, вероятно, основанное на мнениях. Если цена за то, что она скрывает реализацию, трудно понять, как использовать VLA, я лично считаю, что это не стоит проблем. Что вы предлагаете - это взломать выделение стека, вы собираетесь скрыть его макросом? Может ли это считаться хорошей практикой? – chqrlie

2

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

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

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

+0

Исправлен код. Как я сказал, если используется 'foo_create()', тогда невозможно выделить память в стеке, что иногда желательно. –

+0

@ АлександрСоловец На практике это обычно нежелательно. Разрешение объектов, которые будут выделены и оторваны в стеке, невелики; это означает, что вы не можете очистить экземпляр. – duskwuff

+0

Если вы хотите использовать 'alloca', вы можете использовать функцию' foo_init' для ее инициализации. Как сказано в комментариях к chux, вам также понадобится выравнивание структуры, поэтому пользователю намного проще называть 'foo_create' и не беспокоиться об этом. – Kevin