38

MISRA C 2012 ディレクティブ 4.12 は、「動的メモリ割り当てを使用しないでください」です。

例として、このドキュメントでは次のコード サンプルを提供しています。

char *p = (char *) malloc(10);
char *q;

free(p);
q = p; /* Undefined behaviour - value of p is indeterminate */

また、文書には次のように記載されています。

ポインターに格納された値は、free の呼び出し後も変更されていませんが、一部のターゲットでは、ポインターが指すメモリが存在しなくなり、そのポインターをコピーすると メモリ例外が発生する可能性があります

私はほとんどすべての文で問題ありませんが、最後です。p と q は両方ともスタックに割り当てられているため、ポインターのコピーによってメモリ例外が発生する可能性はありますか?

4

8 に答える 8

44

標準によると、ポインターのコピーq = p;は未定義の動作です。

J.2 未定義の動作状態を読む:

有効期間が終了したオブジェクトへのポインターの値が使用されます (6.2.4)。

その章に行くと、次のことがわかります。

6.2.4 オブジェクトの保存期間

オブジェクトの存続期間は、プログラム実行の中で、そのオブジェクト用にストレージが確保されることが保証されている部分です。オブジェクトは存在し、一定のアドレスを持ち、33)、最後に格納された値をその存続期間を通じて保持します。34) オブジェクトが存続期間外に参照される場合、動作は未定義です。ポインターの値は、それが指している (または直前の) オブジェクトがその存続期間の終わりに達すると、不確定になります。

不確定とは:

3.19.2 不定値: 未指定値またはトラップ表現のいずれか

于 2014-11-02T21:08:29.633 に答える
14

ポインタを介してオブジェクトを解放すると、そのメモリへのすべてのポインタが不確定になります。(偶数)不確定なメモリの読み取りは、未定義の動作 (UB) です。以下はUBです:

char *p = malloc(5);
free(p);
if(p == NULL) // UB: even just reading value of p as here, is UB
{

}
于 2014-11-02T21:39:52.793 に答える
4

まずは経歴を…

ISO/IEC JTC1/SC22/WG14 が (現在の ISO/IEC 9899:2011 を生成するために) C 言語の形式化を最初に開始したとき、問題がありました。

多くのコンパイラ ベンダーは、さまざまな方法で物事を解釈していました。

早い段階で、彼らは既存の機能を壊さないという決定を下しました...そのため、コンパイラの実装が分岐している場合、標準は提供unspecifiedundefined動作を提供します。

MISRA C は、これらの動作がトリガーする落とし穴をトラップしようとします。理論はここまで...

--

この質問の具体的な内容については、次のとおりです。

free() のポイントが動的メモリを解放してヒープに戻すことであることを考えると、3 つの可能な実装があり、そのすべてが「実際の」ものでした。

  1. ポインターを NULL にリセットする
  2. ポインターをそのままにしておきます
  3. ポインターを破壊する

標準はこれらのいずれかを義務付けることができなかったため、正式には動作を次のように残していundefinedます-実装は1つのパスをたどる可能性がありますが、別のコンパイラーは別のことを行う可能性があります...想定することはできず、メソッドに依存するのは危険です。

個人的には、標準が具体的で、ポインタを NULL に設定するために free() が必要だったほうがいいと思いますが、それは私の意見です。

--

だからTL;DR; 残念ながら、答えは次のとおりです。

于 2014-11-06T11:36:32.893 に答える
3

pとはどちらqもスタック上のポインタ変数ですが、 によって返されるメモリ アドレスはmalloc()スタック上にありません。

正常に割り当てられたメモリ領域が解放されると、その時点では、誰がメモリ領域を使用しているか、またはメモリ領域の配置を使用しているかどうかはわかりません。

そのため、メモリ領域を使用しようとしてfree()以前に取得したメモリ領域を解放するために使用されるのは、未定義のタイプのアクションです。malloc()あなたは幸運になるかもしれません、そしてそれはうまくいくでしょう。あなたは不運かもしれませんが、そうはなりません。メモリ領域をfree()所有すると、それを所有することはなくなり、他の何かが所有します。

ここでの問題は、あるメモリ ロケーションから別のメモリ ロケーションに値をコピーする際に、どのマシン コードが関係しているのかということです。MISRA は組み込みソフトウェア開発を対象としていることを忘れないでください。そのため、問題は常に、コピーで特別なことを行うファンキーなプロセッサがどのようなものであるかということです。

MISRA 規格は、堅牢性、信頼性、およびソフトウェア障害のリスクの排除に関するものです。彼らはかなりうるさいです。

于 2014-11-02T21:21:45.037 に答える
3

