以下にリストした仮定に違反するアーキテクチャを知りたいです。
Stephen C が PERQ マシンについて言及し、MSalters が 68000 と PIC について言及したようです。
ある種の不当な仮定に適合しない標準準拠の C コンパイラを備えた奇妙で素晴らしいアーキテクチャの名前を挙げて、他の誰も実際に質問に答えなかったことに失望しています。
sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *) ?
必ずしも。いくつかの例:
ハーバード アーキテクチャの 8 ビット プロセッサ (PIC、8051、および M8C) 用のほとんどのコンパイラは、sizeof(int *) == sizeof(char *) を作成しますが、sizeof(func_ptr *) とは異なります。
これらのファミリの非常に小さなチップの中には、RAM が 256 バイト (またはそれ以下) のものもありますが、PROGMEM (フラッシュまたは ROM) が数キロバイトあるため、コンパイラは sizeof(int *) == sizeof(char *) を 1 に等しくします (a単一の 8 ビット バイト) ですが、sizeof(func_ptr *) は 2 (2 つの 8 ビット バイト) です。
数キロバイトの RAM と 128 キロバイトほどの PROGMEM を備えたこれらのファミリの大型チップの多くのコンパイラは、sizeof(int *) == sizeof(char *) を 2 (2 つの 8 ビット バイト) に等しくしますが、sizeof( func_ptr *) 3 (3 つの 8 ビット バイト) に等しい。
いくつかのハーバード アーキテクチャ チップは、完全な 2^16 ("64KByte") の PROGMEM (フラッシュまたは ROM) と、別の 2^16 ("64KByte") の RAM + メモリ マップド I/O を正確に格納できます。このようなチップのコンパイラは sizeof(func_ptr *) を常に 2 (2 バイト) にします。しかし、多くの場合、他の種類のポインター sizeof(int *) == sizeof(char *) == sizeof(void *) を「long ptr」の 3 バイト汎用ポインターにする方法があります。そのポインタがRAMまたはPROGMEMを指しているかどうかを示す追加のマジックビットがあります。(これは、多くの異なるサブルーチンからその関数を呼び出すときに、「print_text_to_the_LCD()」関数に渡す必要がある種類のポインターであり、RAM のどこにでもある可能性のあるバッファー内の変数文字列のアドレスを使用する場合もあれば、1 つの変数を使用する場合もあります)。 PROGMEM のどこにでもある可能性がある多くの定数文字列)。このようなコンパイラには、多くの場合、プログラマーが同じプログラム内で 3 種類の char ポインター (場所を示すのに 2 バイトしか必要としない定数文字列) を指定できるようにするための特別なキーワード ("short" または "near"、"long" または "far") があります。 PROGMEM では、それらは RAM 内のどこにあるかを示すために 2 バイトしか必要としない非定数文字列に配置されます。
1950 年代と 1960 年代に製造されたほとんどのコンピューターは、18 ビット (またはそれ以下) のアドレス バスで、 36 ビット ワード長または18 ビット ワード長を使用します。そのようなコンピューター用の C コンパイラーは、すべての整数と関数をワード境界で揃える必要があるため、sizeof(int *) == sizeof(func_ptr *) = 2 で9 ビットの bytesを使用することが多いと聞きました。ただし、sizeof(char *) == sizeof(void *) == 4 は、そのようなポインターを完全な 36 ビット ワードに格納する特別な PDP-10 命令を利用するためのものです。その完全な 36 ビット ワードには 18 ビット ワード アドレスが含まれ、その他の 18 ビットにはさらにいくつかのビットが含まれており、(とりわけ) そのワード内のポイント先の文字のビット位置を示します。
特定のアーキテクチャのすべてのポインターのメモリ内表現は、指すデータ型に関係なく同じですか?
必ずしも。いくつかの例:
上記のどのアーキテクチャでも、ポインタのサイズはさまざまです。では、どのようにして「同じ」表現を持つことができるのでしょうか?
一部のシステムの一部のコンパイラは、「記述子」を使用して、文字ポインターやその他の種類のポインターを実装します。このような記述子は、" " の最初の "char" を指すポインターと " " の最初の "char" を指すポインターとは異なります。これらは間違いなく異なるデータ型です。大きな配列によって以前に占有されていたメモリ内のまったく同じ場所に。記述子を使用すると、そのようなマシンは、他のマシンでこのような問題を引き起こすバッファ オーバーフローをキャッチしてトラップできます。char big_array[4000]
char small_array[10]
SAFElite および同様の「ソフト プロセッサ」で使用される「低脂肪ポインタ」には、ポインタが指すバッファのサイズに関する同様の「追加情報」があります。Low-Fat ポインターには、バッファー オーバーフローをキャッチしてトラップするという同じ利点があります。
ポインターのメモリ内表現は、アーキテクチャと同じビット長の整数と同じですか?
必ずしも。いくつかの例:
「タグ付きアーキテクチャ」マシンでは、メモリの各ワードには、そのワードが整数なのかポインタなのか、それ以外なのかを示すいくつかのビットがあります。このようなマシンでは、タグ ビットを見ると、その単語が整数かポインタかがわかります。
Nova ミニコンピューターには、「間接スレッド コード」に影響を与えた各単語に「間接ビット」があると聞いています。整数を格納するとそのビットがクリアされ、ポインタを格納するとそのビットが設定されるようです。
ポインター データ型の乗算と除算は、コンパイラによってのみ禁止されています。注: はい、これが無意味であることはわかっています。つまり、この誤った使用法を禁止するハードウェア サポートはありますか?
はい、一部のハードウェアはそのような操作を直接サポートしていません。
他の人がすでに述べたように、68000 と 6809 の「乗算」命令は、(一部の)「データレジスタ」でのみ機能します。「アドレスレジスタ」の値に直接適用することはできません。(コンパイラがこのような制限を回避するのは非常に簡単です。つまり、これらの値をアドレス レジスタから適切なデータ レジスタに MOV してから、MUL を使用します)。
すべてのポインター値を単一のデータ型にキャストできますか?
はい。
memcpy() が正しく機能するために、C 標準では、すべての種類のすべてのポインター値を void ポインター ("void *") にキャストできることが義務付けられています。
セグメントとオフセットをまだ使用しているアーキテクチャであっても、これを機能させるにはコンパイラが必要です。
すべてのポインター値を単一の整数にキャストできますか? 言い換えれば、セグメントとオフセットをまだ利用しているアーキテクチャは何ですか?
わからない。
すべてのポインター値は、" " で定義された "size_t" および "ptrdiff_t" 整数データ型にキャストできると思われます<stddef.h>
。
ポインターをインクリメントすることは、ポインターによって格納されたメモリ アドレスに sizeof (ポイントされたデータ型) を追加することと同じです。p が int32* の場合、p+1 は p の 4 バイト後のメモリ アドレスに等しくなります。
あなたがここで何を求めているのかは不明です。
Q: ある種の構造体またはプリミティブ データ型 (" #include <stdint.h> ... int32_t example_array[1000]; ...
" など) の配列があり、その配列を指すポインターをインクリメントするとします (たとえば、"int32_t p = &example_array[99]; .. . p++; ...")、ポインターはその配列の次の連続するメンバーを指していますか?これは、メモリ内で sizeof(ポイントされたデータ型) バイトです?
A: はい、コンパイラは、標準に準拠するために、ポインターを 1 回インクリメントした後、配列内の次の独立した連続する int32_t を指すようにする必要があります。
Q: では、p が int32* の場合、p+1 は p の 4 バイト後のメモリ アドレスに等しいのですか?
A: sizeof( int32_t ) が実際に 4 に等しい場合、はい。それ以外の場合、sizeof( int32_t ) が 2 または 1 に等しい最新の DSP を含む特定のワード アドレス指定可能なマシンなどの場合、p+1 は p の後のメモリ アドレス 2 または 1 "C バイト" に等しくなります。
Q: では、ポインタを取得して「int」にキャストすると...
A: 「全世界は VAX の異端」の 1 つのタイプです。
Q: ...そして、その「int」をポインタにキャストして戻します...
A: 別のタイプの「全世界が VAX の異端」です。
Q: int32_t へのポインターであるポインター p を取得し、ポインターを格納するのに十分な大きさのsizeof( int32_t )
整数型にキャストし、その整数型に追加し、後でその整数型をキャストして戻すとします。ポインタに - 私がそれをすべて行うと、結果のポインタはp + 1に等しくなりますか?
必ずしも。
多くの DSP と他のいくつかの最新のチップは、8 ビット チップで使用されるバイト指向の処理ではなく、ワード指向のアドレッシングを備えています。
そのようなチップ用の C コンパイラの中には、各単語に 2 文字を詰め込むものもありますが、int32_t を保持するにはそのような単語が 2 つ必要なので、4 であると報告されています ( 24 ビットsizeof( int32_t )
用の C コンパイラがあるという噂を聞いたことがあります)。これを行う Motorola 56000)。
コンパイラは、int32_t へのポインターを使用して "p++" を実行すると、ポインターが次の int32_t 値にインクリメントされるように調整する必要があります。コンパイラがそれを行う方法はいくつかあります。
標準に準拠した方法の 1 つは、int32_t への各ポインターを「ネイティブ ワード アドレス」として格納することです。単一のint32_t値を保持するのに 2 ワードかかるため、C コンパイラは " int32_t * p; ... p++
" をコンパイルして、そのポインタ値を 2 だけインクリメントするアセンブリ言語にします。int32_t * p; ... int x = (int)p; x += sizeof( int32_t ); p = (int32_t *)x;
ポインター値を 4 ずつインクリメントするアセンブリ言語にコンパイルします。
私は、連続した仮想メモリ空間で使用されるポインターに最も慣れています。
いくつかの PIC および 8086 およびその他のシステムには、連続していない RAM があります。これは、「ハードウェアをより単純にする」アドレスに RAM のいくつかのブロックがあります。これらのブロック間のアドレス空間のギャップに、メモリ マップド I/O が接続されていないか、何も接続されていません。
それは思ったよりもさらに厄介です。
場合によっては、読み取り-変更-書き込みによって引き起こされる問題を回避するためにビット バンディング ハードウェアを使用する場合など、2 つ以上の異なるアドレスを使用して、RAM 内のまったく同じビットを読み書きすることができます。