4

私が理解したように、正しいプログラミングスタイルは、別の関数から文字列 (char []) を取得する場合は、呼び出し元で char * を作成し、作成された文字列の長さと一緒に文字列フォーマット関数に渡すのが最善であることを示しています。私の場合、文字列整形関数は「getss」です。

void getss(char *ss, int& l)
{

    sprintf (ss,"aaaaaaaaaa%d",1);
    l=11;
}


int _tmain(int argc, _TCHAR* argv[])
{

    char *f = new char [1];
    int l =0;
    getss(f,l);

    cout<<f;
    char d[50] ;
    cin>> d;
    return 0;
}

「getss」は、文字列をフォーマットして ss* に返します。gets は、呼び出し元によって作成された外部の文字列の長さを取得することは許可されていないと思いました。私の理解では、発信者は変数「l」で長さを伝え、「getcc」はバッファが完全に満たされていない場合に長さを返しますが、発信者によって定義された配列範囲の外に出ることは許可されていません。

しかし、実際には、呼び出し元によって作成されたバッファーのサイズはそれほど重要ではないことがわかりました。サイズ 1 を作成し、gets が 11 文字の長さでいっぱいになる場合は問題ありません。出力では、「getss」が埋めたすべての文字を取得します。

それで、長さ変数を渡す理由は何ですか-常にゼロで終了する文字列を取得し、それに応じて末尾を見つけます。

getss で拡張できる場合、指定された長さのバッファを作成する理由は何ですか?

実世界でどのように行われるか - 別の関数から文字列を取得するには?

4

7 に答える 7

2

実際には、呼び出し元はバッファーを割り当て、内部に収まる文字列の最大サイズを知っている呼び出し元です。そのサイズを関数に渡します。関数は、渡されたバッファーのオーバーフローを回避するためにそれを使用する必要があります。

あなたの例では、それは:ではなくsnprintf()を呼び出すことを意味しますsprintf()

void getss(char *ss, int& l)
{
    l = snprintf(ss, l, "aaaaaaaaaa%d", 1);
}

もちろん、C ++では、std :: stringのインスタンスを返すだけでよいので、これはほとんどCのパラダイムです。Cは参照をサポートしていないため、関数は通常、文字列の長さを返します。

int getss(char *buffer, size_t bufsize)
{
    return snprintf(buffer, bufsize, "aaaaaaaaaa%d", 1);
}
于 2012-07-20T10:32:43.827 に答える
1

現実の世界では、C++ではstd::stringオブジェクトとstd::stringstream

char *f = new char [1];

sprintf (ss,"aaaaaaaaaa%d",1);

こんにちは、バッファオーバーフロー!snprintfCの代わりに使用sprintfし、C++でC++機能を使用します。

于 2012-07-20T10:31:16.883 に答える
1

私の理解では、呼び出し元は変数「l」で長さを伝え、「getcc」はバッファーが完全に満たされていない場合に長さを返しますが、呼び出し元が定義した配列範囲外に出ることは許可されていません。

これはスポットです!

しかし、現実には、呼び出し元が作成したバッファーのサイズはそれほど重要ではないことがわかりました。サイズ1を作成し、11文字の長さの塗りつぶしを取得する場合は問題ありません。出力では、「getss」が入力したすべての文字を取得します。

これは絶対に間違っています。未定義動作を呼び出しても、クラッシュは発生しませんでした。valgrindなどのメモリチェッカーは、この動作をエラーとして報告します。

では、長さ変数を渡す理由は何ですか。

長さは、この種の未定義の動作を回避するためにあります。返される文字列の長さがわからない場合、これはかなり苛立たしいことだと理解していますが、これが文字列の所有権の問題を引き起こさない唯一の安全な方法です。

1つの代替方法は、戻り値を動的に割り当てることです。これにより、任意の長さの文字列を返すことができますが、呼び出し元は戻り値を解放する責任があります。さまざまな場所mallocで発生するため、これは読者にとってあまり直感的ではありません。free

C ++での答えはまったく異なり、はるかに優れていますstd::string。任意の長さの文字列を表す標準ライブラリのクラスであるを使用します。このクラスのオブジェクトは、文字列に割り当てられたメモリを管理し、free手動で呼び出す必要をなくします。

于 2012-07-20T10:34:30.763 に答える
1

この場合、メモリ内の「char*」の後に「重要な」他のデータがないことは単に幸運です。C ランタイムは、常にこの種の違反を確実に検出するとは限りません。それにもかかわらず、ここでメモリを台無しにしており、プログラムはいつでもクラッシュする傾向があります。

それとは別に、生の「char*」ポインターを使用することは、「最新の」C++ コードでは実際に行うべきではありません。

代わりに STL クラス (std::string、std::wstring) を使用してください。そうすれば、このようなメモリの問題を気にする必要はありません。

于 2012-07-20T10:35:13.423 に答える
1

あなたは幸運だっただけです。Sprintf() は (静的に割り当てられた) ストレージを拡張できません。少なくとも長さ + 1 要素の char 配列を渡さない限り、プログラムがクラッシュすることが予想されます。

于 2012-07-20T10:30:35.787 に答える
1

cppの場合、おそらく a のスマート ポインターshared_ptrを検討してください。これにより、メモリの解放が処理されますnew。スペースの割り当てnewは削除で解除する必要があります。そうしないと、プログラムが終了するまで割り当てられます。これは悪いことです。タブを閉じるときに、ブラウザがタブに使用するメモリを解放しないと想像してください。

文字列の特別なケースでは、OPが言ったことをお勧めします。文字列を使用してください。Cpp11 では、これは移動され (コピーされません)、使用する必要newがなく、心配する必要はありませんdelete

std::string myFunc() {
    std::string str
    //work with str
    return str
}
于 2012-07-20T10:43:58.227 に答える
1

C++ では、文字列を作成する必要はありませ。パーツを分けて出力するだけ

std::cout << "aaaaaaaaaa" << 1;

または、文字列として保存する場合

std::string f = "aaaaaaaaaa" + std::to_string(1);

(呼び出しによるイベントto_stringは、定数値の場合は少しばかげています)。

于 2012-07-20T11:00:44.700 に答える