C言語の型を調べる方法〜size_t、ssize_t、va_listってなんぞや〜
0. はじめに
0.1 この記事で書くこと
size_t型やssize_t型、va_list型がどのように定義されているのか、私が調べた方法を書きます。
0.2 前提条件/環境/想定読者
・C言語のソースコードをコンパイルができる環境がある。
・grepや | (パイプ)の意味がわかる。
・char *やint、longなどの型はわかるけど、size_tとかはよくわからないなと思っている方(過去の自分)に向けて書きます。
0.3 注意とお願い
私自身、学習中の身なので間違っている理解があるかもしれません。その際は、ポジティブなフィードバックをいただけたらと思います。理解については以下の記事に書いています。
1. TL;DR
プリプロセス後のソースプログラムを調べると下記のようなことがわかります。
typedef long unsigned int __darwin_size_t;
typedef __darwin_size_t size_t;
typedef long __darwin_ssize_t;
typedef __darwin_ssize_t ssize_t;
typedef __builtin_va_list __darwin_va_list;
typedef __darwin_va_list va_list;
2. コンパイルについて
コンパイルの流れについては以下の記事に書きました。プリプロセスと聞いてピンとこない人は、読んでみてください。
3. 型について調べてみる。
3.1 size_t
$ cat size_t.c
#include <stdio.h>
int main(void)
{
printf("sizeof(size_t) = %d\n", (int)sizeof(size_t));
return (0);
}
$ gcc size_t.c && ./a.out
sizeof(size_t) = 8
"size_t" 型が 8bytes(64bits) だということがわかりました。
$ gcc size_t.c -E -o size_t.i
$ cat size_t.i | grep 'typedef' | grep 'size_t'
typedef long unsigned int __darwin_size_t;
typedef long __darwin_ssize_t;
typedef __int32_t __darwin_blksize_t;
typedef u_int64_t user_size_t;
typedef int64_t user_ssize_t;
typedef __darwin_size_t size_t;
typedef __darwin_ssize_t ssize_t;
"size_t" は、"__darwin_size_t" と同じで、"__darwin_size_t"は、"long unsigned int"と同じだということがわかります。つまり、"size_t"型は"long unsigned int"型だということです。
strlen()などの返り値が、"size_t"型ですね。
3.2 ssize_t
同様に"ssize_t"型も調べていきます。
$ cat size_t.c
#include <stdio.h>
int main(void)
{
printf("sizeof(ssize_t) = %d\n", (int)sizeof(ssize_t));
return (0);
}
$ gcc ssize_t.c && ./a.out
sizeof(ssize_t) = 8
$ gcc ssize_t.c -E -o ssize_t.i
$ cat ssize_t.i | grep 'typedef' | grep 'ssize_t'
typedef long __darwin_ssize_t;
typedef int64_t user_ssize_t;
typedef __darwin_ssize_t ssize_t;
以上より、"ssize_t"型は、"long"型と同じであり、8bytes(64bits) のサイズだということがわかります。
write() system call などの返り値が"ssize_t"型ですね。
"size_t"型や"ssize_t"型"は、PC環境によって大きさが変化するみたいです。自分のPCは、64bitsのシステムアーキテクチャなので、上記のような結果になります。PC環境が異なってもそれぞれの環境で動作するように、これら2つの型が使われるみたいです。
3.3 va_list
最後に"va_list"型も調べていきます。
$ cat va_list.c
#include <stdio.h>
int main(void)
{
printf("sizeof(va_list) = %d\n", (int)sizeof(va_list));
return (0);
$ gcc va_list.c && ./a.out
sizeof(va_list) = 8
$ cat va_list.i | grep 'typedef' | grep 'va_list'
typedef __builtin_va_list __darwin_va_list;
typedef __darwin_va_list va_list;
"va_list"型は、"__builtin_va_list"と同じなようです。8bytes(64bits)のサイズです。
"__builtin_va_list"については、Googleでgccの開発をされてきた Ian Lance Taylor さんが以下のように述べています。もっと深く知りたい方は、gccの実装を調べてみるといいかもしれません。
追記(2023/12/07):
別の資料でva_listのtypedefを見つけました。va_list型は実装依存であるため、システムによって異なるのだと思います。しかし、一部のシステムで、"va_list"を"char *"と型定義しているという事実は理解のヒントになりますね。
4. まとめ
プリプロセスによって「改変されたソースプログラム」から、"typedef"による型定義を読むと、親しみのない型とも顔見知りになれます。