Goの文字列は不変で、str1 += str2
のように文字列を連結した場合、str1とは別の領域に連結したテキストを新しく作成しているらしい。ただ、自分で検証してみると文字の連結はされているにも関わらず、アドレスは変わっていない。
→変数のアドレスは変更されないため。
package main
import "fmt"
func main() {
str1 := "Hello, "
str2 := "World"
fmt.Printf("%s %p
", str1, &str1)
str1 += str2
fmt.Printf("%s %p
", str1, &str1)
}
Hello, 0xc000014070
Hello, World 0xc000014070
文字が新しく作成されていることを調べるためには
byte型の配列に変換してその文字自体のアドレスを確認する。文字列はポインタで参照するけど、文字であれば実データがあるため、それを確認することで確認できる
package main
import "fmt"
func main() {
str1 := "Hello"
fmt.Printf("str1 address: %p
", &str1)
fmt.Printf("str1 data address: %p
", []byte(str1))
str1 += " World"
fmt.Printf("str1 address after concatenation: %p
", &str1)
fmt.Printf("str1 data address after concatenation: %p
", []byte(str1))
}
str1 address: 0xc000014070
str1 data address: 0xc000012028
str1 address after concatenation: 0xc000014070
str1 data address after concatenation: 0xc000012070
Goの文字列変数の扱いについて
C言語のポインタのように文字のアドレスをを持っている。 以下のような構造体で構成される
- 文字列データへのポインタ
- 文字数
type stringStruct struct {
str unsafe.Pointer
len int
}
大規模な文字列の連結を行う場合
strings.Builder
やbytes.Buffer
が推奨されている
内部的な挙動としては、バッファをもって一度だけメモリ領域を確保し、新しく文字列を作成する。加算の場合だと呼び出されるたびに文字列が生成されるため、パフォーマンスが悪化する。
range構文について
配列やスライス
基本的には0,1,2のようにインデックスは増加していく。たとえば、intの配列であれば普通に0, 1, 2のようにインデックスが増加していく。
文字列
文字列の場合のインデックスはその文字が開始するバイトオフセットを指す。これはエンコーディングの問題らしい。goではデフォルトでUTF-8でエンコーディングする。UTF-8は可変長エンコーディングという方式をとっており、1バイトから4バイトの範囲で適切なバイト数が割り振られ文字として認識される。 たとえば、日本語は3バイトのUTF-8を使用しているため、3バイトずつ増加していく。英語であれば、ASCII文字を使用しているため、1バイトで出力される。各言語を正しく処理できるようにしてくれてるっぽい。この機能がないと絵文字や日本語などさまざまな言語に対応できなくなる。
package main
import "fmt"
func main() {
str := "こんにちは"
for i, r := range str {
fmt.Printf("インデックス: %d, 文字: %c
", i, r)
}
}
インデックス: 0, 文字: こ
インデックス: 3, 文字: ん
インデックス: 6, 文字: に
インデックス: 9, 文字: ち
インデックス: 12, 文字: は
package main
import "fmt"
func main() {
str := "hello"
for i, r := range str {
fmt.Printf("インデックス: %d, 文字: %c
", i, r)
}
}
インデックス: 0, 文字: h
インデックス: 1, 文字: e
インデックス: 2, 文字: l
インデックス: 3, 文字: l
インデックス: 4, 文字: o
バイトオフセットとは
バイトオフセットは、ある基準点(通常はデータ構造の開始点やメモリブロックの先頭)から、特定のデータまでのバイト数の距離を表します。この基準点からの距離が、それぞれのデータを一意に位置づけるために使われます。
「あ」がUTF-8でエンコードされる場合、実際には3バイトを使用します。もし「あ」が文字列の最初に来る場合、そのバイトオフセットは次のようになります:
- 「あ」の最初のバイトのオフセットは 0
- 次の文字(「あ」の次に続く文字)の開始オフセットは 3
したがって、「あ」が始まるオフセットは 0
であり、その長さは 3
バイトです。これが意味するのは、メモリ上で「あ」を表すデータは、オフセット 0
から 2
(0, 1, 2 の3バイト)までの位置を占めるということです。次の文字が開始されるのはオフセット 3
からとなります。