配列の場合、なぜa [5] == 5 [a]の場合ですか?

1651
Dinah 2008-12-20 07:01.

JoelがStackOverflowポッドキャスト#34で指摘しているように、Cプログラミング言語(別名:K&R)では、Cの配列のこのプロパティについて言及されています。a[5] == 5[a]

Joelは、それはポインタ演算のせいであると言いますが、私はまだ理解していません。なぜa[5] == 5[a]ですか?

17 answers

1952
Mehrdad Afshari 2008-12-20 07:04.

C標準では、[]演算子を次のように定義しています。

a[b] == *(a + b)

したがって、次のa[5]ように評価されます。

*(a + 5)

そして5[a]評価します:

*(5 + a)

a配列の最初の要素へのポインタです。a[5]は、から5要素離れた値aであり、と同じ*(a + 5)であり、小学校の数学から、それらが等しいことがわかります(加算は可換です)。

289
David Thornley 2008-12-20 07:05.

配列アクセスはポインタの観点から定義されているためです。a[i]*(a + i)、可換であるという意味で定義されます。

237
Keith Thompson 2013-08-23 15:37.

他の答えでは何かが見落とされていると思います。

はい、p[i]定義上、と同等です*(p+i)。これは(加算が可換であるため)、と同等です*(i+p)。これも([]演算子の定義により)と同等i[p]です。

array[i]では、配列名は暗黙的に配列の最初の要素へのポインターに変換されます。)

しかし、この場合、加算の可換性はそれほど明白ではありません。

両方のオペランドが同じ型である場合、または共通の型にプロモートされる異なる数値型である場合でも、可換性は完全に理にかなっていますx + y == y + x

ただし、この場合は、一方のオペランドがポインターで、もう一方のオペランドが整数であるポインター演算について具体的に説明しています。(整数+整数は別の演算であり、ポインター+ポインターは意味がありません。)

C標準の+演算子の説明(N1570 6.5.6)は次のように述べています。

さらに、両方のオペランドが算術型であるか、一方のオペランドが完全なオブジェクト型へのポインタであり、もう一方が整数型である必要があります。

それは同じように簡単に言ったかもしれません:

さらに、両方のオペランドが算術型であるか、左側のオペランドが完全なオブジェクト型へのポインタであり、右側のオペランドが整数型である必要があります。

その場合、i + pとの両方i[p]が違法になります。

C ++の用語では、実際には2セットのオーバーロードされた+演算子があり、大まかに次のように説明できます。

pointer operator+(pointer p, integer i);

そして

pointer operator+(integer i, pointer p);

そのうち最初のものだけが本当に必要です。

では、なぜこのようになっているのでしょうか。

C ++は、この定義をCから継承しました。CはBから取得しました(配列インデックスの可換性は、1972年のユーザーズリファレンスBに明示的に記載されています)、BCPL(1967年のマニュアル)から取得しました。以前の言語(CPL?Algol?)。

したがって、配列のインデックス付けは加算の観点から定義され、その加算は、ポインターと整数であっても可換であるという考えは、何十年も前にCの祖先言語にまでさかのぼります。

これらの言語は、現代のCよりも強く型付けされていませんでした。特に、ポインタと整数の区別はしばしば無視されていました。(初期のCプログラマーは、unsignedキーワードが言語に追加される前に、ポインターを符号なし整数として使用することがありました。)したがって、オペランドが異なるタイプであるために加算を非可換にするという考えは、これらの言語の設計者にはおそらく思い浮かばなかったでしょう。ユーザーが2つの「もの」を追加したい場合、それらの「もの」が整数、ポインター、またはその他のものであるかどうかにかかわらず、それを防ぐのは言語次第ではありませんでした。

そして何年にもわたって、そのルールへの変更は既存のコードを壊していたでしょう(1989年のANSI C標準は良い機会だったかもしれませんが)。

ポインタを左側に、整数を右側に配置する必要があるようにCやC ++を変更すると、既存のコードが破損する可能性がありますが、実際の表現力が失われることはありません。

つまり、後者の形式はIOCCCの外部に表示されるべきではありませんがarr[3]、今では3[arr]まったく同じ意味を持っています。

199
James Curran 2008-12-20 07:07.

そしてもちろん

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

これの主な理由は、Cが設計された70年代に、コンピューターに多くのメモリがなかったため(64KBが多かった)、Cコンパイラーが構文チェックをあまり行わなかったためです。したがって、「X[Y]」はやや盲目的に「*(X+Y)」に翻訳されました

これは、「+=」および「++」の構文についても説明しています。" A = B + C"の形式のすべてが同じコンパイル済み形式でした。ただし、BがAと同じオブジェクトである場合は、アセンブリレベルの最適化を利用できました。しかし、コンパイラーはそれを認識するのに十分な明るさ​​ではなかったので、開発者は(A += C)をしなければなりませんでした。同様に、もしCいた1、別のアセンブリレベルの最適化が利用可能であった、と再び開発者は、コンパイラがそれを認識していなかったので、それを明示しなければなりませんでした。(最近ではコンパイラーがそうしているので、これらの構文は最近ほとんど不要です)

