2015-12-15 1 views
8

私は、構造体の定義が隠れていると、構造体のメンバーに直接アクセスすることができないコンパイラの助けを借りて、コードを安全にすると思います。欠点は、構造体のサイズがわからないためにスタック上の構造体型の変数を宣言できないことです。一方、時にはmalloc()を使用しないことが望ましい場合もあります。この機能はPOSIXに準拠していませんが、これは(部分的に成功して)alloca(3)で解決できます。これはすべての主要なlibc実装に存在します。この小さな賛否両論を考えると、そのようなデザインは一般的に良いと思われますか?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'のポインタ。おそらく '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; 

のVLAが/利用可能な所望されない場合、必要な限り、少なくともことは確かである#define foo_N 100ような定数としてサイズを宣言する。

+0

このアイデアは面白いですが、良い習慣としての資格はほとんどありません。 – chqrlie

+0

@chqrlie不適切なプラクティスの問題を詳しく説明してください。 「習慣としてはほとんど成立しない」 "コードがうまくいかない"というOPに似ています。 – chux

+0

*良い習慣は*おそらく意見に基づいています。実装を隠すために支払うべき価格がVLAの取り扱いを読みにくい場合、私は個人的にはそれが問題の価値がないと思っています。あなたが提案しているのは、スタック割り当てを許可するためのハックです。マクロでそれを隠すつもりですか?それは*良い習慣*と考えられますか? – chqrlie

2

ファンクションbarは、未定義の動作を呼び出します。fooが指す構造体は初期化されません。

構造の詳細を非表示にする場合は、foo_create()を割り当てて初期化し、foo_finalizeはリソースを解放して解放します。

提案するものは動作させることができますが、エラーが発生しやすく、一般的な解決策ではありません。

+0

コードを修正しました。私が 'foo_create()'を使用していると述べたように、時には望ましいスタックにメモリを割り当てることは不可能です。 –

+0

@AlexanderSolovets実際には、これは通常は望ましくありません。スタックにオブジェクトを割り当てたり切り離すことはできません。インスタンスのクリーンアップを実行できないことを意味します。 – duskwuff

+0

'alloca'を使い続けたい場合は、' foo_init'関数を使ってそれを初期化することができます。コメントで述べたchuxのように、構造体の整列も必要なので、ユーザが 'foo_create'を呼び出すのはずっと簡単ですし、それについて心配する必要はありません。 – Kevin

関連する問題