配列の最初の要素の前のポインタ


8

Cでは、ポインターが同じ配列またはその配列の終わりを過ぎた1つの要素を参照する場合、演算と比較は明確に定義されていると言われています。次に、配列の最初の要素の前の1つはどうですか?私がそれを逆参照しない限り、それは大丈夫ですか?

与えられた

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

(1)書くことは合法--pですか?

(2)p-1式を書くことは合法ですか?

(3)(2)が問題ない場合、それを主張できp-1 < aますか?

これには実際的な懸念があります。reverse()で終わるC文字列を逆にする関数を考え'\0'ます。

#include <stdio.h>

void reverse(char *p)
{
    char *b, t;

    b = p;
    while (*p != '\0')
        p++;
    if (p == b)      /* Do I really need */
        return;      /* these two lines? */
    for (p--; b < p; b++, p--)
        t = *b, *b = *p, *p = t;
}

int main(void)
{
    char a[] = "Hello";

    reverse(a);
    printf("%s\n", a);
    return 0;
}

本当にコードでチェックする必要がありますか?

言語弁護士/実用的な観点からのあなたの考えと、そのような状況にどう対処するかを共有してください。


2
(1)記述しても問題ないかもしれませんが、実行すると結果は不定です。セグメント化されたアーキテクチャー(Intel 80286、80386など)では、結果が完全に混乱する場合があります。(2)同上。(3)なし。ただし、答えは「いいえ」です。あなたのハードウェアとあなたのo / sについては、あなたは安全かもしれませんが、C標準はそれを保証しません。
Jonathan Leffler

1
これはあなたの質問に答えますか?C ++プログラマが知っておくべき一般的な未定義の動作は何ですか?具体的には、セクションのポインターを確認してください。
イザヤ

回答:


6

(1)--pと書くことは合法ですか?

C構文のように「合法」ですが、未定義の動作を引き起こします。標準で関連するセクションを見つけることを目的とした場合、--pと同等ですp = p - 1(ただしp、評価は1回のみです)。次に:

C17 6.5.6 / 8

ポインターオペランドと結果の両方が同じ配列オブジェクトの要素を指している場合、または配列オブジェクトの最後の要素の1つ前を指している場合、評価によってオーバーフローは発生しません。それ以外の場合、動作は未定義です。

評価すでに未定義の動作を呼び出した-呼び出すには、デリファレンスポインタか、あなたならばそれは問題ではないという意味、動作は未定義。

さらに:

C17 6.5.6 / 9:

2つのポインターが差し引かれると、両方とも同じ配列オブジェクトの要素を指すか、配列オブジェクトの最後の要素の1つ後を指します。

コードがISO標準の「挑戦」に違反すると、未定義の動作を引き起こします。

(2)式にp-1を書くことは合法ですか?

(1)と同じ、未定義の動作。


これが実際に問題を引き起こす可能性のある例として、配列が有効なメモリページの最初に配置されていると想像してください。そのページの外でデクリメントすると、ハードウェア例外またはポインタートラップ表現が存在する可能性があります。これは、特にセグメント化されたメモリマップを使用している場合、マイクロコントローラーにとって完全にありそうもないシナリオではありません。


質問のサンプルコードにコメントを提供していただけませんか?チェックは必要/適切/十分ですか?
aafulei

それは*p == '\0'最初に可能です。このチェックはp--、forループを防ぐためのものです。
aafulei

@aafuleiそうだね。ループが正しく記述されていないため、追加のチェックが必要です。正しい修正は、ループを書き換えることです。例:godbolt.org/z/R4TuwT
Lundin

コードをありがとうございます。それは賢いものです。もっと多くの人に見てもらうために、あなたの答えに入れておくことをお勧めします。唯一のことは、文字列に奇数の文字がある場合(は数えません'\0')、最後に自己交換(それ自体と交換)が行われることです。しかし、それは結構です。また、ティックを作成する前に、クロス検証のためにもう少し長く我慢してください。
aafulei

p-1式が無効な場合、無効にp=p-1なります。そしてp--ですp=p-1。ポインタのデクリメントは無効であるとあなたは主張しますか?
ハーパー

-2

この種のポインター演算の使用は、デバッグが困難な問題のかなりの束につながる可能性があるため、コーディングの習慣としては不適切です。

こういうものを使うのは20年以上に一度しかありませんでした。コールバック関数を書いていましたが、適切なデータにアクセスできませんでした。呼び出し関数は内部にポインタ提供しました適切な配列そのポインターの直前のバイトが必要でした。

ソースコード全体にアクセスでき、動作を数回確認して必要なものを取得していることを証明し、他の同僚にレビューしてもらい、本番環境に移行してもよいと判断しました。

適切な解決策は、適切なポインターを返すように呼び出し元の関数を変更することでしたが、それは実現不可能であり、時間と費用を考慮しました(ソフトウェアのその部分はサードパーティからライセンス供与されました)。

したがって、a[-1]可能ですが、非常に特別な状況でのみ十分注意して使用する必要があります。そうでなければ、そのような自傷行為のブードゥー教を今までに行う正当な理由はありません。


注:適切な分析では、私の例では、適切な配列の開始前の要素にアクセスしなかったのは明らかですが、同じ配列内にあることが保証されているポインターの前の要素にアクセスしました。


提供されたコードを参照してください:

  • でp [-1]を使用することはできません reverse(a);
  • reverse(a+1);配列内にとどまるので、で使用しても問題ありません。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.