280 likes | 390 Views
非標準機能 処理系依存機能. 内容. 非標準機能 標準化以前のソースコード 独自拡張 C99 と ISO C++ 処理系依存. 内容. 非標準機能 標準化以前のソースコード 独自拡張 C99 と ISO C++ 処理系依存. 標準化前のソースコード. C 言語 1990 年の標準化( C90 )と 1999 年の標準化( C99 )がある C++ 1998 年に ISO 標準策定( ISO C++, C++ std ). C 言語には 90 年以前に書かれたコード資産がある
E N D
内容 • 非標準機能 • 標準化以前のソースコード • 独自拡張 • C99 と ISO C++ • 処理系依存
内容 • 非標準機能 • 標準化以前のソースコード • 独自拡張 • C99 と ISO C++ • 処理系依存
標準化前のソースコード • C言語 • 1990年の標準化(C90)と1999年の標準化(C99)がある • C++ • 1998年にISO標準策定(ISO C++, C++ std) • C言語には90年以前に書かれたコード資産がある • Visual Studio 6.0 (コンパイラは 1997年のもの)で書かれたコードも多い 場合によっては、標準以前のコード→標準への変換が必要
関数の引数 • 関数() と {} の間に引数の型を宣言 標準化前 C90 int function(arg1, arg2) int arg1; int arg2; { return arg1 + arg2; } int function( int arg1, int arg2) { return arg1 + arg2; } • () 内で引数の型を宣言
for 内で定義した変数(C++) • for の () 内で宣言した変数を、for ループの後ろで再利用可能 VC++ 6.0 ISO C++ for (int i=1; i<N; ++i) sum += i; for (i=1; i<N; ++i) prod *= i; for (int i=1; i<N; ++i) sum += i; for (int i=1; i<N; ++i) prod *= i; for (int i=1; i<N; ++i) sum += i; assert(i == N); int i; for (i=1; i<N; ++i) sum += i; assert(i == N); • for の () 内で宣言した変数は、for 内でしか使えない
std名前空間 • .h なしのヘッダファイルも std 名前空間に入ってない VC++ 6.0 ISO C++ #include <cstdio> int main() { printf("foo\n"); } #include <cstdio> int main() { std::printf("foo\n"); } • .h なしのヘッダファイル中の関数は std 名前空間に入っている
内容 • 非標準機能 • 標準化以前のソースコード • 独自拡張 • C99 と ISO C++ • 処理系依存
独自拡張 • たまにある誤解 • 誤 : GCC でコンパイルできれば標準 • 正 : GCCは独自拡張機能が多い • むしろ、標準対応率は VC++ 2005 のコンパイラが一番高い • GCC で C/C++ プログラムを書くときは、 • -ansi -pedandic -std オプションを付ける • -ansi : C90, ISO C++(98) でコンパイル • -pedantic : GCC 独自拡張機能をオフにする • -std : -std "c++98" とかでバージョンを指定
配列のコピー代入 • GCC では = で配列をコピーできる† • 標準では無理(ポインタ渡しなら可能) char a[8], b[8]="StringB"; a = b; • 標準では・・・ • memcpy を使う char a[8], b[8]="StringB"; memcpy(a, b, 8); †注意: 流石にこんなバグの原因になりそうな構文は、削除された模様。最新のGCCでは使えない。
switch case で範囲指定 • GCC では、case に範囲を指定できる switch (n) { case 0 ... 3 : /* do something */break; ・・・ • 標準では・・・ • case を複数並べる switch (n) { case 0: case 1: case 2: case 3: /* do something */ break; ・・・
関数内で関数定義 • GCC では、関数内に関数を書ける void func(double x[], int num) { int inner(double xd) { return 1 / (1 - xd); } for (int i=0 ; i<num ; i++) { printf("%d\n", inner(x[i])); } } • 標準では同様の機能なし
内容 • 非標準機能 • 標準化以前のソースコード • 独自拡張 • C99 と ISO C++ • 処理系依存
C++とC99 • C++とC99には互換性がない C言語 C90 C99 互換性なし 上位互換あり C++ ISO C++ (98) • C99 も標準とはいえ・・・ • C++ との非互換が不評 • 対応しているのは GCC くらい • 元々、GCC の独自拡張機能が C99 標準として採用された
C++とC99共通の機能 • // コメント • inline キーワード • 暗黙の関数宣言廃止 C90 C99, C++ /* 戻り値・引数の型を省略 * すると int になる */ func(x1, x2) { return x1 * x2 } // 型の省略禁止 // inline キーワード追加 inline int func(int x1, int x2) { return x1 * x2 }
可変長引数マクロ • C99 では、可変長引数を持つマクロを定義できる #define dbg(fmt, ...) \ printf("debug:" fmt, __VA_ARGS__) • C++ では・・・ • 引数を持つマクロ自体非推奨 • 代わりに inline 関数を使う inline void dbg(char* fmt, ...) { va_list args; va_start (args, format); vfprintf (stdout, "debug:"format, args); va_end (args); }
可変長配列 • C99 では、変数で配列の長さを指定できる void func(int n) { double x[n]; ・・・ • C++ では・・・ • 配列ではなく、vector クラスを使う #include <vector> using std::vector; void func(int n) { vector<double> x = new vector<double>(n); ・・・
初期化子 • C99 では、以下のような構文で構造体の初期化可能 struct point { int x; int y; }; struct point p = { .x = 1, .y = 2}; • C++ では・・・ • コンストラクタを使う struct Point { int x; int y; Point(int xx, int yy) { x = xx; y = yy; } }; Point p = new Point(1, 2);
restrict ポインタ • 二つのポインタが同じ箇所を指していないことを明示することでコンパイラの最適化を促す void sumup(int n, int * restrict array1, int * restrict array2) { for (int i = 0; i < n; i++)array1[i] += array2[i]; } • こういう処理をするとき、array1 != array2 が保証されている場合にのみ使える命令があったりする • 残念ながら C++ には同様の機能なし
内容 • 非標準機能 • 標準化以前のソースコード • 独自拡張 • C99 と ISO C++ • 処理系依存
処理系依存 • C/C++ の規格上、コンパイラごとに変えてもいい部分が多々ある • オペランドの評価順 • short, int, long のサイズ • 構造体のパッキング
オペランドの評価順 • a+b の a を先に評価するか b を先に評価するかは決められていない int func(int n) { printf("%d ", n); return n; } int x = func(1) + func(2); • 「1 2」と表示されるか「2 1」と表示されるかは未定義 • ちゃんと分けて書くべき int a = func(1); int b = func(2); int x = a + b;
short, int, long のサイズ • 規格上では、以下の不等号のみが定められている • sizeof(short) ≦ sizeof(int) ≦ sizeof(long) • short, int, long のサイズ • 16, 16, 32 ・・・ OK • 16, 32, 32 ・・・ OK • 16, 32, 64 ・・・ OK • 16, 64, 64 ・・・ OK • 32, 32, 16 ・・・ これは駄目 • サイズ固定の整数を使いたければ・・・ • int32 などを使う(コンパイラごとに異なる) • C99 には stdint.h という、サイズ固定の整数を定義したヘッダがある
構造体のパッキング(1) • 構造体のメンバとメンバの間には隙間が空くことがある • (例えば32ビットCPUでは)4バイト間隔でメンバがならんでいると効率よくアクセス可能 • 隙間の空け方は規格で定められていない struct { char c1; char c2; } x; long offset = &(x.c2) - &x; • offset の値は処理系ごとに異なる
構造体のパッキング(2) struct { char c1; char c2; } x; 以下の3つとも規格上 OK x x x c1 c1 c1 c2 c2 c2 • コンパイラによっては、パッキング(メンバの並べ方)を指定可能(#pragma pack など。コンパイラごとに異なる)
コンパイラごとに処理を変える • コンパイラを判定 • ifdef で各コンパイラ特有のシンボルを確認 #ifdef __GNUC__ // GCC 用コード #endif #ifdef __BORLANDC__ // Borland C++ 及び C++Builder 用コード #endif #ifdef __WATCOMC__ // Watcom C/C++ 用コード #endif #ifdef _MSC_VER // Visual C++ 用コード #endif
pragma • #pragma プリプロセッサ • コンパイラ依存の指示を出す • #pragma でできること / 書き方はコンパイラごとに異なる • 対応していない #pragma は無視する (例) 構造体のパッキングを指定 #pragma pack(push,1) struct { char c1; char c2; } x; • 右の図のようになることが保証される • (この書き方は Visual C++ のもの) x c1 c2
参考URL • GCC 独自拡張 • http://www-cms.phys.s.u-tokyo.ac.jp/~naoki/CIPINTRO/gccextend.html • C99 • http://seclan.dll.jp/c99d/c99d00.htm • VC++ 6.0 の癖 • http://www.fides.dti.ne.jp/~oka-t/vc-mfc.html