56
user30364 2009-02-12 05:56.

誰もダイナの問題について言及していないようですsizeof

ポインターに追加できるのは整数のみで、2つのポインターを一緒に追加することはできません。そうすれば、整数へのポインター、またはポインターへの整数を追加するときに、コンパイラーは、考慮に入れる必要のあるサイズのビットを常に認識します。

50
Peter Lawrey 2011-08-12 03:50.

文字通り質問に答えること。それは必ずしも真実ではありませんx == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

プリント

false
29
Frédéric Terrazzoni 2012-06-11 09:50.

この醜い構文は「便利」であるか、同じ配列内の位置を参照するインデックスの配列を処理するときに少なくとも非常に楽しいものになる可能性があることがわかりました。ネストされた角括弧を置き換えて、コードを読みやすくすることができます。

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  
           
    cout << a[a[a[i]]] << endl;
    // ... is equivalent to ...
    cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)
    
}

もちろん、実際のコードではそのユースケースはないと確信していますが、とにかく面白いと思いました:)

26
PolyThinker 2008-12-20 22:16.

素敵な質問/回答。

Cポインタと配列は同じではないことを指摘したいだけですが、この場合、違いは本質的ではありません。

次の宣言を検討してください。

int a[10];
int* p = a;

ではa.out、シンボルaは配列の先頭のpアドレスにあり、シンボルはポインタが格納されているアドレスにあり、そのメモリ位置のポインタの値は配列の先頭です。

20
Noname 2012-03-23 21:05.

Cのポインタについては、

a[5] == *(a + 5)

そしてまた

5[a] == *(5 + a)

したがって、それは本当です a[5] == 5[a].

16
Ajay 2011-06-19 22:37.

答えではありませんが、考えるべき食べ物です。クラスにオーバーロードされたインデックス/添え字演算子がある場合、式0[x]は機能しません。

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

intクラスにアクセスできないため、これを行うことはできません。

class int
{
   int operator[](const Sub&);
};
12
A.s. Bhullar 2013-09-27 20:46.

テッド・ジェンセンによる「Cのポインターと配列に関するチュートリアル」で非常に良い説明があります。

テッドジェンセンはそれを次のように説明しました:

実際、これは真実です。つまり、どこに書いa[i]*(a + i)も問題なく置き換えることができます。実際、コンパイラーはどちらの場合も同じコードを作成します。したがって、ポインタ演算は配列のインデックス付けと同じであることがわかります。どちらの構文でも同じ結果が得られます。

これは、ポインタと配列が同じものであると言っているのではなく、そうではありません。配列の特定の要素を識別するために、2つの構文を選択できると言っているだけです。1つは配列のインデックス付けを使用し、もう1つはポインター演算を使用して同じ結果を生成します。

さて、この最後の式、その一部を見ると(a + i)、+演算子を使用した単純な加算であり、Cの規則は、そのような式は可換であると述べています。つまり、(a + i)は(i + a)。と同じです。したがって、と*(i + a)同じくらい簡単に書くことができます*(a + i)。しかし*(i + a)、から来た可能性がありますi[a]!これらすべてから、次のような奇妙な真実が生まれます。

char a[20];

書き込み

a[3] = 'x';

書くのと同じです

3[a] = 'x';
8
Ajinkya Patil 2016-05-04 22:24.

私は質問が答えられることを知っています、しかし私はこの説明を共有することに抵抗できませんでした。

コンパイラ設計の原則を覚えています。aint配列で、サイズintが2バイトで、のベースアドレスaが1000であると仮定しましょう。

どのようa[5]に機能しますか->

Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

そう、

同様に、cコードを3番地コードに分解5[a]すると、->になります。

Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 

したがって、基本的に両方のステートメントがメモリ内の同じ場所を指しているため、a[5] = 5[a]

この説明は、配列の負のインデックスがCで機能する理由でもあります。

つまり、私がアクセスa[-5]した場合、それは私に与えます

Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

990の位置にあるオブジェクトが返されます。

7
Krishan 2013-12-18 01:22.

Cアレイ、arr[3]及び3[arr]同じであり、その等価ポインタ表記である*(arr + 3)*(3 + arr)。しかし、逆に[arr]3[3]arr正しくないと、構文エラーになります、など(arr + 3)*(3 + arr)*有効な式ではありません。その理由は、間接参照演算子は、アドレスの後ではなく、式によって生成されるアドレスの前に配置する必要があるためです。

6
AVIK DUTTA 2014-10-29 23:14.

cコンパイラで

a[i]
i[a]
*(a+i)

配列内の要素を参照するさまざまな方法です!(まったく奇妙ではありません)

5
dgnuff 2019-04-09 08:45.

今少し歴史。他の言語の中でも、BCPLはCの初期の開発にかなり大きな影響を及ぼしました。BCPLで次のような配列を宣言した場合:

let V = vec 10

これは実際には10ではなく11ワードのメモリを割り当てました。通常はVが最初で、直後のワードのアドレスが含まれていました。したがって、Cとは異なり、Vという名前はその場所に移動し、配列の0番目の要素のアドレスを取得します。したがって、BCPLでの配列の間接参照は、次のように表されます。

