注目すべきC拡張には、動作がマシンのワードサイズに依存しない整数型が含まれますか


12

他のいくつかの言語と比較したCの興味深い特徴は、そのデータ型の多くが、絶対条件で指定されるのではなく、ターゲットアーキテクチャのワードサイズに基づいていることです。これにより、特定のタイプでは困難なマシンでコードを記述するために言語を使用できますが、異なるアーキテクチャで一貫して実行されるコードを設計することは非常に難しくなります。コードを考慮してください:

uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;

int16ビットのアーキテクチャでは(多くの小さなマイクロコントローラーに当てはまります)、このコードは明確に定義された動作を使用して値1を割り当てます。int64ビットのマシンでは、明確に定義された動作を使用して、値4294836225を割り当てます。int32ビットのマシンでは、-131071の値を割り当てる可能性があります(それが実装定義の動作か未定義の動作かはわかりません)。コードは名目上「固定サイズ」タイプであると想定されるもの以外は何も使用しませんが、標準では、現在使用されている2種類のコンパイラーが2つの異なる結果を生成し、今日の多くの一般的なコンパイラーが3番目を生成する必要があります。

この特定の例は、実際のコードでは2つの16ビット値の積を直接64ビット値に割り当てるとは思わないという点で、多少不自然ですが、3つの方法の整数を示す簡単な例として選ばれました。プロモーションは、固定サイズと思われるサイズの符号なしタイプと対話する場合があります。符号なしの型の数学を数学的な整数演算のルールに従って実行する必要がある現実の状況、モジュラー演算のルールに従って実行する必要のある状況、実際にそうでない状況があります問題ない。チェックサムなどの実世界のコードの多くは、算術に依存しており、少なくとも、未定義の動作をトリガーするのではなく、正確なmod 65536として定義されている結果を取得します。uint32_t算術ラッピングmod2³²におり、任意のを実行できます。uint16_t

