「2016年、C言語はどう書くべきか」をちょっと分析してみる (types編)

# Overview

原典は、https://matt.sh/howto-c (日本語訳を作ってくださっている方のページは http://postd.cc/how-to-c-in-2016-1/ )を、分析してみるシリーズです。

[「2016年、C言語はどう書くべきか」をちょっと分析してみる (building編)](http://qiita.com/hidenorly/items/5831e8fa87f4a7adc2c9) では、リンク時の最適化 option (--flto)についての分析でした。

今回は、Writing code編です。

# Types

```
#include <stdint.h>
```

で、定義された型を使いましょうとのこと。

* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdint.h.html
* http://www.c-lang.org/detail/stdint_h.html
に参考文献があります。

c-langの説明から引用しますと、

|名称|内容|
|:--|:--|
| intＮ_t | 幅がＮビット、詰め物ビットなし、2の補数で表現される、符号付き整数型。例えば、int8_tなら、厳密に幅が8ビットの符号付き整数型となる。 |
| uintＮ_t | 幅がＮビットの符号なし整数型。 |
| int_leastＮ_t | 少なくとも幅がＮビットの符号付き整数型。 |
| uint_leastＮ_t | 少なくとも幅がＮビットの符号なし整数型。 |
| int_fastＮ_t | 少なくとも幅がＮビットの最速符号付き整数型。 |
| uint_fastＮ_t | 少なくとも幅がＮビットの最速符号なし整数型。 |
| intptr_t | voidへのポインタがこの型へ変換可能でかつ逆の変換も可能な符号付き整数型。 |
| uintptr_t | voidへのポインタがこの型へ変換可能でかつ逆の変換も可能な符号なし整数型。 |
| intmax_t | すべての符号付き整数型のすべての値を表現可能な符号付き整数型。 |
| uintmax_t | すべての符号なし整数型のすべての値を表現可能な符号なし整数 |

ポイント1:
* charとか使うな。
* 処理系によって変わるintとか使うな
* byteは、uint8_t使え

ポイント2:
* int_fast8_tとuint_fast8_tは、8bit幅とは限らず、8bit保持できる最速の方になる (platform依存)
* int_leastN_tとuint_leastN_tは、"Least types provide you with the most compact number of bits for the type you request."　つまり、N bitの幅の通りになる、のが普通の実装だそうです。

```
One thing to be aware of for fast types: it can impact certain test cases.
```

なお、fastと使うときは、テストケースにインパクトが有ることに注意しましょう、とのこと。
(実際にはplatformによって、幅が違うから。)

# intにすべきか、そうでないか

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/inttypes.h.html#tag_13_19_06

> The ISO/IEC 9899:1990 standard specified that the language should support four signed and unsigned integer data types- char, short, int, and long- but placed very little requirement on their size other than that int and short be at least 16 bits and long be at least as long as int and not smaller than 32 bits. For 16-bit systems, most implementations assigned 8, 16, 16, and 32 bits to char, short, int, and long, respectively. For 32-bit systems, the common practice has been to assign 8, 16, 32, and 32 bits to these types. This difference in int size can create some problems for users who migrate from one system to another which assigns different sizes to integer types, because the ISO C standard integer promotion rule can produce silent changes unexpectedly. The need for defining an extended integer type increased with the introduction of 64-bit systems.

ISO/IEC 9899:1990 standardでは、言語は4つのsignedとunsignedのinteger data typesであるchar, short, int, longをサポートすべきとしています。しかし、それらのサイズに関しては、ほんの少ししかrequirementを定めていません。

* intとshortは少なくとも16bit
* longは少なくともintと同じサイズで、32bitよりも短くはしない
 
 
* 16 bit systemでは、多くの実装が、{char, short, int, long}={8,16,16,32}です。
* 32 bit systemでは、多くの実装が、{char, short, int, long}={8,16,32,32}です。

このint sizeの差は、あるシステムから別のシステムへmigrateしたいユーザーに、いくつかの問題を引き起こします。とりわけ、intのサイズが異るシステムに。というのは、ISO C標準のintegerのpromotion ruleでは、静かに予期せぬ変化を起こすためです。64bit systemの導入に伴い、拡張integerを定義する必要が増えています。

# 例外としてcharを使うケース

* 既存の(charが既に使われている)APIを使うとき
* read-onlyの初期化を行うとき
```
const char *hello = "hello";
```
(というのは、Cのtype of stringのリテラル("hello")は ```char []``` だからです)

ちなみにC11では、unicodeをサポートしていて、utf-8 stringのリテラルは同じく```char []```とのこと。

```
const char *abcgrr = u8"abc😬";
```
のように、u8""でくくることで使える (余談: かつてのVC使いは、_T("")を思いだします。。。)

# 例外としてint, longなどを使うケース

> If you are using a function with native return types or native parameters, use types as described by the function prototype or API specification.

nativeな戻り型や、nativeなparameterと共に関数を使う場合は、function prototypeやAPIの規定に従って型を使います。

# 符号について

```unsigned long long int``` なんて使わずに、 ```uint64_t```を使おうということだそうです。同意。。。

# pointerの取り扱いについて

longなどに入れてpointerを計算するのではなく、

* ```<stdint.h>``` で定義された```uintptr_t``` や
* [stddef.h](http://pubs.opengroup.org/onlinepubs/7908799/xsh/stddef.h.html)で定義された ```ptrdiff_t```

を使うこと、とのことです。

```
ptrdiff_t diff = (uintptr_t)ptrOld - (uintptr_t)ptrNew;
```

とか

```
printf("%p is unaligned by %" PRIuPTR " bytes.\n", (void *)p, ((uintptr_t)somePtr & (sizeof(void *) - 1)));
```

みたいに。

## ptrdiff_t とは

http://pubs.opengroup.org/onlinepubs/7908799/xsh/stddef.h.html

>ptrdiff_t
Signed integral type of the result of subtracting two pointers.

のとおり、signedの整数の型であり、２つのポインターの差の結果を格納するための型とのこと。

# system依存のある型

systemに応じて、32bit/64bitのように扱いたい時 (例えばポインタとか)は、longを使いたくなるかもしれないが、使うなということです。

代わりには、pointerの計算のところで書いた ```ptrdiff_t``` でpointerのoffsetを扱ったり、```intptr_t```を使えとのこと (```uintptr_t```のflavorから来ているとのこと)

ちなみに、 [wikipedia](https://ja.wikipedia.org/wiki/64%E3%83%93%E3%83%83%E3%83%88) のLP64の項目などを見るとわかりますが、platformによってかなり挙動が違いますので、こういうbest practiceには従ったほうが良いです。