2024/04/24「多機能ファイラーAs/Rの魅力と活用法」を出版、KView32公開

RX ファミリ用 C/C++ コンパイラ C99 新規機能について 

マイコン

RX ファミリ用 C/C++ コンパイラパッケージ
アプリケーションノート:<コンパイラ活用ガイド>言語編(C89,C99)

から、C99 新規機能について説明します。

詳細は、以下のPDFファイルを参照してください。

  • RX ファミリ用 C/C++ コンパイラパッケージ
https://www.renesas.com/jp/ja/document/apn/rx-compiler-application-notes-2-compiler-usage-guide-languages-edition-c89-c99

プリミティブ型

C99 では、プリミティブ型として、論理型、複素数型、long long 型、可変長配列型、フレキシブル配列メンバが追加されました。

論理型

真か偽かを表すための論理型が追加されました。

C99 では、真か偽かを表す論理型として、bool 型、_Bool 型が追加されました。また、bool 型(_Bool 型)の値として、真を表す true と、偽を表す false が使用できます。bool 型、_Bool 型、true、false を使用するには、stdbool.h をインクルードしてください。

複素数型

複素数を扱うための_Complex 型と、複素数の虚数部を扱うための_Imaginary 型が追加されました。

C99 では、複素数を扱うための型が追加されました。複素数は、実数部と虚数部からなりたっています。
複素数型として、float _Complex 型、double _Complex 型、long double _Complex 型の3種類の型が使用できます。例えば float _Complex 型は、実数部と虚数部がそれぞれ float 型であるような複素数型であることを意味します。また、複素数の虚数部を扱うために、float _Imaginary 型、double _Imaginary 型、long double _Imaginary型の3種類の型が使用できます。例えば float _Imaginary 型は、float 型の虚数であることを意味します。さらに、虚数単位(数学的には i^2=-1 になるような i と定義されます)を扱うためにIが使用できます。なお、”_Complex”の代わりに”complex”、”_Imaginary”の代わりに”imaginary”、”I”の代わりに”I”を使用することもできます。複素数型を使用するには、complex.h をインクルードしてください。
複素数計算ライブラリを使用するには、標準ライブラリ構築ツールでオプション-head=complex を指定する必要があります。

long long 型

64 ビットの整数型を扱うために、long long 型が追加されました。

C99 では、64 ビットの整数型を扱うために、long long 型(signed long long 型、unsigned long long 型)が言語仕様として正式に規定されました。
なお RX ファミリ C/C++コンパイラでは、C89 でも long long 型が使用できます。

可変長配列型

C99 では可変長配列型が使用できるようになりました。
ただし、RX ファミリ C/C++コンパイラでは可変長配列型はサポートされていません。

フレキシブル配列メンバ

構造体や共用体の最後のメンバに、要素数を指定しない配列メンバ(フレキシブル配列メンバ)を宣言できるようになりました。

C89 では、配列型の構造体・共用体メンバを宣言する際、要素数の指定が必要でした。C99 では、構造体や共用体の最後のメンバに、要素数を指定しない配列メンバ(フレキシブル配列メンバ)を宣言できます。
ビットマップなどの画像データフォーマットや、TCP/IP などの通信データフォーマットで扱う構造体では、構造体の冒頭にデータサイズなどのヘッダ情報が宣言され、ヘッダ情報の後にデータ部分が配列型で宣言されることがあります。さらに、データ部分のサイズは実行時でなければ分からないことがあります。フレキシブル配列メンバにより、このようなデータフォーマットを容易に扱うことができます。

キーワード

C99 では、キーワード inline、restrict、_Pragma が追加されました。

inline

関数をインライン展開するようにコンパイラに指示を与えるために、inline キーワードがサポートされました。

関数に inline キーワードを指定することにより、コンパイラに対して関数のインライン展開を指示できるようになりました。ただし inline キーワードは、コンパイラがインライン展開を実施するためのヒントを与えるだけに過ぎず、実際にインライン展開を実施するかどうかは、コンパイラの処理に依存します。

