4

誰かが私がこのプログラムの下のプログラムでこれらのコード行を理解するのを手伝ってくれるかもしれませんライターによると、それはhello worldの文字列を書きますそしてその中に文字列をworldhelloに逆にする関数があります、私の探求はこのコードが何をするのかです?

char * p_divs = divs; //what does divs do
    char tmp;
    while(tmp = *p_divs++)
        if (tmp == c) return 1

;

また、void関数のこのコード

*dest = '\0';//what does this pointer do?
    int source_len = strlen(source); //what is source
    if (source_len == 0) return;
    char * p_source = source + source_len - 1;
    char * p_dest = dest;
    while(p_source >= source){
        while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;

これがメインプログラムです

#include <stdio.h>
#include <string.h>

int inDiv(char c, char * divs){
    char * p_divs = divs;
    char tmp;
    while(tmp = *p_divs++)
        if (tmp == c) return 1;
    return 0;
}

void reverse(char * source, char * dest, char * divs){
    *dest = '\0';
    int source_len = strlen(source);
    if (source_len == 0) return;
    char * p_source = source + source_len - 1;
    char * p_dest = dest;
    while(p_source >= source){
        while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;
        if (p_source < source) break;
        char * w_end = p_source;
        while((p_source >= source) && (!inDiv(*p_source, divs))) p_source--;
        char * w_beg = p_source + 1;
        for(char * p = w_beg; p <= w_end; p++) *p_dest++ = *p;
        *p_dest++ = ' ';
    }
    *p_dest = '\0';
}

#define MAS_SIZE 100

int main(){
    char source[MAS_SIZE], dest[MAS_SIZE], divs[MAS_SIZE];
    printf("String          : "); gets(source);
    printf("Dividers        : "); gets(divs);
    reverse(source, dest, divs);
    printf("Reversed string : %s", dest);
    return 0;  
}
4

3 に答える 3

4

ここで、inDivを呼び出してc、文字列内の文字を検索できますdivs。次に例を示します。

inDiv('x', "is there an x character in here somewhere?') will return 1
inDiv('x', "ahhh... not this time') will return 0

それを介して作業する:

int inDiv(char c, char * divs)
{
    char * p_divs = divs;    // remember which character we're considering
    char tmp;
    while(tmp = *p_divs++)   // copy that character into tmp, and move p_divs to the next character
                             // but if tmp is then 0/false, break out of the while loop
         if (tmp == c) return 1;  // if tmp is the character we're searching for, return "1" meaning found
    return 0;   // must be here because tmp == 0 indicating end-of-string - return "0" meaning not-found
}

reverseコールサイトを見ることで、次のことを推測できます。

int main()
{
    char source[MAS_SIZE], dest[MAS_SIZE], divs[MAS_SIZE];
    printf("String          : ");
    gets(source);
    printf("Dividers        : ");
    gets(divs);
    reverse(source, dest, divs);
    printf("Reversed string : %s", dest);

gets()標準入力から文字配列に読み取るために呼び出されsourcedivs->これらの入力がに提供されていることがわかりますreverse()。方法destは印刷されます、それは明らかに。の文字列の反転の宛先であることを意味しますsource。この段階では、の関連性についての洞察はありませんdivs

ソースを見てみましょう...

void reverse(char * source, char * dest, char * divs)
{
    *dest = '\0'; //what does this pointer do?
    int source_len = strlen(source); //what is source
    if (source_len == 0) return;
    char* p_source = source + source_len - 1;
    char* p_dest = dest;
    while(p_source >= source)
    {
        while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;

ここで*dest = '\0'は、NUL文字を文字配列に書き込みます。これは文字dest列の終わりの位置をエンコードする通常の番兵の値です。最初の文字に入力*destすると、宛先をクリアする必要があります。source反転するテキスト入力は、その中の文字数にstrlen()設定されることがわかっています。source_len文字がない場合は、return実行する作業がなく、出力はすでにNULで終了しているためです。それ以外の場合は、新しいポインタp_sourceが作成され、source + source_len - 1->に初期化されます。これは、ソース内の最後の非NUL文字を指していることを意味します。 p_destデスティネーションバッファの先頭にあるNUL文字を指します。

次に、ループは次のように言います。-while (p_source >= source)これを行うには、最初は何でもするp_source必要があります-これは、最後の文字を指す>= sourceものとして意味があり、バッファ内の最初の文字アドレスです。比較は、それらが交差するまで、一方または両方をもう一方に向かって移動することを意味します-毎回いくつかの作業を行います。これにより、次のことが可能になります。p_sourcesource

while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;

これは今見たのと同じテストですが、今回はp_source文字列の先頭に向かって後方に移動しているだけですが、これinDiv(*p_source, divs)も当てはまります...つまり、の文字は文字列*p_source内の文字の1つですdivs。つまり、基本的には、文字列の先頭を通過するまで後方に移動します(ただし、Michael Burrがコメントで指摘しているように、このテストの動作は未定義であり、文字列がアドレス0に割り当てられた場合は実際には機能しない可能性があります-特定のデータセグメントに関連している場合でも、ポインタが0からFFFFFFFF hexのようなものに移動する可能性があるため(0未満のようには見えない)、または「除算」文字の1つではない文字が見つかるまで。

ここでは、コードが何をしているのかについての実際の洞察を得ることができます...入力を「単語」に分割し、入力内の文字のセットのいずれかでdivs区切ってから、スペース区切り文字を使用して逆の順序で宛先バッファーに書き込みます。それは私たち自身より少し進んでいます-しかしそれを追跡しましょう:

次の行は...

if (p_source < source) break;

...これは、ループがソース文字列の先頭を超えて終了した場合、すべてのループから抜け出すことを意味します(先を見据えると、コードはすでに生成された出力の最後に新しいNULを配置し、 -しかし、それは私たちが期待することですか?-「helloworld」の「hello」をバックスルーしていた場合、文字列の先頭に到達し、最後の「hello」単語をコピーせずにループを終了します。出力!出力は常に入力内のすべての単語(最初の単語を除く)が逆になります。これは作成者が説明した動作ではありません)。

さもないと:

char* w_end = p_source;  // remember where the non-divider character "word" ends

// move backwards until there are no more characters (p_source < source) or you find a non-divider character
while((p_source >= source) && (!inDiv(*p_source, divs))) p_source--;

// either way that loop exited, the "word" begins at p_source + 1
char * w_beg = p_source + 1;

// append the word between w_beg and w_end to the destination buffer
for(char* p = w_beg; p <= w_end; p++) *p_dest++ = *p;

// also add a space...
*p_dest++ = ' ';

これは入力内の「単語」ごとに発生し続け、最後の行で宛先にNULターミネータが追加されます。

*p_dest = '\0';

今、あなたは言った:

ライターによると、hello worldの文字列を書き込み、その中に文字列をworldhelloに反転する関数があります。

さて、入力「hello world」とスペースを含む仕切り文字(ただし、入力内の他の文字はありません)が与えられると、出力は「helloworld」になります(最後のスペースに注意してください)。

価値のあることですが、このコードはそれほど悪くはありません...入力の長さに関する仮定は危険であり、最初の単語が欠落していますが、ASCIIZバッファのC処理ではかなり正常です...。

**未定義の動作を修正する方法**

未定義の動作について-バッファの開始時にループが終了するようにループを変更するアドレスへの最小の変更。次の行で、ループが終了した理由を明示的に確認し、必要な動作を確認します。それは少し醜いでしょうが、ロケット科学ではありません。

于 2012-04-16T07:21:59.463 に答える
3
char * p_divs = divs; //what does divs do
char tmp;
while(tmp = *p_divs++)
    if (tmp == c) return 1

divschar配列(確かに文字列)へのポインタです。p_divs同じ文字列をポイントし、whileループ内で単一の文字が抽出されてに書き込まれるtmpと、ポインタがインクリメントされ、次のイテレータで次の文字が抽出されることを意味します。tmp一致する場合c、関数は戻ります。

編集:ポインタについてもっと学ぶ必要があります。ポインタ演算を見てください。

于 2012-04-16T05:41:45.680 に答える
1

コメントで指摘したように、Cはこのタスクに本当に理想的なツールではないと思います(選択肢があれば、考え直さずにC ++を使用します)。

しかし、コードがどれほど恐ろしいかについて話すつもりなら、反論は本当に正しかったと思います。もっと良いものを投稿する必要があります。しかし、問題のコメントとは反対に、これは優雅さ、簡潔さ、またはパフォーマンスの妥協を表すものではないと思います。

本当の議論に開かれているかもしれない唯一の部分は優雅さです、しかしこれはその点でほとんど本当の質問がないほど十分に単純でより簡単であると思います。それは明らかにより簡潔です-オリジナルとほぼ同じフォーマット規則を使用して、私のrev_wordsは17ではなく14行の長さです。ほとんどの人がそれらをフォーマットするので、私のものは17行で、彼は21です。

パフォーマンスに関しては、ほとんどの状況で2つはほぼ同等であると思います。Mineは、配列の先頭からの実行を回避します。これにより、わずかな時間を節約できます。オリジナルには早期終了が含まれているため、空の文字列を元に戻す時間を少し節約できます。しかし、どちらも重要ではないと思います。

ただし、もう1つのポイントがはるかに重要だと思います。私は、元の動作のように未定義の動作を使用/呼び出し/依存しないことを合理的に確信しています。他の分野で大きなアドバンテージが得られれば正当化されると考える人もいるかもしれませんが、他の分野では大まかに結びついている、または劣っていることを考えると、この場合、誰がそれを正当化すると考えるかは想像できません。 。

#include <string.h>
#include <stdlib.h>

#include <stdio.h>

int contains(char const *input, char val) {
    while (*input != val && *input != '\0')
        ++input;
    return *input == val;
}

void rev_words(char *dest, size_t max_len, char const *input, char const *delims) {
    char const *end = input + strlen(input);
    char const *start;
    char const *pos;

    do {
        for (; end>input && contains(delims, end[-1]); --end);
        for (start=end; start>input && !contains(delims,start[-1]); --start);
        for (pos=start; pos<end && max_len>1; --max_len) 
            *dest++=*pos++;
        if (max_len > 1) { --max_len; *dest++ = ' '; }
        end=start;
    } while (max_len > 1 && start > input);
    *dest++ = '\0';
}

int main(){ 
    char reversed[100];

    rev_words(reversed, sizeof(reversed), "This is an\tinput\nstring with\tseveral words in\n     it.", " \t\n.");
    printf("%s\n", reversed);
    return 0;
}

編集::

if (max_len > 1) { --max_len; *dest++ = ' '; }

本当にあるべきです:

if (max_len > 1 && end-start > 0) { --max_len; *dest++ = ' '; }

max_len <1を許可する場合は、次を変更できます。

*dest++ = '\0';

に:

if (max_len > 0) *dest++ = '\0';

バッファの長さが(おそらく敵対的な)ユーザーからの入力を介して何らかの形で設定できる場合、それはおそらく価値があります。多くの目的では、正のバッファサイズを要求するだけで十分です。

于 2012-04-16T09:43:12.557 に答える