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


11

私の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の開発者の間には、ある種の未記述の標準がありますか?


3
単なる観察上の注意として、ほとんどのオペレーティングシステムはオプション3を使用します。バッファポインタと容量を示します。呼び出し先はバッファを満たし、バッファが不十分な場合は文字列実際の長さも返します。例:sysctlbynameOS XおよびiOS
rwong

回答:


11

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

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

char[128] buffer;
mytype_to_string(mt, buffer, 128);

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

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


ありがとう!私もAlternative 3が一番好きだと思います。ただし、次のようなことができるようにしたいprintf("MyType: %s\n", mytype_to_string( mt, buf, sizeof(buf));ので、使用した長さではなく、文字列へのポインターを返します。動的ライブラリのコメントは本当に重要です。
–ØysteinSchønning-Johansen、2015

これsizeof(buffer) - 1\0ターミネータを満足させるためではないでしょうか?
Michael-O

1
@ Michael-Oいいえバッファーサイズにnullの用語が含まれていないため、入力できる最大文字列は、渡されたサイズよりも1つ小さくなります。これは、標準ライブラリの安全な文字列がsnprintf使用するように機能するパターンです。
ラチェットフリーク

@ratchetfreak説明をありがとう。その知恵で答えを広げるといいでしょう。
Michael-O

0

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

可能な場合mytypeは、と同じ.hファイルに必要な最大サイズも指定しますmytype_to_string()

#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で推奨されるイディオムであることを思い出します。その参照を見つける必要があります...


0

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

たとえば、strncpy

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

同じパラダイムに従うことで、新しい開発者(または将来の自分)が関数を使用する必要がある場合の認知的負荷を軽減できます。

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

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

-2

あなたがしていることを提案しているのが悪いコードのにおいであるという事実に加えて、私にはオルタナティブ3が最もよく聞こえます。@ gnasher729のように、間違った言語を使用していると思います。


コードのにおいを正確にどう思いますか?詳しく説明してください。
ハルク

-3

正直に言うと、文字列を返すのが複雑で作業集約的でエラーが発生しやすい操作ではない別の言語に切り替えることができます。

コードの99%をそのままにしておくことができるC ++またはObjective-Cを検討できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.