restrict

コンパイラにポインタ向け最適化のヒントを与えるために、restrict キーワードが追加されました。

コンパイラに対し、restrict 修飾されたポインタが示す領域と、他のポインタが示す領域が重複しないことを明示することにより、コンパイラがポインタ向けの最適化を実施しやすくなります。

_Pragma

pragma と同じ機能を提供する演算子として、_Pragma キーワードが追加されました。

#pragma は処理系により使用方法が異なるため、使用するコンパイラの仕様に合わせて#pragma の書き方を変更する必要があります。ソースプログラム内で#pragma を頻繁に使用すると、#pragma 記述のたびにコンパイラ種別に応じて#pragma の記述を切り分ける必要があるため、ソースプログラムの可読性が悪くなることがあります。C99 で追加された_Pragma キーワードを使用することにより、より簡潔に#pragma を記述できます。

リテラル

C99 で新たに使用できるようになったリテラルについて説明します。

浮動小数点の 16 進数表記

浮動小数点の定数値を 16 進数で表記できるようになりました。

C99 では、浮動小数点数を 16 進数で表すことができます。これにより、10 進数で表すよりも誤差を小さくできます。例えば 0.1 という 10 進数の浮動小数点数を記述したとします。0.1 は 16 進数で正確に表すことができないため、コンパイラが 16 進数で表現する際、丸めや誤差などの影響により値が変化することがあります。しかし、16 進数で浮動小数点数を記述すると、丸めや誤差などの影響を受けずに値を固定できます。

浮動小数点数の 16 進数表記の書式は以下の通りです。

0xaaaa.bbbbPdd ( P は小文字も可)

先頭から”0x”、整数部 aaaa、小数点”.”、及び小数部 bbbb の順に書きます。P 以下は正または負の 10 進数で指数を記述します。なお、小数点や小数部は省略できますが、P 以下は省略できません。

enum

列挙型の最後の要素の後ろにカンマがあっても、正常にコンパイルされるようになりました。

C89 では、enum 宣言の最後に余分なカンマがあった場合、エラーになる仕様でした。しかし、C99 ではエラーにならないよう、仕様が変更されています。
なお、RX ファミリ用 C/C++コンパイラでは、C89 であっても余分なカンマの使用は許されます。

配列・構造体の初期化

配列や構造体を初期化する際、特定の要素やメンバを指定して初期化できるようになりました。

C99 では、配列の要素番号及び構造体のメンバを明示的に指定できる、指定初期化子という形式が使用できるようになりました。配列を初期化する際、C89 では各要素を先頭から順番に初期化する必要があります。
しかし、C99 では特定の要素にのみ初期値を設定することができます。このとき、初期化していない要素は0 で初期化されます。構造体のメンバも同様、C89 では先頭のメンバから順番に初期化する必要があります。
C99 では、特定のメンバにのみ初期値を設定することができます。このとき、初期化していないメンバは 0で初期化されます。初期化が必要な要素やメンバが限定されている場合、あるいは、要素数が大きな配列や多くのメンバを持つ構造体のうち特定の要素・メンバのみ初期化が必要な場合などに便利な機能です。

配列での使用例

C89

int array[5] = { 0, 0, 0, 2, 1 };

C99

array の 3 番目と 4 番目の要素に初期値を指定します。それ以外は値が 0 になります。

int array[5] = { [3] = 2, [4] = 1 };

構造体での使用例

C89

struct S {
 int a;
 int b;
} s = { 0, 1 };
struct T {
 int a;
 int b;
 struct T1 {
 int aa;
 int bb;
 } t1;
} t = { 0, 1, { 0, 2 } };
struct S1 {
 int a;
 int b[100];
 int c;
} s1 = { 10, {0,0,…}, 20 };

C99

特定の構造体メンバのみ初期値を設定します。構造体 T のように、構造体がネストしている場合でも使用できます。構造体 S1 のように、間に多くのメンバを含む場合であっても、特定のメンバだけ初期化できます。

