2017-04-24 8 views
1

を使用してstrlenを私は彼らに何が間違っているか知りたいでしょうか?Cは、と私は、ポインタを使用してstrlen関数の標準的な実装を見ているポインタ

  1. これは、本のやり方と多少似ています。これは間違っていますか?

    int strlen(char * s) { 
        char *p = s; 
        while (*p) 
        p++; 
        return p-s; 
    } 
    
  2. I私は空の文字列を渡しますが、それでも私に0を与えた場合、pは、事前増分であるので、それは、ちょっと混乱間違っているだろうけれども:(そして今その私に5返す)

    int strlen(char * s) { 
        char *p = s; 
        while (*++p) 
        ; 
        return p-s; 
    } 
    
  3. をこれを見て、ポストはインクリメントして+1を返します。

    int strlen(char * s) { 
        char *p = s; 
        while (*p++) 
        ; 
        return p-s; 
    } 
    

答えて

1

1)は私には正常に見えます。私は個人的には '\ 0'との明示的な比較を好むので、文脈から明らかでない状況ではpをNULLポインタと比較することを意味していないことは明らかです。

2)プログラムが実行されると、スタックと呼ばれるメモリ領域が初期化されません。ローカル変数はそこに住んでいます。あなたがあなたのプログラムを書いたやり方は、constにした場合、またはmallocを使った場合には、をスタックに入れます。 *pを見ると何が起こるのでしょうか?文字列が長さ0の場合、これはchar p[1] = {0}と同じです。プリインクリメントは、\0の直後のバイトを調べるため、未定義のメモリが表示されます。ここでドラゴンズ!

3)私はそこに質問があるとは思っていません:)あなたが見るように、それは常に正しい答えより1つ多く返します。

補遺:また、あなたはこのスタイルを好む場合は、forループ使用して、これを書くことができます。また

size_t strlen(char * s) { 
    char *p = s; 
    for (; *p != '\0'; p++) {} 
    return p - s; 
} 

または(よりエラーが発生しやすい-LY)

size_t strlen(char * s) { 
    char *p = s; 
    for (; *p != '\0'; p++); 
    return p - s; 
} 

を、strlenをすることができます負の数を返さないので、符号なしの値を使用する必要があります。 size_tはさらに優れています。

+0

これは私のメインがどのように見えているのか、今私に5を与えています。 main(){ char A [] = ""; printf( "%d"、strlen(A)); } – user7703770

+0

ああ...わかりました。何が起きているのか確認する前にポインタを増やしています。あなたがチェックするのは、スタック内のデータです。それは定義されていないので、\ 0を打つまで空の文字列の値を得ることができます。 – lungj

0

バージョン1は正常です - while (*p != '\0')while (*p != 0)に相当します。これはwhile (*p)に相当します。 のみ*p場合は0(IOW、あなたは文字列の末尾じゃない)でない場合は、元のコードとバージョン1では

は、ポインタ pが進んでいます。

バージョン2及び3あらかじめpかかわらず*p0であるか否かの*p++は、pを指し、という副作用として評価されます。は、pとなります。 *++pは、と評価され、の文字はpを指し、副作用としてpとなります。したがって、バージョン2と3は常に文字列の末尾より先にpを進めるため、値がオフになっています。

-1

strlenの機能を比較するときに実行する1つの問題は、実際の文字列の長い文字列のstrlenと比較して、パフォーマンスが低下しますか?どうして? strlenは、文字列の終わりを検索する際に1回の繰り返しで1バイト以上処理します。より効率的な交換をどのように実装できますか?

それほど難しいことではありません。基本的なアプローチは、反復ごとに4バイトを調べ、その4バイト内のどこにヌルバイトがあるかに基づいてリターンを調整することです。あなたは(配列インデックスを使用して)次のような何かができる:

size_t strsz_idx (const char *s) { 
    size_t len = 0; 
    for(;;) { 
     if (s[0] == 0) return len; 
     if (s[1] == 0) return len + 1; 
     if (s[2] == 0) return len + 2; 
     if (s[3] == 0) return len + 3; 
     s += 4, len += 4; 
    } 
} 

あなたはポインタとマスクを使用して、まったく同じことを行うことができます。

size_t strsz (const char *s) { 
    size_t len = 0; 
    for(;;) { 
     unsigned x = *(unsigned*)s; 
     if((x & 0xff) == 0) return len; 
     if((x & 0xff00) == 0) return len + 1; 
     if((x & 0xff0000) == 0) return len + 2; 
     if((x & 0xff000000) == 0) return len + 3; 
     s += 4, len += 4; 
    } 
} 

いずれかの方法で、あなたは4バイトの比較を見つけるだろう各反復はstrlenと同等のパフォーマンスを提供します。

+0

最初のループは、その時点で4バイトをチェックしません。それはより複雑なループを使用し、元のものより多くの分岐と追加があります。 2番目のループは悪化します。**はエイリアシングルール**に違反し、*未定義の動作*になります。また、この答えは質問に答えません。 Downvoted。 – user694733

+0

*厳密なエイリアシング*ルールに関する*少し*知識より悪いことはありません。 'to-and-from char *'のキャストに関するルールは何ですか?そこに違反はありません、それがあなたの鼎の基礎であれば、あなたは100%間違っています。なぜ '-Wall -Wextra -pedantic'を使ってコードをコンパイルして調べないのですか?私が間違っているときにはダウンボントを気にしませんが、どちらかが正しいと思っても私は期待しません。 –

+0

任意のオブジェクト型のアドレスを 'char *'にキャスティングすることができますが、これは標準的な例外です。しかし、元の型と最終型が一致しない限り( 'unsigned * - > char * - > unsigned *'は正当です、 'char [] - > unsigned *'はそうではありません)、あなたは逆と逆参照で同じことをすることはできません。 N1570 6.5 p7は、エイリアシングの可能なタイプを列挙していますが、この場合は6つのタイプのどれも適用されません(最後のものではありません)。テストでは、未定義の動作があると思われる場合があるため、C言語で何かが合法であるかどうかを確認するだけでは不十分です。 '*(unsigned *)s'はこれの古典的な例です。 – user694733

関連する問題