let J = V!5

J = !(V + 5)配列のベースアドレスを取得するためにVをフェッチする必要があったため、実際には(BCPL構文を使用して)実行する必要がありました。したがってV!5、と5!V同義でした。事例観察として、WAFL(Warwick Functional Language)はBCPLで記述されており、私の記憶の限りでは、データストレージとして使用されるノードにアクセスするために前者ではなく後者の構文を使用する傾向がありました。確かにこれは35年から40年前のことなので、私の記憶は少し錆びています。:)

ストレージの余分な単語を省き、名前が付けられたときにコンパイラに配列のベースアドレスを挿入させるという革新は後で起こりました。Cの歴史論文によると、これは構造がCに追加された頃に起こりました。

!BCPLでは、単項接頭演算子とバイナリ中置演算子の両方があり、どちらの場合も間接参照を行っていたことに注意してください。バイナリ形式には、間接参照を実行する前に2つのオペランドの追加が含まれているだけです。BCPL(およびB)の単語指向の性質を考えると、これは実際には非常に理にかなっています。Cではデータ型を取得する際に「ポインタと整数」の制限が必要にsizeofなり、モノになりました。

1
Harsha J K 2018-04-03 08:42.

さて、これは言語サポートのためにのみ可能である機能です。

コンパイラはa[i]として解釈し*(a+i)、式はに5[a]評価され*(5+a)ます。加算は可換であるため、両方が等しいことがわかります。したがって、式はに評価されtrueます。

0
Jayghosh Wankar 2017-02-13 03:54.

Cで

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

ポインタpは「変数」であり、配列名aは「ニーモニック」または「シノニム」であるためp++、有効ですがa++無効です。

a[2]対等である2[a]このの両方の内部動作が内部のように計算「ポインタ演算」であるため、*(a+2)イコール*(2+a)

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

引退した一郎:「私はただ死ぬと思う」

引退した一郎:「私はただ死ぬと思う」

写真:リッチシュルツ/ APマーリンズ外野手/野球のトゥルーヒットキング鈴木一郎は43歳ですが、近い将来、プロ野球をやめるつもりはありません。昨年、彼は143試合に出場し、ヒットしました。

12年後、有名なクラブペンギン氷山がついにヒント

12年後、有名なクラブペンギン氷山がついにヒント

約12年間の運用後、ディズニーの子供向けのMMOクラブペンギンは閉店しますが、プレーヤーがゲームで最も長く実行されているミームの1つを実現する機会を得る前ではありません。氷山がひっくり返った。

安い航空券はあなたが思っているほどお得ではありません

安い航空券はあなたが思っているほどお得ではありません

お気に入りのレストランが割引価格で食事を提供し始めたが、ナプキン、フォーク、または座席が必要な場合は追加料金を支払う必要があるとします。私たちのほとんどはおそらく彼らに私たちのビジネスを与えることを躊躇するでしょうが、その躊躇は航空運賃に変換されません。

注意してください、あなたが話す次の男はUFCファイターかもしれません

注意してください、あなたが話す次の男はUFCファイターかもしれません

マークハントはUFCで最もヘビーな打者かもしれません。彼はほぼ間違いなく、現在オーストラリアに住んでいる最もタフなファイターです。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

安全な公衆パニックルーム

安全な公衆パニックルーム

不気味な音に慣れて、コンクリートの周りをうろつきます。痛みや苦い口を吐き出し、「過ち」の不思議な病気を通してもう一度味わうことを切望します。

長い間行方不明だった 2 つのサンフランシスコ クリークが間もなく日の目を見る可能性がある

都市の水路を回復する理由はたくさんありますが、気候が変化するにつれて、それらはすべてより持続可能な未来につながります.

長い間行方不明だった 2 つのサンフランシスコ クリークが間もなく日の目を見る可能性がある

サンフランシスコの長い間失われた小川を復元する動きが高まっており、コミュニティを地域の生態系と結びつけ、また、都市が気候変動や季節的な洪水に適応するのに役立つ新しいタイプの「グリーン インフラストラクチャ」を作成しています。最新の取り組みは町の 2 つの非常に異なる場所で行われており、それらは明確ではあるが補完的な方法で展開されています。

サンフランシスコのベイエリアで家族写真が撮れる場所トップ 5

サンフランシスコのベイエリアで家族写真が撮れる場所トップ 5

ベイエリアには写真を撮るための美しい屋外スポットがたくさんあります。この記事のすべての写真は、ベイエリアにいるさまざまな PictureHum フォトグラファーとの PictureHum セッションからのものです。

退屈に耐えられないから生きていけない

退屈に耐えられないから生きていけない

それが現代の衝動です — より少ないもので世界を覆し、私たちのほんのわずかな瞬間を自分たちから盗もうとする. 私はかつてダイニングルームのソファで一日を過ごし、祖父母の農家に斜めに差し込む光を眺め、私には関係のない世界について話している大人のつぶやきを半分聞いたり、壁を通してくぐもったテレビを聞いたり、遊んだりしました。床と犬と私の小さな靴を暖めた太陽の黒点と。

Language