struct S {
 int a;
 int b;
} s = { .b = 1 };
struct T {
 int a;
 int b;
 struct T1 {
 int aa;
 int bb;
 } t1;
} t = { .b = 1, .t1.bb = 2 };
struct S1 {
 int a;
 int b[100];
 int c;
} s1 = { .a = 10, .c = 20 }

複合リテラル

構造体や配列型の即値を記述できます。

C99 では、初期値を持つ無名のオブジェクトを作成できます。これにより、初期化された配列データや構造体データを扱う場合に、より簡潔に記述できるようになります。複合リテラルの書式は下記のようになります。

( 型 [ 要素数 ] ){ 要素 1, 要素 2, … }

使用例

C89 では、Point 型の変数 temp を宣言し、メンバを初期化したうえで関数 func に渡す必要があります。しかし C99 では、temp のような一次変数を用意することなく、直接関数に値を渡すことができます。

C89

typedef struct Point {
 short x,y;
} Point;
Point x;
void func(Point *p)
{
 x = *p;
}
void func_1()
{
 Point temp = {100,200};
 func(&temp);
}
void func_2()
{
 Point temp[2] = {{100,200},{300,400}};
 func(temp);
} 

C99

typedef struct Point {
 short x,y;
} Point;
Point x;
void func(Point *p)
{
 x = *p;
}
void func_1()
{
 func(&(Point){100,200});
}
void func_2()
{
 func((Point[2]){{100,200}, {300,400}});
} 

文法

C99 で新たに追加された文法について説明します。

一行コメント

C++の一行コメントが使用できるようになりました。

C89 まではコメント記述は/**/のみ使用できました。C99 では、//で始まり改行で終わる C++の一行コメントも使用できるようになりました。
なお、RX ファミリ用 C/C++コンパイラでは、C89 でも//によるコメントの記述が可能です。

ワイド文字の連結

ワイド文字の連結方法が規定されました。

C 言語では、2つ以上の文字列が連続して記述されている場合、1つの文字列に連結されます。例えば、「”aaa”△“bbb”」(△は半角スペース)という文字列があった場合、「”aaabbb”」のように連結した文字列として扱われます。同様にワイド文字列が連続している場合も、「L”aaa”△L”bbb”」は「L”aaabbb”」のように連結したワイド文字列として扱われます。ここで C89 では、文字列とワイド文字列が連続している場合の連結方法は未定義動作になっていました。C99 では、文字列とワイド文字列が連続している場合、ワイド文字列として連結することが規定されました。
なお RX ファミリ用 C/C++コンパイラでは、C89 で文字列とワイド文字列を連続して記述した場合にはC6282 エラーとなります。

可変個引数マクロ

マクロに可変個引数を使用できるようになりました。

C89 では、printf や scanf などのように関数に可変個引数を使用することはできましたが、マクロでは使用できませんでした。C99 では、マクロでも可変個引数を使用できるようになりました。書式は以下の通りです。


#define マクロ名(str,…) 可変個引数を使用する位置でVA_ARGSを使用

なお、マクロを使用する際、可変個引数を省略することも可能です。

関数型マクロの空引数

関数型マクロに空の引数を渡せるようになりました。

C89 では、関数型マクロを使用するときに引数を省略できませんでした。C99 では、空の引数を渡すことができるようになりました。
なお RX ファミリ用 C/C++コンパイラでは、C89 でもエラーにならず、警告(C5054)が出力されるだけです。C99 と同様、空の指定と解釈されます

識別子の使用可能文字

識別子にユニバーサル文字や多バイト文字が使用できるようになりました。

C89 では、識別子に使用可能な文字列は、アルファベット(大文字、小文字)、数字(識別子の先頭は除く)、”_”(アンダースコア)に限定されていました。C99 では、識別子に使用可能な文字が拡張され、ユニバーサル文字や多バイト文字を使用できるようになりました。ただし、数字は識別子の先頭として使用できません。

変数の宣言位置

変数の宣言をブロックの途中にも記述できるようになりました。