この状況は明らかに望ましくないと思われますが(多くの目的で64ビット処理が標準になると、より多くなります)、私が観察したC標準委員会は、いくつかの顕著な生産ですでに使用されている言語機能を導入することを好みます環境を「ゼロから」作成するのではなく、C言語には、型を格納する方法だけでなく、可能なプロモーションを含むシナリオでの型の動作をコードで指定できる顕著な拡張機能がありますか?コンパイラー拡張機能がそのような問題を解決する可能性がある少なくとも3つの方法を見ることができます。

  1. 特定の「基本的な」整数型を特定のサイズに強制するようにコンパイラーに指示するディレクティブを追加します。

  2. ターゲットアーキテクチャ上の型の実際のサイズに関係なく、マシンの型が特定のサイズを持っているかのように、さまざまなプロモーションシナリオを評価するようコンパイラーに指示するディレクティブを追加します。

  3. 添加;(特定の特性を有するタイプに関係なく、基礎となるワードサイズの、MOD-65536ラッピング代数環として振る舞うべきであり、他のタイプに暗黙的に変換すべきではないこと、例えば宣言をタイプを宣言する手段を可能にすることによってwrap32には、int得られるはずが16ビットより大きいwrap32かどうかに関係なく、型の結果。ただしint、にwrap32直接aを追加するwrap16ことは不正です(どちらも他方に変換できないため)。

私自身の好みは3番目の選択肢です。なぜなら、異常なワードサイズのマシンでさえ、変数が2のべき乗サイズの場合と同じように「ラップ」する多くのコードで動作できるからです。コンパイラは、型を適切に動作させるためにビットマスキング命令を追加する必要がありますが、コードがmod 65536をラップする型を必要とする場合、ソースコードを乱雑にするよりも、それを必要とするマシンでコンパイラにそのようなマスキングを生成させる方が良いですまたは、そのようなマスキングが必要なマシンで使用できないため、単純にそのようなコードを持っています。しかし、上記の手段のいずれかを介して、または私が考えていない手段を介してポータブルな動作を実現する一般的な拡張機能があるかどうか、私は興味があります。

私が探しているものを明確にするために、いくつかのことがあります。最も顕著な:

  1. 望ましいセマンティクスを確保するためにコードを記述する方法は多数ありますが(たとえば、特定のサイズの符号なしオペランドで数学を実行して、明示的にラップするかしないかの結果を得るために実行するマクロを定義する)、または少なくとも望ましくないセマンティクス(たとえば、a が昇格しないコンパイラーで条件付きで型wrap32_tを定義し、その型が昇格するマシンでコンパイルに失敗する必要があるコードの方が、実行して偽の動作をもたらすよりも優れていると考える)将来の言語拡張機能で最も有利に動作するコードを記述する方法がある場合、それを使用することは、独自のアプローチを考案するよりも優れているでしょう。uint32_tuint32_twrap32_t

  2. 多くの整数サイズの問題を解決するために言語をどのように拡張できるかについて、かなり堅実なアイデアがあり、コードが異なるワードサイズのマシンで同一のセマンティクスをもたらすことを可能にしますが、それらを書くのにかなりの時間を費やす前にその方向でどのような努力が既に行われたかを知るため。

私は、C標準委員会または彼らが作成した作業を軽disするような見方を決して望みません。ただし、「自然な」プロモーションタイプが32ビットであるマシンと64ビットであるマシンで、数年以内にコードを正しく動作させることが必要になると思います。言語の控えめな拡張(C99 nnd C14間の他の多くの変更よりも控えめ)を使用すると、64ビットアーキテクチャを効率的に使用するクリーンな方法を提供できるだけでなく、バ​​ーゲンでも標準が歴史的に後方に曲げてサポートするために「異常なワードサイズ」のマシン(例えば、12ビットのマシンを可能にするcharuint32_tmod2³²をラップする]。将来の拡張機能の方向に応じて、デフォルトの整数型が「期待される」ように動作する今日のコンパイラで使用できるマクロを定義できるようになると期待していますが、整数の将来のコンパイラでも使用できますタイプのデフォルトの動作は異なりますが、必要な動作を提供できる場所です。


4
@RobertHarveyよろしいですか?私が整数の昇進を理解しているように、intがより大きい場合uint16_t、乗算のオペランドは昇格さintれ、乗算はint乗算として実行され、結果のint値はint64_tの初期化のために変換されますwho_knows

3
@RobertHarveyどうやって?OPのコードでは、についての言及はintありませんが、まだ潜入しています(C標準の私の理解が正しいと仮定します。)

2
@RobertHarvey確かに悪いように聞こえますが、そのような方法を指摘できない限り、「なにか間違ったことをしているに違いない」と言って何も貢献していません。まさに問題は、整数の昇格を回避する方法、またはその効果を回避する方法です。

3
@RobertHarvey:C標準化委員会の歴史的な目標の1つは、ほとんどすべてのマシンに「Cコンパイラ」を持たせ、特定のターゲットマシン用に独立して開発されたCコンパイラが十分に特定できるようにすることです。ほとんど交換可能です。これは、標準が草案される前に人々が多くのマシンのCコンパイラを書き始めたという事実によって複雑になり、標準化委員会はコンパイラが既存のコードが依存する可能性のあることを禁止したくありませんでした。標準の一部ではなく基本的な側面...
supercat

3
...それは、だれもが「理にかなった」一連の規則を策定しようとしたためではなく、むしろ、委員会が、既存の独立して作成されたコンパイラ共通ていたすべてのものを特定しようとしたためです。残念ながら、このアプローチは、プログラマーが何をする必要があるかを指定するには同時に曖昧すぎるが、コンパイラーが「ただそれを行う」ことを許可するにはあまりにも具体的である標準に至りました。
supercat

回答:


4

このようなコードの典型的な意図として

uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;

64ビット(結果が格納される変数のサイズ)で乗算を実行することです。(プラットフォームに依存しない)正しい結果を取得する通常の方法は、オペランドの1つをキャストして64ビット乗算を強制することです。

uint16_t ffff16 = 0xFFFF;
int64_t i_know = (int64_t)ffff16 * ffff16;

このプロセスを自動化するC拡張機能に遭遇したことはありません。


1
私の質問は、特定の算術式の正しい評価を強制する方法ではなく(オペランドのキャストの種類に応じて、オペランドのキャスト、またはのサイズに応じて、またはサイズに応じてuint32_t定義されたマクロの使用)、むしろ私自身のマクロを定義するのではなく、そのようなことを便利に処理するための新しい標準。#define UMUL1616to16(x,y)((uint16_t)((uint16_t)(x)*(uint16_t)(y)))#define UMUL1616to16(x,y)((uint16_t)((uint32_t)(x)*(uint16_t)(y)))int
supercat

ハッシュやチェックサムの計算などの場合、目的は多くの場合、結果を取得してオペランドのサイズに切り捨てることです。のような式の典型的な意図は、(ushort1*ushort2) & 65535uすべてのオペランド値に対してmod-65536算術演算を実行することです。C89の理論的根拠を読んで、結果が2147483647を超えると一部の実装でそのようなコードが失敗する可能性があることを著者が認識したが、そのような実装はますます稀になると予想したことはかなり明らかだと思います。ただし、このようなコードは最新のgccでは失敗する場合があります。
-supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.