Aren't strings in Go, immutable?

blov · · 483 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>This was pointed out by a friend of mine, though I never got the time into actually sit and find out. So I thought I could take the easy way out, ask you guys.</p> <pre><code>package main import &#34;fmt&#34; func main() { a := &#34;hello&#34; fmt.Println(&amp;a) a = a + &#34; there&#34; fmt.Println(&amp;a) a = &#34;world&#34; fmt.Println(&amp;a) } </code></pre> <p>This seemingly simple code, produces the following results (on golang.org):</p> <pre><code>0x1040a120 0x1040a120 0x1040a120 Program exited. </code></pre> <p>Could someone please help me understand why all 3 of them have the same address (despite <a href="https://golang.org/ref/spec#String_types">string being immutable in Go</a>)? Is it because of some clever memory allocation during run-time?</p> <p>P.S: This whole thing might have arose from my misunderstanding of what <em>Immutable</em> is. Please help me understand if that&#39;s the case too.</p> <hr/>**评论:**<br/><br/>pobody: <pre><p>The actual strings, like &#34;hello&#34; and &#34;world&#34; are immutable. But all that means is somewhere in memory the string &#34;hello&#34; exists and Go will not change the contents of that memory location.</p> <p>Strings are slices of bytes. But you can&#39;t access the underlying array directly like you could a typical slice. The string type is abstracting this away for you. In other words, when you ask for <code>&amp;a</code>, you are getting the address of another pointer, not the address of the start of the actual data &#34;hello&#34;.</p> <p>One way to see this is to look at a subslice: <a href="https://play.golang.org/p/KWccuJsFpj">https://play.golang.org/p/KWccuJsFpj</a></p> <p>Note how <code>b</code> (a subslice of <code>a</code>) doesn&#39;t change even though <code>a</code> does. <code>b</code> is pointing to a subslice of the string &#34;hello&#34;, which <code>a</code> <em>used</em> to also point to, but no longer does. </p> <p>For another way to look at this, see the &#34;Strings&#34; section of <a href="http://research.swtch.com/godata">http://research.swtch.com/godata</a>.</p></pre>kamaleshbn: <pre><p>Awesome, that was crystal clear, thank you! :)</p></pre>uncle_bad_touches: <pre><p>You are printing out the address of the string header on the stack (see <a href="https://golang.org/pkg/reflect/#StringHeader">https://golang.org/pkg/reflect/#StringHeader</a>). What you want to look at is reflect.StringHeader.Data where the actual contents of the string is stored.</p></pre>materialdesigner: <pre><p><a href="https://groups.google.com/forum/m/#!topic/golang-nuts/EXvylZB6ILk">https://groups.google.com/forum/m/#!topic/golang-nuts/EXvylZB6ILk</a></p> <p>Tl;dr string variables are mutable, but string <em>values</em> (I.e. The array backing the string) is immutable</p></pre>tv64738: <pre><p><code>42</code> is immutable too. <a href="https://play.golang.org/p/USGq-LOjtK" rel="nofollow">https://play.golang.org/p/USGq-LOjtK</a></p> <pre><code>package main import &#34;fmt&#34; func main() { a := 42 fmt.Println(&amp;a) a = a + 13 fmt.Println(&amp;a) a = 7 fmt.Println(&amp;a) } </code></pre> <p>Yet the variable stays in the same memory location, the same way:</p> <pre><code>0x1040e0f8 0x1040e0f8 0x1040e0f8 </code></pre></pre>kamaleshbn: <pre><p>Umm, <code>int</code> is <em>mutable</em>, and what you have here is the expected output.</p> <p>P.S: Number types are mutable in almost all programming languages (as far as I know), and the reason is very simple, when you create a <code>number</code> type variable, it&#39;s allocated a fixed memory based on the number type you choose (int, int64, float, uint etc.), i.e. it takes the same amount of memory to save <code>1</code> or <code>999999</code>. And changing the value of the number would <em>overwrite</em> on the same memory location as it was initially. Strings on the other hand, occupy 1 byte per character, and they are variable in size, so the compiler cannot allocate a set memory size beforehand.</p></pre>wot-teh-phuck: <pre><blockquote> <p>occupy 1 byte per character,</p> </blockquote> <p>Err, that&#39;s not the case assuming by character you mean a &#34;visual character&#34;.</p></pre>kamaleshbn: <pre><p>You&#39;re absolutely right, <em>unfortunately</em> I just think of alphabets when I talk <em>string</em>. And by <em>character</em> I just mean 1 alphabet. I know this is wrong, considering a lot of characters occupy 2+ bytes. Sorry again, it&#39;s just a weird habit I think.</p></pre>tv64738: <pre><p>I think what you&#39;re trying to say there is that variables are variable. That includes string variables. And int.</p></pre>TheMerovius: <pre><p>Because a string is, essentially, a struct that holds a pointer and a length. This is necessarily so, so that the compiler knows the size of a <code>string</code> at compile time (e.g. when passed as arguments) and it makes slicing more efficient, because two slices can share memory, but have differing lengths.</p> <p>Assigning anything to it, overwrites that struct, but it won&#39;t mutate the memory that string is pointing to. That is, what is meant when we say &#34;strings are immutable&#34;.</p> <p>When you look at <code>&amp;a</code>, you are taking the address of that struct. You can also unpack the address of the <a href="https://play.golang.org/p/U8kIn3Xn4y">underlying memory</a> to see how this is working.</p> <p>A different way to look at it, is that <code>a</code> is not a <code>string</code>, but a <code>string</code> <em>variable</em>. By doing <code>a := &#34;hello&#34;</code> you are saying &#34;declare a variable with name <code>a</code> of type <code>string</code> and store the string <code>&#34;hello&#34;</code> in it&#34;. The string remains immutable, but the variable is mutable (or indeed… variable :) which is why they are called that).</p> <p>Contrast that with <code>[]byte</code>. You can store a <code>[]byte</code> in a variable, but you can not only reassign the variable, you can also change the memory that <code>[]byte</code> points at (and thus also influence all the variables that have the same <code>[]byte</code> stored in it), so to speak.</p></pre>ultra_brite: <pre><p>Your biggest problem is that you don&#39;t understand what a pointer is. I would study this at first place if I were you. I pointer is just an address, it has nothing to do with the value of a variable or how it is stored.</p></pre>BraveNewCurrency: <pre><blockquote> <p>Your biggest problem is that you don&#39;t understand what a pointer is.</p> </blockquote> <p><em>No.</em> I have dozens of years experience in C and kernel programming. I do, in fact, know what a pointer is. But I was just as confused about the output as the OP.</p> <blockquote> <p>pointer is just an address, it has nothing to do with the value of a variable or how it is stored.</p> </blockquote> <p>That is correct, but meaningless in this context. It turns out that the variable <code>a</code> does NOT point to the string, but to an intermediate structure that eventually points to the string. The extra level of indirection is <em>specific to Go</em>, so people can be familiar with pointers but still not be magically know why this program does what it does.</p> <blockquote> <p>I would study this at first place if I were you.</p> </blockquote> <p>I downvoted your post for being wrong and coming across as condescending. This problem had <em>nothing</em> to do with understanding pointers.</p> <p>(P.S. We would say the above phrase as &#34;I would study this first, if I were you&#34;. The phrase &#34;First Place&#34; is mostly used for winning a competition, although sometimes it can be used like this: &#34;You should have known that in the first place&#34;.)</p></pre>SteveMcQwark: <pre><blockquote> <p>That is correct, but meaningless in this context. It turns out that the variable <code>a</code> does NOT point to the string, but to an intermediate structure that eventually points to the string. The extra level of indirection is <em>specific to Go</em>, so people can be familiar with pointers but still not be magically know why this program does what it does.</p> </blockquote> <p>I&#39;m not sure it&#39;s useful to think of the variable itself as pointing to something. The variable <em>holds</em> a structure which is the string value itself, which <em>points to</em> the character data. There&#39;s one level of indirection, which is the same as in most variable length string representations (aside from short string optimizations and such). <code>const char*</code> in C has the same amount of indirection, and the same mutability characteristics. You can reassign the <code>string</code> variable as much as you want, you just can&#39;t mutate the character data pointed to by a particular string value. And reassigning the variable doesn&#39;t change its address, which is what the OP was observing.</p> <p>The important thing to remember with Go is that there&#39;s nothing like C++ references going on. While we can call strings a reference type, they aren&#39;t implicitly dereferenced. The reference itself is a first class value, so taking the address of the string takes the address of the reference and not of the backing data.</p></pre>ultra_brite: <pre><p>lol butt hurt? you&#39;re so smart you can&#39;t even take 30 minutes to read Go specification ? If you did you wouldn&#39;t be confuse about what a pointer is in Go. 12 years of experience in this or that is meaningless when you can&#39;t understand the difference between Go and C at first place.</p></pre>BraveNewCurrency: <pre><blockquote> <p>lol butt hurt?</p> </blockquote> <p>No, I was merely pointing out that &#34;knowing pointers&#34; does not explain the original program.</p> <blockquote> <p>you&#39;re so smart you can&#39;t even take 30 minutes to read Go specification ?</p> </blockquote> <p>I did. And I just looked again. I didn&#39;t see any explanation that &#34;taking the value of a string give you a pointer to a struct, not the string data itself.&#34; Am I missing something or did you not &#34;take 30 minutes&#34;?</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

483 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传