Goの文字列について

投稿日: 2025-05-27 13:44:25

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.Builderbytes.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 からとなります。