web-dev-qa-db-ja.com

文字列を返す関数、良いスタイル?

私のCプログラムでは、ADTの文字列表現を作成する方法が必要になることがよくあります。文字列を画面に出力する必要がない場合でも、このようなデバッグ方法があると便利です。そのため、この種の機能がよく出てきます。

char * mytype_to_string( const mytype_t *t );

文字列が返すメモリを処理するために、ここには(少なくとも)3つのオプションがあることを実感しています。

代替方法1:関数の静的文字配列に戻り文字列を格納します。文字列がすべての呼び出しで上書きされることを除いて、私はあまり考える必要はありません。これは、場合によっては問題になることがあります。

代替方法2:関数内でmallocを使用して、ヒープに文字列を割り当てます。バッファのサイズや上書きについて考える必要がないので、本当にすてきです。ただし、完了したら文字列をfree()することを覚えておく必要があります。また、解放できるように一時変数に割り当てる必要もあります。ヒープ割り当てはスタック割り当てよりもかなり遅いため、これがループで繰り返されるとボトルネックになります。

代替方法3:ポインタをバッファに渡し、呼び出し側にそのバッファを割り当てさせます。お気に入り:

char * mytype_to_string( const mytype_t *mt, char *buf, size_t buflen ); 

これにより、発信者により多くの労力がかかります。この代替案では、引数の順序に関する別のオプションが提供されることにも気づきました。最初と最後のどちらの引数が必要ですか? (実際には6つの可能性)

それで、どちらを選ぶべきですか?なんで? Cの開発者の間には、何らかの不書記の標準がありますか?

私が最もよく見た方法は2と3です。

ユーザー提供のバッファは実際には非常に簡単に使用できます。

_char[128] buffer;
mytype_to_string(mt, buffer, 128);
_

ただし、ほとんどの実装では、使用されたバッファーの量が返されます。

オプション2は速度が遅くなり、動的にリンクされたライブラリを使用する場合、それらが異なるランタイム(および異なるヒープ)を使用する可能性がある場合は危険です。したがって、別のライブラリに割り当てられたものを解放することはできません。次に、これを処理するためにfree_string(char*)関数が必要です。

11
ratchet freak

@ratchetfreakの優れた答えに加えて、代替案#3は標準のCライブラリ関数と同様のパラダイム/パターンに従うことを指摘します。

たとえば、strncpyです。

char * strncpy ( char * destination, const char * source, size_t num );

同じパラダイムに従うことは、新しい開発者(またはあなたの将来の自己さえ)があなたの関数を使用する必要があるときの認知負荷を減らすのに役立ちます。

投稿にあるものとの唯一の違いは、Cライブラリのdestination引数が引数リストの最初にリストされる傾向があることです。そう:

char * mytype_to_string( char *buf, const mytype_t *mt, size_t buflen ); 
0
John Go-Soco

#3の追加の設計アイデア

可能であれば、mytype_to_string()と同じ.hファイルでmytypeに必要な最大サイズも提供します。

#define MYTYPE_TO_STRING_SIZE 256

これで、ユーザーはそれに応じてコーディングできます。

char buf[MYTYPE_TO_STRING_SIZE];
puts(mytype_to_string(mt, buf, sizeof buf));

注文

配列のサイズは、最初はVLAタイプを考慮しています。

char * mytype_to_string( const mytype_t *mt, size_t bufsize, char *buf[bufsize]); 

1次元ではそれほど重要ではありませんが、2次元以上では役立ちます。

void matrix(size_t row, size_t col, double matrix[row][col]);

最初にサイズを読むことは次のCで推奨されるイディオムであることを思い出します。その参照を見つける必要があります...