printf再実装プロジェクト (%pの処理)
今現在、%pの処理を取り組んでました。ようやく完成したのでそれを実装するにあたって必要になった知識や学習したことをまとめます。
まず、%pは何をするのかというところから。
man 3 printf でマニュアル確認をすると。。
The void * pointer argument is printed in hexadecimal (as if by %#x or %#lx).
ポインタ型の引数を16進数でプリントされると書いてあります。(%#x %#lxかのように。xは16進数で標準出力に出力し、#フラグがあると頭に0xを表示します。)
ポインタの値は、アドレスです。ただの整数値を16進数で表しているので、16進数でプリントする関数をつくれば良さそうです。
作っていく中で、ポインタ型の引数をとり16進数を表示する関数を作ったのですが、そうすると再利用性が低いので、ポインタ型の引数を取る関数と、16進数で表示する関数とで分けました。そうすれば、xの実装時も再利用できます。
そこで、つまずいたのは、16進数で表示する関数に何の型で値を渡すかです。そこで型変換を使うことにしました。
自分は、型変換についてそこまで知識がなかったので、一から詳しく勉強することにしました。その時に、JIS規格
https://www.jisc.go.jp/app/jis/general/GnrViewerLogIn?logIn
C言語の規約を読みました。なかなか、読みづらかったので、わからないところを適宜検索しながら噛み砕いて行きました。
そして、ポインタ型<=整数型という決まりがあったので、long long unsigned int 型を引数にとる 16進数で表示する関数を作りました。
ポインタ型の引数をとる関数の内部で、ポインタ型をlong long unsigned int 型にキャストしてエラーが出ないようにしました。
何とか%pを再現することができました。
これを再現する上で学んだことを簡単にまとめます。
・型変換について(暗黙の型変換、整数拡張、ゼロ拡張、符号拡張)
・# defineをつかったデバッグ
ps.
return (n++);としたら期待した値が帰ってこなかったです。n = 5ならreturn した時に6が帰ってくると期待しましたが、5でした。
理由は、後置インクリメントは式の値に影響せずに変数を1増加させるからです。例えば、
x = n++ + n;
という式があった場合、まず、左から式が評価されます。
x = 5 + n;
5というのはまだインクリメントされてない値です。
後置インクリメントは、式の評価後にインクリメントします。この時点で、nが+1されます。
そのあとに最後のn が評価されるのですが、nはすでに最初のnの評価を終えて、 インクリメントされているので、5+1の値。すなわち、6になります。
x = 5 + 6;
よってxの値は11になります。
これは、違和感を感じるかもしれないですが、言語の仕様なので、仕方ないです。インクリメントを使うときは、複雑にならないように(今回の例では、n++;を一行使って書く,インクリメントを式内で使わないなど。)簡潔に書くようにしてわかりやすいコードを書くことが大切だと思います。
次はx、Xに取り組みます。