コンテンツにスキップ

C言語/C11の変更点

出典: フリー教科書『ウィキブックス(Wikibooks)』

2024年5月時点の最新のJIS C『プログラム言語 C』はJISX 3080:2003であり、これは ISO/IEC 9899:1999(Programming languages―C)及び ISO/IEC 9899 Technical Corrigendum 1:2001 を和訳したもので、その後改定 ISO/IEC 9899:2011(いわゆるC11)やISO/IEC 9899:2017(いわゆるC18)の内容を反映した JIS C の改定は行われておらず、現実の言語処理系の実装と JIS C の間で乖離が進んでいます。

他方、C18は仕様上の変更はなく __STDC_VERSION__ の定義が 201710L に更新されただけです。

そこで、この章では JISX 3080:2003 の元になった ISO/IEC 9899:1999+TC1:2001 とISO/IEC 9899:2011の差異を説明することで、JIS Cと現実の間を埋めようと思います。

第3版の主な変更点(__STDC_VERSION__ 201112L)には以下が含まれます:

  1. 条件付き(オプション)の機能(以前は必須だったものも含む)
  2. 複数の実行スレッドのサポート。改良されたメモリシーケンシングモデル、アトミックオブジェクト、スレッドローカルストレージが含まれる
  3. 追加の浮動小数点特性マクロ()
  4. オブジェクトのアラインメントの問い合わせと指定
  5. Unicode文字と文字列() (元々はISO/IEC TR 19769:2004で規定)
  6. 型汎用式
  7. 静的アサート
  8. 無名の構造体と共用体
  9. 戻り値のない関数を表す属性
  10. 複素数を作成するマクロ()
  11. 排他的アクセスでファイルを開くサポート
  12. gets関数の削除()
  13. aligned_allocat_quick_exitquick_exitの関数の追加()
  14. 境界チェックインターフェースの(条件付き)サポート (元々はISO/IEC TR 24731-1:2007で規定)
  15. 解析可能性の(条件付き)サポート

コード例、技術解説と変更の背景

[編集]

C11は、プログラミング言語Cの(C23がリリースされるまでは)最新バージョンであり、2011年に公開された標準仕様です。第3版では、いくつかの重要な変更が加えられました。以下ではそれぞれの変更点についてコード例と技術解説、変更の背景を示します。

Conditional (Optional) Features

[編集]
#ifdef __STDC__

#  if defined(__STDC_VERSION__) && __STDC_VERSION__>=201112L

printf("C11に対応(Version:%ld)\n", __STDC_VERSION__);

#    ifdef __STDC_NO_ATOMICS__

printf("ATOMICSに非対応\n");

#    else

printf("ATOMICSに対応\n");

#    endif

#  else

printf("C11より前に対応\n");

#else

/* ISO Cに未対応 */

printf("ISO Cに未準拠\n");

#endif
技術解説

第3版では、いくつかの機能が以前は必須であったものから任意のものに変更されました。これにより、異なるプラットフォームやコンパイラにおいて、様々な機能のサポートが柔軟になりました。

変更の背景

異なるプラットフォームやコンパイラの間で、機能のサポートが一貫していない場合がありました。この変更により、開発者はコードを柔軟にして、さまざまな環境で同じコードを実行できるようになりました。

Support for Multiple Threads of Execution

[編集]
#include <threads.h>
#include <stdio.h>

int main() {
    thrd_t thread;
    int result = thrd_create(&thread, my_function, NULL);
    if (result == thrd_success) {
        printf("Thread created successfully\n");
    }
    return 0;
}
技術解説

第3版では、複数のスレッドをサポートするために、<threads.h>が導入されました。これにより、スレッドの生成、同期、管理などが簡単になりました。

変更の背景

近年、マルチコアプロセッサが一般的になり、マルチスレッドプログラミングの需要が高まってきました。C言語の標準化においても、これに対応する必要があり、<threads.h>の導入が必要とされました。

Additional Floating-Point Characteristic Macros

[編集]
#include <float.h>
#include <stdio.h>

int main() {
    printf("Minimum positive normalized floating-point number: %e\n", FLT_MIN);
    printf("Maximum finite floating-point number: %e\n", FLT_MAX);
    return 0;
}
技術解説

新しい浮動小数点の特性マクロが導入されました。これにより、浮動小数点数の最小値や最大値などをプログラム中で使用できるようになりました。

変更の背景

浮動小数点数演算は、数値計算において不可欠であり、その性質を正確に理解することが重要です。新しい特性マクロは、プログラマーが浮動小数点数の動作をより正確に理解し、それに応じてコードを書くのに役立ちます。

Querying and Specifying Alignment of Objects

[編集]
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)aligned_alloc(16, sizeof(int));  // Allocate 16-byte aligned memory for an integer
    if (ptr) {
        printf("Pointer is aligned to 16 bytes: %s\n", ((uintptr_t)ptr % 16 == 0) ? "true" : "false");
        free(ptr);
    }
    return 0;
}
技術解説

