-
Specific Features
目次
機能特徴
HI-TECH Cコンパイラは、他のCコンパイラとほぼ互換の多くの特徴を持ち、より信頼できるプログラミング手段に貢献します。
ANSI C標準の互換性
これを記述している時点では、C言語のANSI規格のドラフトがとても進んだステージにありましたが、いまだ公式な規格ではありませんでした。そのため、規格の要求に応じることはできませんでしたが、ANSI規格のドラフトにおける新しい、変化した特徴の大部分を取り込んでいます。そのため、ある意味では多くの人はこれを"ANSI準拠"とみなすことでしょう。
型のチェック
以前のCコンパイラは型のチェックに緩いアプローチを採用していました。これはUNIX Cコンパイラに顕著で、ほとんどの任意の型の混在をexpression内で許可します。HI-TECH Cコンパイラはより厳格な型のチェックを行いますが、もし、そのエラーが害のないものと、ユーザーがわかった場合、ほとんどの場合はコンパイルの続行を許可しつつ警告メッセージを出します。これが起こるのは、例えば、INT型の数値がポインタ変数に割り当てられた場合です。生成されたコードはほとんどユーザーの意図したものでしょうが、実際には、ソースコードでエラーがあり、ユーザーはすぐに必要な場所をチェックして修正します。
メンバ名
初期のCコンパイラでは、例外的な状況以外は、異なる構造において区別されたメンバ名が要求されていました。HI-TECH Cはほとんどの最近のCの実装と同じく、異なる構造体や共用体において重複を許可します。メンバが定義されている構造体の型の、式(expression)のコンテクストにおいてのみメンバ名が認識されます??。実用的に言えば、これはメンバ名が"."もしくは"-"演算子の右項でのみ認識されるということを意味し、式の左項はもしくはメンバが定義されているのと同じ構造体、もしくは構造体へのポインタと同じです??。これは一つ以上の構造体で衝突を起こさずに構造体名の再利用を許可するだけでなく、メンバ使用の型チェックを許可します。他のCコンパイラとの共通のエラーは、違う型の構造体へのポインタを伴うメンバの使用、もしくはこれよりよりひどい単純な型へのポインタである変数です???
しかしながら、このように定義されない構造体のポインタや何かとして、ユーザーが使いたい場合にはここから外れるものもあります。これは型変換を使用するものです。例えば、いくつかのレジスタからなるメモリマップトI/Oデバイスにアクセスしたいと仮定します。定義は以下の通りです。
struct io_dev { short io_status; /* status */ char io_rxdata; /* rx data */ char io_txdata; /* tx data */ }; #define RXRDY 01 /* rx ready */ #define TXRDY 02 /* tx ready */ /* デバイスの(絶対)アドレスの定義 */ /* define the (absolute) device address */ #define DEVICE ((struct io_dev *)0xFF00) send_byte(c) char c; { /* wait till transmitter ready */ while(!(DEVICE->io_status & TXRDY)) continue; /* send the data byte */ DEVICE->io_txdata = c; }
図2.絶対アドレスでの型変換
この例では、問題となるデバイスは16bitのステータスポートで二つの8bitデータポートがあります。デバイスのアドレス(つまりステータスポートのアドレス)は(16進数の)-FF00として与えられます。このアドレスは必要な構造体ポインタへと型変換され構造体のメンバ名が使用可能になります。これで生成されるコードは要求通りに絶対メモリにアクセスできるものです。
右項とメンバ名のいくつかの例については図3て提示されています。
符号なしの型
HI-TECH Cはすべての整数型、つまりCHAR,SHORT,INT,LONG、に対して符号なしの変種を持ちます。 符号なしの値が右シフトした場合、論理シフトの役割を果たします。つまり、最も右のビットに入れるということです。同様に符号ありの値が右シフトした場合も最も右のビットに符号を追加します??
算術演算子
intよりも短い場合には算術演算子がより効果的に働く機器において、intよりも短いオペランドは必要がない場合にはint以上には拡大されません。
例えば、二つの文字が他の文字に保存される場合
struct fred { char a; int b; } s1, * s2; struct bill { float c; long b; } x1, * x2; main() { /* 誤り - cはfredのメンバではありません */ s1.c = 2; /* 正しい */ s1.a = 2; /* 誤り - s2 はポインタです */ s2.a = 2; /* 正しい */ x2->b = 24L; /* 正しいのですが、longからintの型変換に注意してください */ s2->b = x2->b; }
図3. メンバの用法の例
上記の例は8bitで算術的に計算されるので、8bitの先頭のオーバーフローは失われます。しかしながら、もし、二つの文字がintに保存される場合、正しい結果を保証するため、加算は16bitで行われます。
ANSIスタンダードのドラフトによれば、doubleよりもむしろfloatによる計算の処理の方が、double精度に変換して再び戻すよりも精度が短いです。
構造体の扱い
HI-TECH Cは構造体の割り当て、構造体引数、構造体相当の?関数を完全に普遍的に実装しています。図4の例は構造体を返す関数の例です。いくつかの適切な(日適切な)使用法も同じく提示しています。
struct bill { char a; int b; } afunc() { struct bill x; return x; } main() { struct bill a; a = afunc(); /* ok */ pf("%d", afunc().a); /* ok */
/* 不適切な例:afunc()にはアサインできないのでafunc().aも同様?? */ afunc().a = 1; /* 同じ理由で不適切です */ afunc().a++; }
図4.構造体を返す関数の例
列挙型
HI-TECH Cは列挙型をサポートしています。これらは名前のついた定数の構造的な定義方法を提供します。
列挙型の使用はUnixのCコンパイラで許されている以上に制限されていますが、LINTで許可されているよりは柔軟性があります。特に列挙型の式(expression)は、配列インデックスとして、もしくはSwitch文(statement)のオペランドとして多次元配列に対しても使用できます。 算術演算も列挙型に対して実行でき、列挙型の式(expression)も代入?関係演算子ともに比較可能です。列挙型の使用例については図5で提示しています。
初期化のシンタックス
カーニハンとリッチーによる「プログラミング言語C」ではペアの括弧が、あるコンテクストにおけるイニシャライザでは省略できる、と言っています。一方ANSIドラフトではCプログラムに従うにはどのイニシャライザにおいてもすべての括弧を含まなければいけないか、全部を残しておく必要があるとしています??。HI-TECH Cはコンパイラのフロントエンドが、イニシャライズされる配列のサイズを決定し、またどの括弧が省略されたかについてあいまいさがないために ペアの括弧の省略を許可しています??
/* a represents 0, b -> 1 */ enum fred { a, b, c = 4 }; main() { enum fred x, y, z; x = z; if(x < z) func(); x = (enum fred)3; switch(z) { case a: case b: default: } }
図5.列挙型の使用
曖昧さを回避するために、カッコのペアが存在するとき、どの括弧のペアでも、これらの閉じ括弧も存在しなければなりません。曖昧さが存在する場合、コンパイラは("initialization syntax")の文句をいうでしょう。
5.9. Function Prototypes
VOIDとVOIDへのポインタ
VOID型は関数が値を返さないということをコンパイラに伝えるために使われます。VOID関数からの返り値はエラーとしてフラグが立ちます??
VOID*型、つまりVOIDへのポインタは"ユニバーサルな??"ポインタ型として使用されます。これは一般目的ストレージアロケータや他のポインタ型の違う値をアサインし得る??ものを返すポインタのような用途をアシストします???コンパイラは型変換やVOID*方と他のポインタ型の相互の型変換することなく許可します??プログラマはこの機能を注意深く、またどのようなVOID*の値も他のポインタ型に使用可能であることに注意してください。つまり、そのようなポインタとの連携はどんなオブジェクトでも保存するのに適しています??
5.11. Type qualifiers
5.12.
Cプログラムにインラインアセンブラを提供する方法は二つあります。一つ目はプログラム内のどこでも数行のアセンブラが許可されています。これは#asmから#endasmプリプロセッサディレクティブを介するものです。これらの二つのディレクトリに挟まれたいかなる行も、コンパイラによってそのまま直にアセンブラファイルにコピーされます。あるいCステートメントのどこかで、asm(string)構造体を使うことができます。stringはアセンブラファイルにコピーされます。コンパイラ生成コードに影響するため、インラインアセンブラコードには十分注意する必要があります。
PRAGMAディレクティブ
ANSIスタンダードドラフトはコンパイラに対して、コンパイラプロセスのいろいろなことをコントロールする、#pragmaプリプロセッサディレクティブを許可しています。HI-TECH Cはいまのところただ一つ、packディレクティブのみpragmaをサポートしています。 これはメンバが構造体に配置されるやりかたのコントロールを許可します。デフォルトではいくつかのコンパイラ(特に8086や68000コンパイラ)はマシンアクセスの最適化の境界でも構造体メンバを配置します????packプラグ間はパッキング要素の最大値を指定します。例えば#pragma pack1(1)はコンパイラに対し、構造体メンバの間に追加のパディングを行わない、つまりすべてのメンバが境界で1によって分けられる??ということを意味します。同様に#pragma pack(2)は境界が2で分けられて配置されることを許可します。packプラグマでケースを使用しない場合はそのデータタイプに対して使われる配置以上が強制されます??
一つ以上のpackプラグマがプログラム内で使用可能です。いつ使用しても他のpackプラグマによって変更されるか、ファイルの終了まで、強制は続きます。packプラグマを<stdio.h>のようなファイルの前に使用しないでください。ランタイムライブラリのデータ構造に誤った定義を引き起こします。