web-dev-qa-db-ja.com

Cのポインターを使用したforループ

forループでポインターが何をするのかわかりません。 *p次のループで実行しますか?

char str[128] = "Some Text";
char *p;

for (p = str; *p /*what does this mean?*/; p++)
{
    // Code
}

私は残りを理解していますが、なぜそうではない*p like p > 3またはそのようなもの?
なぜそれだけですか?
なぜそのように書かれているのですか?

40
MeChris

forループの条件などのブールコンテキストでは、Cの各式はtrue(非ゼロ)またはfalse(ゼロ)に評価されます。

forループは、ストリングの終わりに達したときに終了する必要があります。

Cでは、各文字列は文字'\0'で終了します。これは実際には0です。したがって、forループが文字列の終わりに達すると、*p'\0'に評価され、これは0になり、falseに評価され、forを終了しますループ。

53
errikos

Forループは、ステートメント内の2つの;の間にあるものがゼロ(false)である場合に終了します。 *pはpを逆参照し、charpが指す値を返します。 デニスリッチーによれば、「Cは文字列を通常はマーカーで終わる文字の配列として扱います」。そのマーカーは、ゼロの(ASCII)値を持つヌル文字です。したがって、これはforループです。

for (p = str; *p; p++)

これらと同等です

for (p = str; *p != '\0'; p++)
for (p = str; *p != 0; p++)
for (p = str; p[0] != '\0'; p++)

ヌル終了文字の別の名前は、sentinelまたはDonald Knuth "dummy value"(コンピュータープログラミングの技術、第1巻)。 str文字列、各文字のインデックス(先頭からのオフセット)、および各インデックスの値の図は次のとおりです。

enter image description here

完全を期すため、またここでのコメントでのリクエストの後に、デバッガーはstrが占有するメモリブロックで何を見るかを示します。

0x00007fffffffe6a0:
  0x53 0x6f 0x6d 0x65 0x20 0x54 0x65 0x78 0x74 0x00 0x00 0x00 0x00 0x00 0x00 0x00
     S    o    m    e         T    e    x    t
  1. 最初の行の16進値は、このメモリブロックのアドレス(64ビット)です。ここで、pがforループの開始点を指します。
  2. 2行目には、文字列の文字の16進値が表示されます。 ASCII table here 。を見ることができます。文字列の最後の文字はtで、16進値は0x74です。文字列のヌル文字0x00。その後、デバッグモードでビルドし、コンパイラがゼロで初期化されたため、さらにいくつかのヌル文字が表示されます。
  3. 3行目に、参照用に文字列の文字を追加しました

現在、Cのポインターを使用して急な学習曲線を描いていますが、最終的には "I C the point"と言うことができます。

31

これはこのように書き直すことができます

for (p = str; *p != '\0'; p++)
{
    // Code
}

Cでは、文字列は常にヌル文字で終了する必要があります。これは、「\ 0」または0と同じです。

19
KeylorSanchez

飛び込む前に、Cで expression に関する簡単なルールを述べたいと思います。

Cが式のブール値を必要とする場合、式がzeroと等しいときにfalse値が推論され、trueそれ以外の場合の値。つまり、書くたびに

if(expr)

exprは任意の式であり、コンパイラは基本的に次のように記述されているかのように動作します。

if((expr) != 0)  

あなたの質問に来て:

次のループで*pは何をしますか?

Cでは、文字列はヌル文字'\0'で終了します。

enter image description here

すべての文字に対応する10進数があります。この'\0'ASCIIエスケープ文字 です。 '\0'に相当する10進数は、0です。

したがって、ループ内の式*pは、pが指すメモリアドレスの文字に相当する10進数がゼロまたは非ゼロであることを確認するだけです。 pが文字列の最後に到達して最初の'\0'文字を見つけると、式*pは戻ります1 ゼロ値。ゼロは、Cのfalseを意味します。これは、上記の*p != '\0'または*p != 0のテストと同等です。

これがどのように機能するかです:

enter image description here


1*pが評価されると、*pの値がメモリからフェッチされます。この値は、式*pの値です。

13
haccks

ドライで深みのある方法で分析しましょう!

または、D。リッチーが言うように、 アセンブリ言語の力と…アセンブリ言語の便利さを使ってやってみましょう。