<stdalign.h><stdlib.h> が導入され、オブジェクトのアライメントをクエリおよび指定する機能が追加されました。この例では、aligned_alloc() 関数を使用して、指定されたアライメントでメモリを割り当てます。

変更の背景

アライメントは、特定のハードウェア要件やパフォーマンスの向上のために必要な場合があります。特に、SIMD(Single Instruction, Multiple Data)命令を使用する場合などに重要です。アライメントの明示的な制御ができるようにすることで、プログラマーは効率的なコードを書くことができます。

Unicode Characters and Strings

[編集]
#include <stdio.h>
#include <uchar.h>

int main() {
    char16_t my_char = u'A'; // Unicode character literal
    char16_t my_string[] = u"Hello, こんにちは"; // Unicode string
    printf("Unicode character: %lc\n", my_char);
    printf("Unicode string: %s\n", my_string);
    return 0;
}
技術解説

<uchar.h> を使用して、Unicode 文字や文字列を扱う機能が追加されました。この例では、16 ビットの Unicode 文字を使用しています。

変更の背景

国際化が進む現代において、Unicode のサポートは非常に重要です。C11 において Unicode のサポートを強化することで、C 言語がより広範な環境で使用されるようになりました。

これらの変更により、C11 はモダンなプログラミングニーズに対応し、より効率的かつ安全なコードを書くことができるようになりました。

Type-Generic Expressions

[編集]
#include <stdio.h>

#define max(a, b)           \
    _Generic((a) + (b),     \
        int: max_int,       \
        double: max_double, \
        default: max_generic)(a, b)

int max_int(int a, int b) { return (a > b) ? a : b; }

double max_double(double a, double b) { return (a > b) ? a : b; }

void max_generic(void *a, void *b) { printf("Type not supported\n"); }

int main() {
    int result1 = max(10, 20);
    double result2 = max(10.5, 20.7);
    char result3 = max('a', 'b');
    printf("%d, %f, %c\n", result1, result2, result3);
    return 0;
}
技術解説

_Generic 構文を使用して、型に応じて異なる関数や式を選択することができます。この例では、max マクロが int 型と double 型に対して適切な関数を呼び出すようにしています。

変更の背景

C 言語は静的型付け言語ですが、ジェネリックな操作を行いたい場合があります。これまでのバージョンではマクロや void ポインタを使用する必要がありましたが、_Generic 構文の導入により、より型安全かつ柔軟なジェネリックなプログラミングが可能になりました。

Static Assertions

[編集]
#include <assert.h>
#include <stdio.h>

#define SIZE 5

_Static_assert(SIZE >= 5, "Array size must be at least 5");

int main() {
    int arr[SIZE];
    printf("Array size: %d\n", (int)(sizeof(arr) / sizeof(arr[0])));
    return 0;
}
技術解説

_Static_assert マクロを使用して、コンパイル時に条件式が真であるかどうかをチェックし、条件式が偽の場合にはコンパイルエラーを生成します。この例では、配列のサイズが 5 以上であることを静的にアサートしています。

変更の背景

静的アサーションは、プログラムの品質と安全性を向上させるために重要です。コンパイル時に問題を検出することで、実行時のエラーを減らすことができます。C11 の導入により、静的アサーションが言語仕様の一部となり、より安全なコードを書くことができるようになりました。

これらの変更は、C 言語の柔軟性、効率性、および安全性を向上させるために導入されました。開発者はこれらの新機能を活用することで、より高度なプログラミングを行うことができます。

Anonymous Structures and Unions

[編集]
#include <stdio.h>

struct example {
    union {
        int x;
        float y;
    }; // Anonymous union
    struct {
        char a;
        char b;
    }; // Anonymous structure
};

int main() {
    struct example e;
    e.x = 10;
    e.a = 'A';
    e.b = 'B';

    printf("Union member x: %d\n", e.x);
    printf("Struct members a and b: %c, %c\n", e.a, e.b);
    return 0;
}
技術解説

C11 では、匿名構造体と匿名共用体が導入されました。これにより、構造体や共用体のメンバーを直接参照できるようになり、コードが簡潔になります。この例では、匿名共用体と匿名構造体のメンバーを直接アクセスしています。

変更の背景

匿名構造体や共用体を使用することで、ネストされた構造体や共用体のメンバーを簡単にアクセスでき、コードの可読性が向上します。これにより、複雑なデータ構造を扱う際の柔軟性が向上しました。

No-Return Functions

[編集]
#include <stdlib.h>
#include <stdio.h>
#include <stdnoreturn.h>

noreturn void my_exit_function() {
    printf("Exiting program\n");
    exit(1);
}

int main() {
    my_exit_function();
    printf("This will never be printed\n");
    return 0;
}
技術解説

<stdnoreturn.h> を使用して、関数が戻らないことを示す noreturn 属性が導入されました。この例では、my_exit_function 関数が noreturn 属性を持ち、戻り値がないことを示しています。

変更の背景

戻り値がないことを明示することで、コンパイラはコードの最適化や警告生成を行いやすくなります。これにより、プログラムの予測可能性と信頼性が向上します。

