Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 15320] ファイルの概要

このコミットは、Go言語のランタイムにおけるprintf関数に、文字(char)値を出力するための新しい変換指定子%cを追加するものです。これにより、ランタイム内部でのデバッグ出力やログ記録において、単一の文字をより直接的かつ意図的に表示できるようになります。

コミット

commit 7f9c02a10d736d8d4c39717c82b69ec50e9677f1
Author: Carl Shapiro <cshapiro@google.com>
Date:   Tue Feb 19 18:05:44 2013 -0800

    runtime: add conversion specifier to printf for char values
    
    R=r, golang-dev
    CC=golang-dev
    https://golang.org/cl/7327053

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/7f9c02a10d736d8d4c39717c82b69ec50e9677f1

元コミット内容

runtime: add conversion specifier to printf for char values

R=r, golang-dev
CC=golang-dev
https://golang.org/cl/7327053

変更の背景

Go言語のランタイムは、そのパフォーマンスと低レベルな操作の必要性から、C言語で書かれた部分を多く含んでいます。このC言語部分では、デバッグや内部状態の出力のためにprintfのような書式付き出力関数が頻繁に利用されます。

このコミットが作成された2013年2月時点では、Goランタイムの内部printf実装(runtime·vprintf)には、単一の文字を直接出力するための専用の変換指定子が存在しなかったと考えられます。例えば、文字を整数として%dで出力したり、文字列として%sで出力したりすることは可能でしたが、これは文字本来の意味での出力とは異なります。

%c変換指定子を追加することで、ランタイム開発者は文字データをより直感的に、かつ正確にデバッグ出力できるようになります。これは、特に文字コードやバイト列を扱う低レベルな処理において、デバッグの効率と正確性を向上させるために重要な改善です。

前提知識の解説

Goランタイム (Go Runtime)