の値はp、それが指すメモリが解放された後はそのまま使用できません。より一般的には、初期化されていないポインターの値は同じステータスを持ちます。コピーする目的でそれを読み取るだけでも、未定義の動作が呼び出されます。

この驚くべき制限の理由は、トラップ表現の可能性です。が指すメモリを解放するpと、その値がトラップ表現になる可能性があります。

1990 年代初頭に、このように振る舞ったそのような標的の 1 つを覚えています。当時は組み込みターゲットではなく、むしろ広く使用されていました: Windows 2.x. Intel アーキテクチャを 16 ビット プロテクト モードで使用し、ポインターは 32 ビット幅で、16 ビットセレクターと 16 ビット オフセットを備えていました。メモリにアクセスするために、特定の命令を使用してポインターをレジスターのペア (セグメント・レジスターとアドレス・レジスター) にロードしました。

    LES  BX,[BP+4]   ; load pointer into ES:BX

ポインター値のセレクター部分をセグメント レジスタにロードすると、セレクター値を検証するという副作用がありました。セレクターが有効なメモリ セグメントを指していない場合、例外が発生します。

無害に見えるステートメントのコンパイルは、q = p;さまざまな方法でコンパイルできます。

    MOV  AX,[BP+4]    ; loading via DX:AX registers: no side effects
    MOV  DX,[BP+6]
    MOV  [BP-6],AX
    MOV  [BP-4],DX

また

    LES  BX,[BP+4]    ; loading via ES:BX registers: side effects
    MOV  [BP-6],BX
    MOV  [BP-4],ES

2 番目のオプションには 2 つの利点があります。

  • コードがよりコンパクトになり、命令が 1 つ少なくなります

  • ポインタ値は、メモリを逆参照するために直接使用できるレジスタにロードされます。これにより、後続のステートメントに対して生成される命令が少なくなる可能性があります。

メモリを解放すると、セグメントのマップが解除され、セレクタが無効になる場合があります。値はトラップ値になり、それを にロードするとES:BX例外が発生します。一部のアーキテクチャではトラップとも呼ばれます。

すべてのコンパイラがLESポインタ値をコピーするためだけに命令を使用するわけではありません。これは遅いためです。しかし、一部のコンパイラは、メモリがかなり高価で不足していたため、コンパクトなコードを生成するように指示されたときに使用しました。

C 標準ではこれが許可されており、未定義の動作の形式が次のコードで説明されています。

有効期間が終了したオブジェクトへのポインターの値が使用されます (6.2.4)。

この値は、次のように定義されると不確定になるためです。

3.19.2 不定値: 未指定の値またはトラップ表現のいずれか

ただし、文字型を介してエイリアシングすることで値を操作できることに注意してください。

/* dumping the value of the free'd pointer */
unsigned char *pc = (unsigned char*)&p;
size_t i;
for (i = 0; i < sizeof(p); i++)
    printf("%02X", pc[i]);   /* no problem here */

/* copying the value of the free'd pointer */
memcpy(&q, &p, sizeof(p));   /* no problem either */
于 2016-11-14T08:12:31.547 に答える
0

ポインターが逆参照されていない場合でも、ポインターを解放した後にポインターを検査するコードに問題があるのには、次の 2 つの理由があります。

  1. C 標準の作成者は、ポインタが周囲のメモリ ブロックに関する情報を含み、逆参照されているかどうかにかかわらず、何かが行われるたびにそのようなポインタを検証する可能性があるプラットフォームでの言語の実装に干渉することを望んでいませんでした。そのようなプラットフォームが存在する場合、標準に違反してポインターを使用するコードはそれらで動作しない可能性があります。

  2. 一部のコンパイラは、プログラムが UB を呼び出す入力の組み合わせを決して受け取らないという前提で動作するため、UB を生成する入力の組み合わせは不可能であると見なす必要があります。この結果、コンパイラが単純にそれらを無視した場合にターゲット プラットフォームに悪影響を及ぼさない UB の形式でさえ、恣意的で無制限の副作用を持つことになる可能性があります。

私見、解放されたポインターに対する等値、関係、またはポインター差分演算子が最新のシステムに悪影響を与える理由はありませんが、コンパイラーがクレイジーな「最適化」を適用するのが流行しているため、ありふれたプラットフォームは危険になっています。

于 2016-11-14T00:08:15.523 に答える
-1

The poor wording in the sample code is throwing you off.

It says "value of p is indeterminate", but it is not the value of p that is indeterminate, because p still has the same value (the address of a memory block which has been released).

Calling free(p) does not change p -- p is only changed once you leave the scope in which p is defined.

Instead, it is the value of what p points to that is indeterminate, since the memory block has been released, and it may as well be unmapped by the operating system. Accessing it either through p or through an aliased pointer (q) may cause an access violation.

于 2014-11-03T09:28:26.733 に答える