for
ループでポインターが何をするのかわかりません。 *p
次のループで実行しますか?
char str[128] = "Some Text";
char *p;
for (p = str; *p /*what does this mean?*/; p++)
{
// Code
}
私は残りを理解していますが、なぜそうではない*p
like p > 3
またはそのようなもの?
なぜそれだけですか?
なぜそのように書かれているのですか?
for
ループの条件などのブールコンテキストでは、Cの各式はtrue(非ゼロ)またはfalse(ゼロ)に評価されます。
for
ループは、ストリングの終わりに達したときに終了する必要があります。
Cでは、各文字列は文字'\0'
で終了します。これは実際には0
です。したがって、for
ループが文字列の終わりに達すると、*p
は'\0'
に評価され、これは0
になり、falseに評価され、for
を終了しますループ。
Forループは、ステートメント内の2つの;
の間にあるものがゼロ(false)である場合に終了します。 *p
はpを逆参照し、char
、p
が指す値を返します。 デニスリッチーによれば、「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
文字列、各文字のインデックス(先頭からのオフセット)、および各インデックスの値の図は次のとおりです。
完全を期すため、またここでのコメントでのリクエストの後に、デバッガーは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
p
がforループの開始点を指します。t
で、16進値は0x74
です。文字列のヌル文字0x00
。その後、デバッグモードでビルドし、コンパイラがゼロで初期化されたため、さらにいくつかのヌル文字が表示されます。現在、Cのポインターを使用して急な学習曲線を描いていますが、最終的には "I C the point"と言うことができます。
これはこのように書き直すことができます
for (p = str; *p != '\0'; p++)
{
// Code
}
Cでは、文字列は常にヌル文字で終了する必要があります。これは、「\ 0」または0
と同じです。
飛び込む前に、Cで expression に関する簡単なルールを述べたいと思います。
Cが式のブール値を必要とする場合、式がzeroと等しいときに
false
値が推論され、true
それ以外の場合の値。つまり、書くたびにif(expr)
expr
は任意の式であり、コンパイラは基本的に次のように記述されているかのように動作します。if((expr) != 0)
あなたの質問に来て:
次のループで
*p
は何をしますか?
Cでは、文字列はヌル文字'\0'
で終了します。
すべての文字に対応する10進数があります。この'\0'
は ASCIIエスケープ文字 です。 '\0'
に相当する10進数は、0
です。
したがって、ループ内の式*p
は、p
が指すメモリアドレスの文字に相当する10進数がゼロまたは非ゼロであることを確認するだけです。 p
が文字列の最後に到達して最初の'\0'
文字を見つけると、式*p
は戻ります1 ゼロ値。ゼロは、Cのfalse
を意味します。これは、上記の*p != '\0'
または*p != 0
のテストと同等です。
これがどのように機能するかです:
1*p
が評価されると、*p
の値がメモリからフェッチされます。この値は、式*p
の値です。
または、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 :のこの図に示されているように、アドレスのサイズをそのポイントされたタイプだけ増やすことと同等のもの
_*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それは、私が約束したことです! ;)
詩的には、ループ内の* pの苦労を表現しようとしました。
ブレイブC * p(ログラムマー)
静寂の輪の中
NULはそれらを停止します
これは俳句であり、3行で構成されています。最初と最後の行は5音節で、中央の行は7音節です。次に、* pがNULになるまで、pが増分されます。
Hour of Codeアンバサダー 、ジェシカアルバ
ジェシカ(D.クヌース(1)を引用)の想像上のアドバイスに従い、seeの* pの意味をforループ:
for (p = str; *p; p++)
この目標を達成するために、まず、Cで単項演算子 "*"がどのように機能するかを検証します。ポインターに適用されると、ポインターが指すオブジェクトにアクセスします。」(B. KernighanおよびD. Ritchie(2))
つまり、* pは単にpが指す値です:
Forループは、3つの命令で構成されています。
- p = str
- * p
- 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ループの開始時に、pがstrの最初の要素を指していると述べることができます。
あなたの質問の中核であるポイント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の回答を参照)になり、ループが終了します。
絵は千の言葉に値します、ここにループのグラフィカルな表現があります:
次のスニペットでは* 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");
}
参考文献
(1)Vol。 I、基本アルゴリズム、セクション1.1(1968)
(2)Cプログラミング言語Pg 94-99
文字列の終端子(最終的にforループで見つかる)がASCII NUL
であり、これも評価されることがあるという事実を利用します。 tofalse、forループを終了します。
0、false、NULLとASCII NUL。この質問を参照してください。 NULL、 '\ 0'と0の違いは何ですか?
私は、さまざまな時に言及された賞金授与者の欲求を満たそうとしました。簡単にするために、回答をそれぞれ3行の3つのセクションに制限しました。これは、( The Bellman が彼の3つのルールで述べたように)「3回言ったことは本当です」(この答えのテーマ)。
テクニカル
for
ループの真理は、式*p
が0
に評価され、ループの各反復の前にこの評価が実行されるときに終了します。C0
はfalseとそれ以外はtrue-それは、他の世界ではvery拡張的な定義です!
ポインター変数p
は、p = str
で配列の先頭を指すように1回初期化され、p
はすべての反復の終わりに増分されるため、*p
各反復で配列の連続した要素にアクセスしています。
したがって、式*p
は、0
によって読み取られた配列要素が*p
または0
ターミネーターの終わりである場合、'\0'
(false)と評価されます。 C「文字列」。ただし、コンパイラによって自動的に提供されるため、str
の初期化ではこのゼロを確認できません。
歌詞
真実の表現
若者に理解されていない
リッチーとクヌースを読む
気まぐれ
ジェシカアルバは非常に知識のある素晴らしい女優であり、これらの引用が明らかにするように、コンピューター技術の発展を観察することから真実を乗り越えてきました。
「5年ごとに、私はまったく別の人だと感じています。」
「製品とそのパフォーマンスがすべてです。機能するか機能しないかのどちらかです。」
昔、遠く離れたPDPでは、リソースが不足しており、名前が短くなりました。インデックスのi
、ポインターのp
は、初期のJediプログラマーを喜ばせました。
暗黙のテストは、for
条件空間で真実を伝えました。単一の_*
_を入力するだけで、p
を信頼し、文字列の最後にプッシュしました。
今日まで、彼らはfor(e = s;*e;e++)
最も馴染みのあるエレガントなループを使用して、C++帝国とそのctor、dtor、vile iteratorのコホートを無視しています。テンプレート、例外、および不明瞭なタイプに対する裸のビットとバイト、Cが戦う勇気のある人だけが、_void *
_をアンキャストします。
俳句:
WHY for (p=str; *p; p++)
IS for (p=str; p[0] != 0; p++)
THINK for (i=0; str[i]; ++i)
[〜#〜]編集済み[〜#〜]
追加の詳細を次に示します。
「俳句」のコードの2行目は、1行目と同等です。元の投稿は、コードコメントで「これはどういう意味ですか」と尋ねています。 2行目は、同等性による答えを示しています。 * pはp [0]を意味します。 forループの2番目の句は、p [0]がゼロに等しいかどうかを考慮します。
「俳句」のコードの3行目は、概念的に使用できるコード行です。元の行の動作は、3行目と同じように動作すると考えることができます。
それがループのcondition部分です。
その条件が満たされない場合、ループはもう実行されません。*p
は、ポインターp
を逆参照し、文字列str
でポイントされている文字を返します。
Cスタイルの文字列str
は、値\0
で終了します。
条件が満たされないまで、ループは(p
を使用して)各文字を反復処理します。
Cでは、0
または\0
の値は、false
の意味に似ています。つまり、条件が満たされていません。
他の値はtrue
の意味に似ています。つまり、条件が満たされています。
つまり、p
はstr
の各文字を反復処理し、文字列の終了文字\0
に到達するとすぐに停止します。
なぜ*p
の代わりにp
を使用しないのですか?p
はポインターであり、アドレスが含まれているためです。アドレス演算のみを使用するのは難しい場合もあります。それは良い習慣ではなく、コードを読みにくくします。*p
は逆参照されたポインターであり、はp
が指す値を含みます。この場合、文字列が\0
で終了していることがわかっているため、p
が指す値を使用するのは簡単です。条件として(if
、while
など)*p
は*p != '\0'
と同等です。
まず、名前が何かを指していると言うように、ポインターの概念を把握する必要があります。ポインターには変数のアドレスが含まれています。
_ 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)
など。
文字列は文字の配列であるため、文字列にも同じことが当てはまりますが、文字列の末尾に文字列が '\ 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を追加して、次の文字のアドレスを取得します
次のように説明できます。
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
}