C89 では、変数宣言はブロックの先頭に書く必要がありました。C99 からは、変数が参照される前であれば、ブロックの先頭以外にも記述できます。

標準インクルードファイル

C99 では complex.h、fenv.h、inttypes.h、stdbool.h、stdint.h、tgmath.c の6つの標準インクルードファイルが追加されました。

complex.h

複素数計算ライブラリが追加されました。

complex.h は、複素数計算ライブラリを使用するための標準インクルードファイルです。以下に complex.h に含まれる関数の一覧を示します。float 型の複素数の場合は定義名の最後に”f”を付けた関数名、long double 型の複素数の場合は定義名の最後に”l”を付けた関数名、double 型の複素数の場合は定義名がそのまま関数名になります。
複素数計算ライブラリを使用するには、標準ライブラリ構築ツールでオプション-head=complex を指定する必要があります。

fenv.h

浮動小数点環境のライブラリが追加されました。

fenv.h は、浮動小数点環境へアクセスするための標準インクルードファイルです。浮動小数点環境とは、fenv.h で定義される浮動小数点状態フラグや浮動小数点例外フラグなどのことを言います。以下に、fenv.h に含まれるマクロ、関数の一覧を示します。
浮動小数点環境ライブラリを使用するには、標準ライブラリ構築ツールでオプション-head=fenv を指定する必要があります。

inttypes.h

整数型の書式変換を行うライブラリが追加されました

inttypes.h は、整数型を拡張するための標準インクルードファイルです。また、stdint.h で定義された整数型を扱うための書式を提供します。以下にマクロと関数の一覧を示します。
書式変換ライブラリを使用するには、標準ライブラリ構築ツールでオプション-head=inttypes を指定する必要があります。

stdbool.h

論理型、および論理値に関するマクロを定義する標準インクルードファイルが追加されました。

bool, true, false の名前を使用することができます。stdbool.h はマクロ名の定義だけからなるインクルードファイルです。以下に定義の一覧を示します。

stdint.h

指定した幅の整数型を宣言するための標準インクルードファイルが追加されました。

整数型を条件ごとに分類して定義します。整数型の大きさが環境に依存しないため、他の環境への移植性が高くなります。stdint.h はマクロ名の定義だけからなるインクルードファイルです。以下に定義の一覧を示します。

tgmath.h

型総称マクロを定義する標準インクルードファイルが追加されました。

tgmath.h をインクルードし、以下の一覧にある数学関数(型総称マクロ)を使用すると、引数の型に対応した関数名に自動的に展開されます。例えば、float 型の引数を sin 関数に渡すと sinf 関数に、complex 型の引数を渡すと csin 関数に展開されます。tgmath.h はマクロ名の定義だけからなるインクルードファイルです。

マクロ

C99 で新たに追加されたマクロについて説明します。

プリデファインドマクロ

STDC_ISO_10646STDC_IEC_559STDC_IEC_559_COMPLEXの3つのマクロが追加されま
した。

STDC_ISO_10646は、wchar_t の表現する文字コードが ISO/IEC 10646 に準拠している場合に定義されます。またマクロの値は、いつ規定された ISO/IEC 10646 に準拠しているか、yyyymmL(yyyy は年、mm は月)の形式で定義されます。RX ファミリ用 C/C++コンパイラでは、C99 の場合は 199712L と定義されます。しかし、C89 では定義されません。
STDC_IEC_559は、annex F (IEC60559 浮動小数点)に準拠することを意味します。RX ファミリ用 C/C++コンパイラでは、C99 では定義されますが、C89 では定義されません。
STDC_IEC_559_COMPLEXは、annex G (IEC60559 互換複素数)に準拠することを意味します。RX ファミリ用 C/C++コンパイラでは、C99 では定義されますが、C89 では定義されません。

プラグマ

C99 で新たに追加されたプラグマについて説明します。

#pragma STDC FP_CONTRACT

#pragma STDC FP_CONTRACT を使用することにより、浮動小数点演算を省略するかどうか制御できます。