Macros to Create Complex Numbers

[編集]
#include <stdio.h>
#include <complex.h>

int main() {
    double complex z1 = 1.0 + 2.0 * I;
    double complex z2 = CMPLX(1.0, 2.0); // Using CMPLX macro

    printf("Complex number z1: %.1f + %.1fi\n", creal(z1), cimag(z1));
    printf("Complex number z2: %.1f + %.1fi\n", creal(z2), cimag(z2));
    return 0;
}
技術解説

<complex.h> を使用して、複素数を作成するための CMPLX マクロが導入されました。このマクロは、実数部と虚数部を指定して複素数を簡単に作成できます。

変更の背景

複素数は科学技術計算において重要な役割を果たします。CMPLX マクロの導入により、複素数を扱うコードがより簡潔で読みやすくなりました。

Support for Opening Files for Exclusive Access

[編集]
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "wx"); // Open file for exclusive access
    if (fp) {
        fprintf(fp, "This file is opened for exclusive access.\n");
        fclose(fp);
    } else {
        printf("File could not be opened for exclusive access.\n");
    }
    return 0;
}
技術解説

ファイルを排他的に開くための "x" モードが導入されました。この例では、"wx" モードを使用してファイルを排他的に開きます。同じファイルがすでに存在する場合、fopen 関数は失敗します。

変更の背景

ファイルの排他的アクセスは、データ競合やファイルの上書き防止に役立ちます。この機能により、マルチスレッドやマルチプロセス環境でのファイルアクセスの安全性が向上しました。

Removed the gets Function

[編集]
#include <stdio.h>

int main() {
    // gets() function removed in C11
    // char buffer[100];
    // gets(buffer); // Deprecated, use fgets instead

    char buffer[100];
    if (fgets(buffer, 100, stdin)) {
        printf("You entered: %s", buffer);
    }
    return 0;
}
技術解説

C11 では、不正なメモリアクセスの危険性があるため、gets 関数が削除されました。この例では、安全な fgets 関数を使用しています。

変更の背景

gets 関数はバッファオーバーフローのリスクがあり、セキュリティ上の問題を引き起こす可能性があるため、C11 で削除されました。代わりに、より安全な fgets 関数が推奨されます。

Added the aligned_alloc, at_quick_exit, and quick_exit Functions

[編集]
#include <stdlib.h>
#include <stdio.h>

void cleanup_function() {
    printf("Cleanup function called.\n");
}

int main() {
    at_quick_exit(cleanup_function);
    printf("Calling quick_exit.\n");
    quick_exit(0);
    return 0;
}
技術解説

C11 では、新たに aligned_alloc、at_quick_exit、および quick_exit 関数が追加されました。aligned_alloc は特定のアライメントでメモリを割り当て、at_quick_exit は quick_exit 時に呼び出されるクリーンアップ関数を登録し、quick_exit は通常の終了処理を行わずに即座にプログラムを終了します。

変更の背景

これらの関数の追加により、メモリ管理の柔軟性が向上し、プログラムの即時終了やリソースクリーンアップが容易になりました。特に、高パフォーマンスアプリケーションや特定のメモリアクセス要件のあるプログラムにおいて有用です。

(Conditional) Support for Bounds-Checking Interfaces

[編集]
// Example code is conditional based on the availability of the feature
#ifdef __STDC_LIB_EXT1__
#include <stdio.h>
#include <string.h>

int main() {
    char dest[10];
    strcpy_s(dest, sizeof(dest), "Hello");
    printf("String copied: %s\n", dest);
    return 0;
}
#else
int main() {
    printf("Bounds-checking interfaces not supported.\n");
    return 0;
}
#endif
技術解説

C11 では、条件付きで境界チェックインターフェースが導入されました。strcpy_s のような関数は、バッファオーバーフローを防ぐために使用されます。

変更の背景

境界チェックインターフェースは、安全なコードを書くために重要です。これらのインターフェースは、バッファオーバーフローによるセキュリティ問題を防ぐのに役立ちます。

(Conditional) Support for Analyzability

[編集]
// Example of an analyzable function
#ifdef __STDC_ANALYZABLE__
#include <analyzable.h>
void my_function(int x) {
    __analyze_enter(x); // Hypothetical analyze enter marker
    // Function code
    __analyze_exit();   // Hypothetical analyze exit marker
}
#endif

int main() {
    // Code that may benefit from analyzability support
    return 0;
}
技術解説

C11 では、条件付きでコードの解析性をサポートする機能が導入されました。この機能により、プログラムの静的解析ツールが使用しやすくなり、コードの品質と信頼性が向上します。

変更の背景

コードの静的解析は、バグやセキュリティ問題を早期に発見するための重要な手段です。条件付きサポートにより、開発者は静的解析ツールをより効果的に使用できます。

これらの変更により、C11 はプログラミング言語としての柔軟性と安全性が大幅に向上し、現代の開発ニーズに対応する機能が強化されました。