見出し画像

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の実装を調べてみるといいかもしれません。

__builtin_va_list is automatically defined by gcc. This permits gcc to recognize and optimize uses of va_start and friends.

Discussion: where is the definition of "_builtin_va_list"

追記(2023/12/07):
別の資料でva_listのtypedefを見つけました。va_list型は実装依存であるため、システムによって異なるのだと思います。しかし、一部のシステムで、"va_list"を"char *"と型定義しているという事実は理解のヒントになりますね。

typedef char *va_list;
#define va_start(ap, v) (ap=(va_list) _ADDRESSOF(v) + _INTSIZEOF(v)) #define va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - INTSIZEOF(t))) #define va_end(ap) (ap = (va_list)0)

Variadic Functions in C An Introduction

4. まとめ

プリプロセスによって「改変されたソースプログラム」から、"typedef"による型定義を読むと、親しみのない型とも顔見知りになれます。

5. 参考文献

  1. gccの概要 by @keitean

  2. Compile in C by me

  3. Discussion: where is the definition of "_builtin_va_list" by Ian Lance Taylor


いいなと思ったら応援しよう!