19

重複の可能性:
添え字を介して 1 つ後ろの配列要素のアドレスを取得: C++ 標準で合法かどうか?

int array[10];

int* a = array + 10;   // well-defined
int* b = &array[10];   // not sure...

最後の行は有効か無効か?

4

5 に答える 5

24

はい、配列の最後を超えてアドレスを取得できますが、逆参照することはできません。10個のアイテムの配列の場合、array+10うまくいきます。&array[10]実際に未定義の動作を引き起こすかどうか (そして、もしそうなら、本当にそうすべきかどうか) については (特に委員会によって) 数回議論されてきました。肝心なのは、少なくとも現在の標準 (C と C++ の両方) によれば、公式には未定義の動作を引き起こすということですが、実際に機能しないコンパイラが 1 つある場合、引数のいずれにも誰もできませんでした。それを見つけたり引用したりします。

編集: かつて私の記憶は半分正しかった - これは委員会への公式の欠陥報告(の一部)であり、少なくとも一部の委員会メンバー (Tom Plum など) は、未定義の動作を引き起こさないように文言が変更されたと考えていた. OTOH、DR は 2000 年にさかのぼり、ステータスはまだ「ドラフト中」であるため、本当に修正されているのか、それとも修正される可能性が高いのかは疑問の余地があります (N3090/3092 を調べて把握していません)。

ただし、C99 では、未定義の動作ではないことは明らかです。

于 2010-06-29T21:32:32.030 に答える
10

他の回答が示しているように、式array[10]は と同等*(array + 10)であり、配列の末尾を過ぎた要素の未定義の逆参照が発生するように見えます。ただし、式&array[10]は と同等&*(array + 10)であり、C99 標準では明確にされています (6.5.3.2 アドレスおよび間接演算子)。

単項 & 演算子は、そのオペランドのアドレスを返します。オペランドの型が ''type'' の場合、結果の型は ''pointer to type'' になります。オペランドが単項演算子の結果である場合、*その演算子も & 演算子も評価されず、演算子に対する制約が引き続き適用され、結果が左辺値ではないことを除いて、結果は両方が省略されたかのようになります。同様に、オペランドが演算子の結果である場合、演算[]子もによって暗示された&単項も評価されず、結果は、演算子が削除され、演算子が + 演算子に変更されたかのようになります。*[]&[]

したがって、未定義のもの&array[10]はありません。逆参照操作は行われません。

于 2010-06-29T21:45:43.220 に答える
2

いいえ、未定義です。 array[10]に相当し*(array + 10)ます。つまり、無効なポインターを逆参照しました。

于 2010-06-29T21:32:24.200 に答える
1

array[10]は と同等*(array + 10)(および とも同等10[array]) であるため、から&array[10]を削除したように機能します。適切なコンパイラは、同じコード (および同じ警告) を発行するはずです。**(array+10)

于 2010-06-29T21:32:50.487 に答える
-3

これは実際にはかなり簡単に答えることができます。

あなたがしているのはポインター演算だけです。それについて無効なことは何もありません。結果のポインターを何らかの方法で使用しようとする場合、プロセスがそのポインターが指すメモリにアクセスできるかどうかに依存します。アクセスできる場合、そのアクセス許可は何ですか?

foo.cc:

#include <iostream>

using namespace std;
int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        cerr << "Pointers values are: " << a << " " << b << " " << c << endl;
        return 0;
}

コンパイルする:

g++ -Werror -Wall foo.cc -o foo

int *b = &array[10] が有効であるため、警告は発生しません。

実際には、ポインター演算について何かを指摘するために私が追加した次の行も同様です。

foo がコンパイルされるだけでなく、問題なく実行されます。

 ./foo
  Pointers are: 0x7fff1d356e68 0x7fff1d356e68 0x7fff1d359d20

基本的に、配列構文 foo[idx] は、ポインター演算の簡潔でクリーンな表現です。

c99 に関して多少の混乱がある場合、標準はとにかくこの数学に影響を与えず、明確に定義されています。

C99 の同じこと:

#include <stdio.h>

int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        fprintf(stderr, "Pointers are: %p, %p, %p\n" , a , b , c );
        return 0;
}

それで:

gcc -std=c99 -Wall -Werror -o foo foo.c

./foo

出力:

Pointers are: 0x7fff2c7d22c8, 0x7fff2c7d22c8, 0x7fff2c7d5180
于 2010-06-29T22:31:22.517 に答える