Goランタイムは、Goプログラムの実行を管理する低レベルなコンポーネント群です。これには、ガベージコレクタ、スケジューラ(ゴルーチンの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goランタイムの一部はGo言語自体で書かれていますが、パフォーマンスが重要であったり、OSとの直接的なやり取りが必要な部分は、C言語やアセンブリ言語で書かれています。このコミットで変更されているsrc/pkg/runtime/print.csrc/pkg/runtime/runtime.hは、まさにそのC言語で書かれたランタイムのコア部分に当たります。

printf関数と変換指定子

printfはC言語の標準ライブラリ関数で、書式文字列(format string)と可変個の引数を受け取り、書式文字列に従って整形されたテキストを出力します。書式文字列には、プレーンなテキストの他に、引数の値をどのように表示するかを指示する「変換指定子(conversion specifier)」が含まれます。

一般的なprintfの変換指定子の例:

  • %d: 整数を10進数で出力
  • %s: 文字列を出力
  • %f: 浮動小数点数を出力
  • %c: 単一の文字を出力

このコミットは、Goランタイム内部で使用されるprintfライクな関数(runtime·vprintf)に、標準C言語のprintfにおける%cと同様の機能を追加するものです。

src/pkg/runtime/print.csrc/pkg/runtime/runtime.h

  • src/pkg/runtime/print.c: Goランタイム内部で使用される低レベルな出力関数(printfのGoランタイム版)の実装が含まれています。これは、Go言語の標準ライブラリのfmtパッケージとは異なり、よりプリミティブなレベルで動作し、デバッグやエラー報告のために使われます。
  • src/pkg/runtime/runtime.h: GoランタイムのC言語部分で使用されるヘッダファイルで、各種関数プロトタイプ、構造体定義、マクロなどが含まれています。print.cで実装される関数の宣言もここに含まれます。

技術的詳細

この変更は、Goランタイムの内部printf実装であるruntime·vprintf関数に、%c変換指定子を追加することで、単一のバイト(文字)を直接出力できるようにします。

runtime·vprintfは、書式文字列を解析し、対応する引数を取得して適切な出力関数にディスパッチする役割を担っています。 変更のポイントは以下の2点です。

  1. 書式文字列の解析部分: runtime·vprintf内で、書式文字列中の%cを見つけた際に、対応する引数を取得するように変更します。
  2. 文字出力関数の追加: 取得した文字(バイト)を実際に標準出力(またはランタイムが設定した出力先)に書き込むための新しいヘルパー関数runtime·printbyteを追加します。

これにより、ランタイムコード内でruntime·printf("Char value: %c\n", someByteValue);のような記述が可能になり、someByteValueが文字として解釈され出力されるようになります。

コアとなるコードの変更箇所

src/pkg/runtime/print.c

--- a/src/pkg/runtime/print.c
+++ b/src/pkg/runtime/print.c
@@ -84,6 +84,7 @@ vprintf(int8 *s, byte *base)
 		tnarg = 0;
 		switch(*p) {
 		case 't':
+		case 'c': // ここで 'c' を 't' と同じグループに追加
 			tnarg = arg + 1;
 			break;
 		case 'd':	// 32-bit
@@ -126,6 +127,9 @@ vprintf(int8 *s, byte *base)
 		case 'a':
 			runtime·printslice(*(Slice*)v);
 			break;
+		case 'c': // ここで 'c' の実際の処理を追加
+			runtime·printbyte(*(int8*)v);
+			break;
 		case 'd':
 			runtime·printint(*(int32*)v);
 			break;
@@ -202,6 +206,12 @@ runtime·printbool(bool v)
 	gwrite((byte*)"false", 5);
 }
 
+void
+runtime·printbyte(int8 c) // 新しいヘルパー関数 runtime·printbyte の定義
+{
+	gwrite(&c, 1); // 単一のバイトを書き出す
+}
+
 void
 runtime·printfloat(float64 v)
 {

src/pkg/runtime/runtime.h

--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -817,6 +817,7 @@ void*\truntime·getcallerpc(void*);\
  * runtime go-called
  */
 void	runtime·printbool(bool);\
+void	runtime·printbyte(int8); // runtime·printbyte の関数プロトタイプ宣言を追加
 void	runtime·printfloat(float64);\
 void	runtime·printint(int64);\
 void	runtime·printiface(Iface);\

コアとなるコードの解説

  1. src/pkg/runtime/print.cvprintf 関数内の変更:

    • switch(*p) ステートメントは、書式文字列の現在の文字(*p)がどの変換指定子であるかを判断します。
    • case 't': の行の下に case 'c': が追加されています。これは、%c指定子が見つかった場合も、%t(おそらく真偽値のbool型)と同様に、次の引数(arg + 1)を処理対象としてマークすることを示しています。
    • さらに下の方にある別の switch(*p) ブロック(実際の値の出力処理を行う部分)に、新しい case 'c': が追加されています。
      • このブロックでは、引数リストから現在の引数vint8*型として解釈し、その値をruntime·printbyte関数に渡しています。*(int8*)vは、ポインタvが指すメモリ位置にあるint8型の値をデリファレンスして取得します。
  2. src/pkg/runtime/print.c に新しい関数 runtime·printbyte の追加:

    • この関数はint8 cを引数として受け取ります。int8はGoランタイムにおける1バイトの符号付き整数型で、文字を表すのに適しています。
    • 関数内部ではgwrite(&c, 1);が呼び出されています。gwriteはGoランタイムの低レベルなI/O関数で、指定されたアドレスから指定されたバイト数だけデータを書き込みます。ここでは、cのアドレスから1バイトだけを書き出すことで、単一の文字を出力しています。
  3. src/pkg/runtime/runtime.h の変更:

    • void runtime·printbyte(int8); という行が追加されています。これは、runtime·printbyte関数のプロトタイプ宣言であり、この関数がint8型の引数を取り、何も返さないことをコンパイラに伝えます。これにより、他のCファイル(print.cを含む)からruntime·printbyteを正しく呼び出すことができるようになります。

これらの変更により、Goランタイムの内部printfは、%c指定子を認識し、対応する引数を単一の文字として解釈し、gwriteを通じて出力する一連の処理フローが確立されました。

関連リンク

参考にした情報源リンク