ISO/IEC:9899(emphasis mine)-C99標準を参照して、必要なすべての側面を説明しようとします。 (投稿スタイルは、Donald Knuthのフレーズ"「科学はコンピュータに説明するのに十分に理解できるものです。芸術は私たちが行う他のすべてです。」

まず最初に、for- loopが正確に何をすべきかを調べましょう!

ISO/IEC:9899 6.8.5「反復ステートメント」を参照

意味論

4反復ステートメントにより、ループ本体と呼ばれるステートメントが繰り返し実行されます制御式が0と等しくなるまで

これまでのところ、私が推測する新しいことは何もありません。

6.8.5.3 forステートメント

1ステートメントfor ( clause-1 ; expression-2 ; expression-3 ) statement

次のように動作します。式式-2は制御式つまり、ループ本体の各実行前に評価です。 ...

そのため、事前に評価された_// Code_の値がゼロでない限り、本文(あなたの場合は_*p_)が実行されることがわかります。

...式式-3が評価されます void式として各実行後ループ本体。[...]

(_p++_の定義は必要ないと思いますか?!)反復ごとにpが増加するため、_*p_に変更があるかもしれません。

次の点は関係ありませんが、forのセマンティック部分を完全にし、その理由であるfor(;;)がinfループである理由を知っているので、これを追加しています。

2(---)clause-1とexpression-3はどちらも省略できます。省略されたexpression-2は、ゼロ以外の定数に置き換えられます。

OK、それはあなたのケースでforループが行うことの、乾燥しているが情報が豊富な部分です。

次に、ポインター演算に取りかかりましょう。

6.5.6加算演算子

制約

2加算の場合、両方のオペランドが算術型であるか、または1つのオペランドはオブジェクトへのポインタ型で、もう一方は整数型である必要があります。 (インクリメントは1を追加することと同じです。

したがって、あなたの場合、「オブジェクトへのポインタ」タイプに1(整数)を追加しています。

tomislav kostic :のこの図に示されているように、アドレスのサイズをそのポイントされたタイプだけ増やすことと同等のもの

 CC BY-SA 3.0 by tomislav kostic 

_*p_が実際に何をするのかを見てみましょう。

6.5.3.2アドレスおよび間接演算子

制約

[...]

2単項*演算子のオペランドはポインター型でなければなりません。

意味論

[...]

4単項*演算子は間接を示します。オペランドが関数を指している場合、結果は関数指定子になります。 オブジェクトを指している場合、結果はオブジェクトを指定する左辺値です。オペランドのタイプが「 'type to to type'」の場合、結果のタイプは「 'type'」です。無効な値がポインターに割り当てられている場合、単項*演算子の動作は未定義です。

これは少し乾いた1 しかし、理解を深めるために、これを次の方法でリバースエンジニアリングできます。

6.5.2.1配列の添字

[...]

意味論

2角括弧[]で囲まれた式が後に続く後置式は、配列オブジェクトの要素の添字による指定です。 添字演算子[]の定義は、E1 [E2]が(*((E1)+(E2)))と同一であることです。

だから*((p)+(0)) what is(_p+0_はp...と同じであるため)は_p[0]_と等しく、pのオブジェクト。

また、forループの_expression-2_が_0_を評価している場合、繰り返しを中断していることがわかっているため、_p[0] != 0_と同じであると言えます。

最後のステップ

C-Coder's Friendを見てみましょう。 [〜#〜] jssca [〜#〜]...いや、待って...友人が呼ばれた... [〜#〜] ascii [〜#〜] これが明確になったので、_0_が何を表しているのかを理解できる。

Cで文字列の終わりを指定しているのはNULLトークンです。


決定的なもの:

すべて、これは次のとおりです。

forが実際にアドレスを指し示すまで、そのp- loopの本文を繰り返します。ここで、オブジェクトは「文字列の終わり」トークンと評価されます。

または:

pが最後まで文字列を通過するようにします。


そして今、私の自己を引用します。決して忘れてはならないもの:
(私のものを強調.....)

変数は、値に評価できる左辺値オブジェクトを指定する識別子の前にあるdeclarator(type-specifier)を介して宣言されます

多かれ少なかれ!


1それは、私が約束したことです! ;)

13
dhein

* pハイク

詩的には、ループ内の* pの苦労を表現しようとしました。

ブレイブC * p(ログラムマー)

静寂の輪の中

NULはそれらを停止します

これは俳句であり、3行で構成されています。最初と最後の行は5音節で、中央の行は7音節です。次に、* pがNULになるまで、pが増分されます。


少しポップ

enter image description here

Hour of Codeアンバサダー 、ジェシカアルバ


* pはループ内で何をしますか?

ジェシカ(D.クヌース(1)を引用)の想像上のアドバイスに従い、see* pの意味をforループ:

for (p = str; *p; p++)

この目標を達成するために、まず、Cで単項演算子 "*"がどのように機能するかを検証します。ポインターに適用されると、ポインターが指すオブジェクトにアクセスします。」(B. KernighanおよびD. Ritchie(2))

つまり、* pは単にpが指す値です

enter image description here

1.1 forループの詳細

Forループは、3つの命令で構成されています。

  1. p = str
  2. * p
  3. p ++

1.では、配列strにポインターをpに割り当てます。 Cでは、次の割り当ての効果は同じです。

p = &str[0];
p = str; 

「定義により、変数の値または配列型の式は、配列の要素ゼロのアドレスです」(K&R(2))。さらに、「a [i]を評価する際、Cはそれを*(a + i)にすぐに変換します。 …。つまり、&a [i]a + iは同一です」(K&R(2))。 i =とすると、上記の割り当てが得られます。

ここで、forループの開始時に、pstrの最初の要素を指していると述べることができます。

1.2質問の核

あなたの質問の中核であるポイント2に移りましょう。ループの2番目の式は終了条件を制御します。命令「* p」が評価され、falseの場合はループが終了します。これは、「* p」が「* p!= 0」または単語と同等であることを意味します。pが指す値がゼロの場合、exit

ここで、* pがゼロのときを理解するために、配列strが次のように初期化されたことを思い出してください。

char str[128] = "Some Text";

および:「すべての文字列定数には、最後の文字としてヌル終了文字(\ 0)が含まれます」(gnu-manual)。そのため、実際にメモリに保存される文字列の末尾には\ 0があります: "Some Text\0"。

3番目の命令p ++では、ポインターpは、9回目の反復でstr配列の次の要素に進みます。 * pが0(または\ 0、NULL、NUL、@ Joeの回答を参照)になり、ループが終了します。

1.3信じる

絵は千の言葉に値します、ここにループのグラフィカルな表現があります:

enter image description here

1.4さらなる例:異なる例での* pの同じ使用法

次のスニペットでは* pが同じ方法で使用されていますが、whileループで使用されています。

#include <stdio.h>
int main() {
    char str[128] = "We all scream for ice cream!";
    char *p = str;
    // here we see again the loop exit condition *p == '\0'
    while(*p) {
        printf("%c", *p);
        p++;
    }
    printf("\n");
}

For(; * C;)eがあなたと共にありますように!


参考文献

(1)Vol。 I、基本アルゴリズム、セクション1.1(1968)

(2)Cプログラミング言語Pg 94-99

9
terence hill

文字列の終端子(最終的にforループで見つかる)がASCII NULであり、これも評価されることがあるという事実を利用します。 tofalse、forループを終了します。

0、false、NULLとASCII NUL。この質問を参照してください。 NULL、 '\ 0'と0の違いは何ですか?

4
Joe

私は、さまざまな時に言及された賞金授与者の欲求を満たそうとしました。簡単にするために、回答をそれぞれ3行の3つのセクションに制限しました。これは、( The Bellman が彼の3つのルールで述べたように)「3回言ったことは本当です」(この答えのテーマ)。

テクニカル

forループの真理は、式*p0に評価され、ループの各反復の前にこの評価が実行されるときに終了します。C0はfalseとそれ以外はtrue-それは、他の世界ではvery拡張的な定義です!

ポインター変数pは、p = strで配列の先頭を指すように1回初期化され、pはすべての反復の終わりに増分されるため、*p各反復で配列の連続した要素にアクセスしています。

したがって、式*pは、0によって読み取られた配列要素が*pまたは0ターミネーターの終わりである場合、'\0'(false)と評価されます。 C「文字列」。ただし、コンパイラによって自動的に提供されるため、strの初期化ではこのゼロを確認できません。

歌詞

真実の表現

若者に理解されていない

リッチーとクヌースを読む

気まぐれ

ジェシカアルバは非常に知識のある素晴らしい女優であり、これらの引用が明らかにするように、コンピューター技術の発展を観察することから真実を乗り越えてきました。

「5年ごとに、私はまったく別の人だと感じています。」

「製品とそのパフォーマンスがすべてです。機能するか機能しないかのどちらかです。」

4
Weather Vane

昔、遠く離れたPDPでは、リソースが不足しており、名前が短くなりました。インデックスのi、ポインターのpは、初期のJediプログラマーを喜ばせました。

暗黙のテストは、for条件空間で真実を伝えました。単一の_*_を入力するだけで、pを信頼し、文字列の最後にプッシュしました。

今日まで、彼らはfor(e = s;*e;e++)最も馴染みのあるエレガントなループを使用して、C++帝国とそのctor、dtor、vile iteratorのコホートを無視しています。テンプレート、例外、および不明瞭なタイプに対する裸のビットとバイト、Cが戦う勇気のある人だけが、_void *_をアンキャストします。

4
chqrlie

俳句:

WHY   for (p=str; *p;        p++)
IS    for (p=str; p[0] != 0; p++)
THINK for (i=0;   str[i];    ++i)

[〜#〜]編集済み[〜#〜]

追加の詳細を次に示します。

「俳句」のコードの2行目は、1行目と同等です。元の投稿は、コードコメントで「これはどういう意味ですか」と尋ねています。 2行目は、同等性による答えを示しています。 * pp [0]を意味します。 forループの2番目の句は、p [0]がゼロに等しいかどうかを考慮します。

「俳句」のコードの3行目は、概念的に使用できるコード行です。元の行の動作は、3行目と同じように動作すると考えることができます。

3
synthetel

それがループのcondition部分です。
その条件が満たされない場合、ループはもう実行されません。
*pは、ポインターpを逆参照し、文字列strでポイントされている文字を返します。
Cスタイルの文字列strは、値\0で終了します。
条件が満たされないまで、ループは(pを使用して)各文字を反復処理します。

Cでは、0または\0の値は、falseの意味に似ています。つまり、条件が満たされていません。
他の値はtrueの意味に似ています。つまり、条件が満たされています。

つまり、pstrの各文字を反復処理し、文字列の終了文字\0に到達するとすぐに停止します。

なぜ*pの代わりにpを使用しないのですか?
pはポインターであり、アドレスが含まれているためです。アドレス演算のみを使用するのは難しい場合もあります。それは良い習慣ではなく、コードを読みにくくします。
*pは逆参照されたポインターであり、pが指す値を含みます。この場合、文字列が\0で終了していることがわかっているため、pが指す値を使用するのは簡単です。条件として(ifwhileなど)*p*p != '\0'と同等です。

2
Ely

まず、名前が何かを指していると言うように、ポインターの概念を把握する必要があります。ポインターには変数のアドレスが含まれています。

_    int var=0;
    int *p;
    int p=&var;
_

このコードではpはポインターであり、printf("%d",p);は変数varのアドレスを出力し、printf("%d",*p);は変数varの値を出力します。この例では0。

次に、配列の仕組みを理解する必要があります。配列は、固定サイズ[〜#〜] sequential [〜#〜]の要素のコレクションを格納できる一種のデータ構造です同じタイプ。

_        int array[3]={9,8,7};
        printf("%d",array[0]); //prints what is on 1st position,9
        printf("%d",array[1]); //prints what is on 2nd position,8
        printf("%d",array[2]); //prints what is on 3rd position,7
_

演算子_[]_は、配列を扱うユーザーフレンドリーな作業です。コードの最後の3行は、次の行に置き換えることができます(同じように機能します)。

_        printf("%d",*(array+0)); //prints what is on 1st position,9 
        printf("%d",*(array+1)); //prints what is on 2nd position,8
        printf("%d",*(array+2)); //prints what is on 3rd position,7
_

arrayは、配列の最初の要素へのポインタです(配列の最初の要素のアドレスを含む)。 _*array_。配列はseqentialであることがわかっています。これは_array+1_が配列の2番目の要素を指しているため、これを逆参照すると2番目の要素の値が取得されます。 *(array+1)など。 Memory segment of array

文字列は文字の配列であるため、文字列にも同じことが当てはまりますが、文字列の末尾に文字列が '\ 0'(ヌル文字)を持っている点が異なります。

_    char str[128] = "Some Text";
    char *p;

    for (p = str; *p; p++)
    {
        printf("%c",*p);
    }
_

このプログラムは、文字列strを出力します。

_p = str_ //文字列strの最初の文字のアドレスをpに割り当てます。文字列の最初の文字のトラックを失うことはないので、pを使用しますstrではない

_*p_ //この表現は_*p!=0_を意味するため、文字列の最後に到達するまでtrueになります。asciiの「0」には整数値48があることに注意してください

_p++_ // forブロックの最後にpに+1を追加して、次の文字のアドレスを取得します

1
Tomislav Kostic

次のように説明できます。

for( initialization ; Conditional Expression ; expression3)
{
    Code here will execute while 2nd Expression(Conditional Expression) is true
    true means non-zero value
    '\0' is equivelant to 0,so when *p equal '\0' : loop will terminate
}
0
machine_1