浮動小数点演算の省略とは、浮動小数点定数値の丸めによる誤差を考慮しない、あるいは、演算結果の精度落ちがあった場合に例外が発生してもそれを通知しない、といった処理の省略を意味します。「#pragma STDC FP_CONTRACT ON」が宣言されると、それ以降の浮動小数点演算の省略が許されます。一方、「#pragma STDC FP_CONTRACT OFF」が宣言されると、それ以降の浮動小数点演算の省略が禁止されます。「#pragma STDC FP_CONTRACT DEFAULT」が宣言されると、それ以降の浮動小数点演算の省略はデフォルトの指定に戻ります。デフォルトの指定が ON か OFF のどちらに従うかは処理系定義です。
RX ファミリ用 C/C++コンパイラでは、#pragma STDC FP_CONTRACT 指定があっても、無視されます。

#pragma STDC FENV_ACCESS

#pragma STDC FENV_ACCESS を使用することにより、浮動小数点環境へのアクセスを制御できます。

浮動小数点環境とは、標準インクルードファイル fenv.h で定義される浮動小数点状態フラグや浮動小数点例外フラグなどのことを言います。浮動小数点環境へのアクセスの有無を明示的にコンパイラに知らせることにより、コンパイラが浮動小数点環境に対する最適化を実施しやすくなる場合があります。「#pragma STDC FENV_ACCESS ON」が宣言されると、それ以降、浮動小数点環境にアクセスする可能性があることをコンパイラに知らせます。一方、「#pragma STDC FENV_ACCESS OFF」が宣言されると、それ以降、浮動小数点環境にアクセスしないことをコンパイラに知らせます。「#pragma STDC FENV_ACCESS DEFAULT」が宣言されると、それ以降の浮動小数点環境へのアクセスはデフォルトの指定に戻ります。デフォルトの指定が ON か OFF のどちらに従うかかは処理系定義です。
RX ファミリ用 C/C++コンパイラでは、常に浮動小数点環境へのアクセスがあると解釈し、#pragma STDC FENV_ACCESS の指定は無視されます。

pragma STDC CX_LIMITED_RANGE

#pragma STDC CX_LIMITED_RANGE を使用することにより、複素数演算に対する数学公式の使用を制御できます。

複素数の乗算、除算、絶対値に対する数学公式は、無限大の扱いや、適切でないオーバフローやアンダーフローのために、問題があります。#pragma STDC CX_LIMITED_RANGE を使用することにより、数学公式が適用可能であることをコンパイラに知らせます。「#pragma STDC CX_LIMITED_RANGE ON」が宣言されると、それ以降、数学公式を適用可能になります。一方、「#pragma STDC CX_LIMITED_RANGE OFF」が宣言されると、それ以降、数学公式を適用不可能になります。「#pragma STDC CX_LIMITED_RANGEDEFAULT」が宣言されると、それ以降、数学公式の適用可否はデフォルトの指定に戻ります。デフォルトの指定は OFF です。
RX ファミリ用 C/C++コンパイラでは、#pragma STDC CX_LIMITED_RANGE 指定があっても、常に数学公式を使用します。

C89 から C99 へ移行時の注意点

暗黙の型宣言

暗黙の関数宣言と暗黙の型宣言に対する規定が変更されています。

C89 では、関数の原型宣言がなくてもその関数を呼び出すことができました。しかし C99 では、規格上、その関数の呼び出しは未定義動作として扱われるようになりました。
また、C89 では、暗黙の型宣言は int 型で扱われました。しかし C99 では、規格上、暗黙の変数宣言は許されなくなりました。

負の整数除算

負の整数除算に対する振る舞いが変わる可能性があります。

C89 では、整数除算の除数、被除数のうちどちらか一方が負の数の場合、その結果は実装依存になっていました。一方 C99 では、割り算の結果は全て 0 方向に切り捨てることになりました。この結果、負の整数除算を含むプログラムで、動作が変わる可能性があります。
RX ファミリ用 C/C++コンパイラでは、C89 の場合も 0 方向に切り捨てられます。

タイトルとURLをコピーしました