C言語~C99で追加された仕様 | 2024.06.17 |
行末までコメント(C++のコメントを採用) |
C++と同様、「//」から行末までをコメントとする。
#include <stdio.h>
int main()
{
printf("hello World\n"); // 行末までコメント
}
int main()
{
printf("hello World\n"); // 行末までコメント
}
C99以前から多くのコンパイラ環境で独自に採用されていたが、公式に取り込まれた。
C言語~C99で追加された仕様 | 2024.06.11 |
変数宣言はブロック先頭でなくてもよい |
変数宣言の位置はC90までは関数定義などのブロックの先頭にまとめる必要があったが、C99からはC++と同様に、参照前なら先頭でなくてもよくなった。
#include <stdio.h>
int main()
{
printf("hello\n");
int a = 10; // 変数宣言が先頭ではない
printf("hello %d\n", a);
}
int main()
{
printf("hello\n");
int a = 10; // 変数宣言が先頭ではない
printf("hello %d\n", a);
}
上は、C90ではエラーになる。
ただしgccでコンパイルする場合は、-std=c90を指定してもgcc拡張が適用されてエラーにならない。
gccで厳密なC90を試すならば、-pedantic-errorsをつけてコンパイルするとよい。
ただしgccでコンパイルする場合は、-std=c90を指定してもgcc拡張が適用されてエラーにならない。
gccで厳密なC90を試すならば、-pedantic-errorsをつけてコンパイルするとよい。
C言語~C99で追加された仕様 | 2024.06.14 |
forの初期化部で変数宣言ができる |
C++のように、for文の初期化部で変数宣言ができる。
#include <stdio.h>
#
int main()
{
for (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
}
#
int main()
{
for (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
}
C言語~C99で追加された仕様 | 2024.06.11 |
bool型 |
真理値(ブール)型の導入。
型名は「bool」または「_Bool」、リテラルは「true」「false」。stdbool.hのインクルードが必要。
型名は「bool」または「_Bool」、リテラルは「true」「false」。stdbool.hのインクルードが必要。
#include <stdio.h>
#include <stdbool.h>
int main()
{
// C99 bool型、true/falseが追加
// _Bool または bool
//bool f = false;
_Bool f = true;
printf("hello %d\n", f);
}
#include <stdbool.h>
int main()
{
// C99 bool型、true/falseが追加
// _Bool または bool
//bool f = false;
_Bool f = true;
printf("hello %d\n", f);
}
上の結果は「hello 1」と出る。つまりtrue=1である。
上の_Boolはboolでもよい。
上の_Boolはboolでもよい。
C言語~C99で追加された仕様 | 2024.06.14 |
64ビット整数 |
long long(long long int) 64bit整数の導入。
#include <stdio.h>
int main()
{
long long lx = 1000LL;
unsigned long long ulx = 2000ULL;
long long int lxi = 3000LL;
printf("hallo %lld %llu %lld\n", lx, ulx, lxi);
}
int main()
{
long long lx = 1000LL;
unsigned long long ulx = 2000ULL;
long long int lxi = 3000LL;
printf("hallo %lld %llu %lld\n", lx, ulx, lxi);
}
64bitのLinuxはlongもlong longも同じ64ビット整数である。Windowsは64ビットでも従来どおりにlongは32ビットで、long longは64ビットである。定数のサフィックスはlongがLで、long longはLL、printfのフォーマットも%lldのように。
C言語~C99で追加された仕様 | 2024.06.14 |
ビット数が明確な整数型 |
プラットフォームに依存しないビット数が明確な整数型の定義。
#include <stdio.h>
#include <stdint.h>
//#include <inttypes.h>
int main()
{
int8_t i8 = INT8_MIN;
int16_t i16 = INT16_MAX;
int32_t i32 = INT32_MIN;
int64_t i64 = INT64_MAX;
uint16_t ui16 = UINT16_MAX;
uint64_t ui64 = UINT64_MAX;
printf("int8 %d\n", i8); // int8 -128
printf("int16 %d\n", i16); // int16 32767
printf("int32 %d\n", i32); // int32 -2147483648
printf("int64 %ld\n", i64); // int64 9223372036854775807
printf("uint16 %u\n", ui16); // uint16 65535
printf("uint64 %lu\n", ui64); // uint64 18446744073709551615
}
#include <stdint.h>
//#include <inttypes.h>
int main()
{
int8_t i8 = INT8_MIN;
int16_t i16 = INT16_MAX;
int32_t i32 = INT32_MIN;
int64_t i64 = INT64_MAX;
uint16_t ui16 = UINT16_MAX;
uint64_t ui64 = UINT64_MAX;
printf("int8 %d\n", i8); // int8 -128
printf("int16 %d\n", i16); // int16 32767
printf("int32 %d\n", i32); // int32 -2147483648
printf("int64 %ld\n", i64); // int64 9223372036854775807
printf("uint16 %u\n", ui16); // uint16 65535
printf("uint64 %lu\n", ui64); // uint64 18446744073709551615
}
stdint.hかinttypes.hのインクルードが必要。inttypes.hはstdint.hに加えてprintfの書式に便利なマクロが定義されている。
stdint.hには、次のような各整数型の最小・最大値がマクロ定義されている。
Type | 最小値 | 最大値 |
int8_t | INT8_MIN | INT8_MAX |
int16_t | INT16_MIN | INT16_MAX |
int32_t | INT32_MIN | INT32_MAX |
int64_t | INT64_MIN | INT64_MAX |
uint8_t | - | UINT8_MAX |
uint16_t | - | UINT16_MAX |
uint32_t | - | UINT32_MAX |
uint64_t | - | UINT64_MAX |
size_t | - | SIZE_MAX |
C言語~C99で追加された仕様 | 2024.06.14 |
指定する添字の排列要素を初期化する(指示初期化子) |
配列の添字を個別に指定して初期化できる。
int arr[] = { [添字] = 初期値, [添字] = 初期値, ... };
#include <stdio.h>
#
int main()
{
int arr[] = { 10, [3] = 13, 14 };
// C99 指示初期化子 配列の添字を指定して初期化できる。
// 指示初期化の添字前は、数値なら0、ポインタはNULLで初期化される。
int i;
for (i = 0; i < 5; i++) {
printf("%d\n", arr[i]);
}
}
#
int main()
{
int arr[] = { 10, [3] = 13, 14 };
// C99 指示初期化子 配列の添字を指定して初期化できる。
// 指示初期化の添字前は、数値なら0、ポインタはNULLで初期化される。
int i;
for (i = 0; i < 5; i++) {
printf("%d\n", arr[i]);
}
}
10
0
0
13
14
0
0
13
14
指示初期化子の後の通常の初期値は、その次の添字から始まる。初期値が指定されない範囲は0クリア(浮動小数点なら0.0でポインタはNULL)になる。
C言語~C99で追加された仕様 | 2024.06.14 |
指定する構造体メンバを初期化する(指示初期化子) |
構造体のメンバを個別に指定して初期化できる。
struct 構造体 x = { .メンバ名 = 初期値, .メンバ名 = 初期値, ... };
#include <stdio.h>
int main()
{
struct Person {
int age;
char *name;
} psn = { .age = 49, .name = "yamane" };
// すべてのメンバを初期化しなくてもよい
printf("%d %s\n", psn.age, psn.name);
}
int main()
{
struct Person {
int age;
char *name;
} psn = { .age = 49, .name = "yamane" };
// すべてのメンバを初期化しなくてもよい
printf("%d %s\n", psn.age, psn.name);
}
初期値が指定されないメンバは0クリア(浮動小数点なら0.0でポインタはNULL)になる。
C言語~C99で追加された仕様 | 2024.06.14 |
動的なサイズ指定による配列宣言 |
スタックに確保する自動変数の配列は、サイズを変数や演算の結果により動的に指定できる。※
#include <stdio.h>
#include <string.h>
#
int main()
{
int sz = 10;
char s[sz]; // 変数szの値で配列サイズが決まる
strcpy(s, "Hello");
printf("%s\n", s);
}
#include <string.h>
#
int main()
{
int sz = 10;
char s[sz]; // 変数szの値で配列サイズが決まる
strcpy(s, "Hello");
printf("%s\n", s);
}
※ | 静的配列には動的サイズは指定できない |
C言語~C99で追加された仕様 | 2024.06.14 |
サイズ0の配列宣言 |
サイズ0の配列が宣言できる。
主に構造体のメンバとして、サイズが不明な配列として定義する場合に有効である。
主に構造体のメンバとして、サイズが不明な配列として定義する場合に有効である。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
struct Person {
int size;
char name[]; // C99 サイズ0の配列
// C90までは1以上のサイズでないと配列は宣言できなかった。
// バッファサイズ未定で動的に確保する場合などでサイズ0の配列を定義できる
// C90ではエラーになることを確認した。
};
struct Person *p = malloc(sizeof(struct Person) + 10);;
strcpy(p->name, "Yamane");
printf("%s\n", p->name);
}
#include <stdlib.h>
#include <string.h>
int main()
{
struct Person {
int size;
char name[]; // C99 サイズ0の配列
// C90までは1以上のサイズでないと配列は宣言できなかった。
// バッファサイズ未定で動的に確保する場合などでサイズ0の配列を定義できる
// C90ではエラーになることを確認した。
};
struct Person *p = malloc(sizeof(struct Person) + 10);;
strcpy(p->name, "Yamane");
printf("%s\n", p->name);
}
上のような動的にサイズが決まる配列メンバのnameは、C90ではname[1]のようにする必要があった。
C言語~C99で追加された仕様 | 2024.06.14 |
複合リテラル |
配列・構造体のリテラル(定数)を表現できる(複合リテラル)。
構造体のリテラル (構造体){値, 値, ..}
配列のリテラル (型 []){値, 値, ..}
配列のリテラル (型 []){値, 値, ..}
#include <stdio.h>
struct Person {
int age;
char name[10];
};
void PrintPerson(struct Person *p)
{
printf("%s %d\n", p->name, p->age);
}
struct Person GetPerson()
{
return (struct Person){50, "Tanaka"};
}
int main()
{
PrintPerson(&(struct Person){49, "Yamane"}); // 構造体のリテラル
struct Person p = GetPerson();
printf("%s %d\n", p.name, p.age);
for (int i = 0; i < 5; i++) {
printf("%d\n", (int []){10, 20, 30, 40, 50}[i]); // 配列のリテラル
}
}
struct Person {
int age;
char name[10];
};
void PrintPerson(struct Person *p)
{
printf("%s %d\n", p->name, p->age);
}
struct Person GetPerson()
{
return (struct Person){50, "Tanaka"};
}
int main()
{
PrintPerson(&(struct Person){49, "Yamane"}); // 構造体のリテラル
struct Person p = GetPerson();
printf("%s %d\n", p.name, p.age);
for (int i = 0; i < 5; i++) {
printf("%d\n", (int []){10, 20, 30, 40, 50}[i]); // 配列のリテラル
}
}
Yamane 49
10
20
30
40
50
10
20
30
40
50
C90までは、配列は文字列のみ定数を表現できていたが、それ以外の構造物のリテラルの表現方法はなかった。複合リテラルにより、構造体や配列の変数を確保してから初期化しなくても、リテラルとして表現できるようになった。
C言語~C99で追加された仕様 | 2024.06.17 |
インライン関数 |
inline指示子により、関数をインライン化できる。
#include <stdio.h>
#include "test12.h"
// C99 inline関数
static inline int add(int a, int b)
{
return a + b;
}
int main()
{
printf("%d\n", add(10, 5));
}
#include "test12.h"
// C99 inline関数
static inline int add(int a, int b)
{
return a + b;
}
int main()
{
printf("%d\n", add(10, 5));
}
gccではstaticまたはexternのスコープを明示的に指定する必要がある。リンカがインライン関数の実体が見つけられないというエラーにされる。
C言語~C99で追加された仕様 | 2024.06.17 |
可変長引数マクロ関数 |
可変長な引数を受け取るマクロ関数の定義が可能。
#define 関数(...) func(__VA_ARGS__)
マクロ関数の可変長引数の部分を「...」として、__VA_ARGS__に展開する。
#include <stdio.h>
#define PR(fmt, ...) printf(fmt, __VA_ARGS__)
int main()
{
PR("Hello %d %d\n", 10, 20); // Hello 10 20
}
#define PR(fmt, ...) printf(fmt, __VA_ARGS__)
int main()
{
PR("Hello %d %d\n", 10, 20); // Hello 10 20
}
C言語~C99で追加された仕様 | 2024.06.14 |
現在の関数を参照するマクロ |
デバッグに便利な「現在の関数名」が参照できるマクロが定義された。※
__func__ 現在の関数名
__LINE__ 現在の行番号
__FILE__ 現在のソースファイル名
__LINE__ 現在の行番号
__FILE__ 現在のソースファイル名
#include <stdio.h>
void theFunction()
{
printf("%s %d %s\n", __func__, __LINE__, __FILE__);
}
int main()
{
theFunction();
}
void theFunction()
{
printf("%s %d %s\n", __func__, __LINE__, __FILE__);
}
int main()
{
theFunction();
}
theFunction 5 test08.c
※ | __LINE__と__FILE__はC90から存在する。 |