Beginner problems with pointers

agolangf · · 461 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hello all.</p> <p>To be honest, I feel bad to ask here that beginner problem (this subreddit looks like adressed to rather mature programmers than to beginners) - but I&#39;m unable to find these information elsewhere. </p> <p>After A Tour of Go, some books, and hours googling &amp; trying I still don&#39;t understand some pointer&#39;s essentials. </p> <p>Cutting to the chase, I&#39;m trying to change field of global struct. Unfortunately, most of examples are about using structs locally and I have problems to translate it for global usage. Also, I need to pass struct by some functions.</p> <p>I create struct in this way</p> <pre><code>type Place struct { name string x, y int } var Bromberg Place var Thorn Place func InitPlace() { Bromberg = Place{&#34;Bromberg&#34;, 0, 0} Thorn = Place{&#34;Thorn&#34;, 0, 0} } </code></pre> <p>then I&#39;m passing it by some function then try to change x of specific city</p> <pre><code>function ChooseCity variable city type-Place if random(100) &lt;= 50 city is Bromberg else city is Thorn run function do_something_else_with_city with argument city func do_something_else_with_city with argument city do_something run function ChangeX with argument city function ChangeX with argument city change city.x </code></pre> <p>In that case, do I need to pass argument with pointer every time? </p> <p>I was trying to use receiver <a href="http://stackoverflow.com/questions/11810218/how-to-set-and-get-fields-in-golang-structs">as here</a>, and simply use example from A Tour of Go, but every time change of x was temporary. How should I deal with it?</p> <p>Respectfully, Pors</p> <hr/>**评论:**<br/><br/>PsyWolf: <pre><p>I wanna reply to one specific part of your question that no one seems to have covered.</p> <blockquote> <p>In that case, do I need to pass argument with pointer every time? </p> </blockquote> <p>Yes you do, but not explicitly. Check out <a href="https://play.golang.org/p/lylFAxW6Tb" rel="nofollow">https://play.golang.org/p/lylFAxW6Tb</a></p> <p>In well written go, most variables in your program fall into 1 of 2 categories</p> <ul> <li>Always used as a value</li> <li>Always used as a pointer </li> </ul> <p>It&#39;s relatively rare that you actually need to treat a variable sometimes as a value and other times as a pointer. By declaring your variable the right way up front, you won&#39;t have to use &amp;s and *s everywhere it get it to compile. </p></pre>SomeoneUnsuspicious: <pre><p>For now I&#39;m going to stick with all these &amp; and * to get more used to this mechanics, but in general it seems really useful. No problems if some structs are assigned to one type?</p></pre>sicueft: <pre><p>I think the &amp; and * are confusing because whenever people are talking about those <em>operators</em>, they use terminology without explaining the terminology and how the positions of the symbols matters. For instance, what does (var p *int) even mean to someone who doesn&#39;t know how memory is allocated in a program? It should really be explained step by step in the simplest way possible, so I&#39;ll try with some examples. Lets start with:</p> <p>var p *int : this means that variable p <em>is</em> a pointer <em>to</em> a value of the datatype int. </p> <p>a := p : this would initialize a variable called &#34;a&#34; that is a pointer <em>of</em> the same value as p <em>to</em> a value of datatype int. So pointers are values in this way and they hold the <em>memory address</em> of whatever value they&#39;re referencing. &#34;a&#34; and &#34;p&#34; would be two variables in two <em>different</em> address spaces but would <em>hold</em> that same value. Every new variable you create has its own address space. If you want to refer to a variable using only one address space, don&#39;t declare any new variables!</p> <p>*p : this is very different from (var p *int) because notice the position of the (*) and <em>this is simply syntax</em>. In fact, it&#39;s confusing because this is <em>not</em> a pointer. It&#39;s called <em>dereferencing</em> (because pointers <em>reference</em>) and it gets that value of datatype int instead of returning the address which would just be plain &#34;p&#34;. In this case, the output would be &lt;nil&gt; because we haven&#39;t pointed &#34;p&#34; to anything.</p> <p>&amp;p : this tells you the memory address of the variable &#34;p&#34; that was instantiated in this program.</p> <p>*p = 1 : this would <em>throw an error</em> because &#34;p&#34; is not pointing to anything so you can&#39;t set the value of a nonexistent variable to a value. So instead declare a variable of int value first and then point p to it. (z := 1) then, (p = &amp;z). Then you can change the value of &#34;z&#34; through &#34;p&#34; like (*p = 2) then &#34;z&#34; will equal 2.</p> <p>b := *p : this would be how you <em>get</em> (rather than <em>set</em>) the value of datatype int that p is pointing to. (*p) is the value that is at the address value &#34;p&#34;. Again, (*p) is <em>not</em> a pointer and neither is &#34;b&#34;. They are the values <em>of</em> the pointer &#34;p&#34; (which is an instantiated variable), in this case &#34;z&#34;.</p> <p>c := &amp;b (==) c:= &amp;(*p) (==) c:= p : this would initialize a variable &#34;c&#34; that <em>gets</em> the <em>memory address</em> of the value &#34;b&#34; (or *p) and this <em>is</em> a pointer because the value that &#34;c&#34; holds is a <em>memory address</em>. &amp;b is <em>not</em> a pointer, the variable &#34;c&#34; is the pointer. Remember to distinguish <em>operations</em> from <em>variables</em> and <em>values</em>, which are all different things.</p> <p>d := &amp;c (==) d := &amp;(&amp;(*p)) : d is a pointer <em>of</em> a pointer. It holds the memory location of the variable c which holds the memory location of &#34;b&#34; which is a value of datatype int. </p> <p>e := &amp;(&amp;c) : this would <em>throw an error</em> because &amp;c <em>isn&#39;t</em> a variable with a memory address so you can&#39;t get an address for it. Remember that &#34;d&#34; and &#34;&amp;c&#34; are two <em>different</em> things. e := &amp;(d) would work just fine because &#34;d&#34; has an address somewhere because it was initialized in the program.</p> <p>f := *(&amp;c) : &#34;f&#34; would be the value of the address space of the pointer &#34;b&#34;. Why is that? (&amp;c) gets you the address for &#34;c&#34; and &#34;c&#34; is a pointer to &#34;b&#34; from (c := &amp;b), and <em>not</em> (c:= &amp;(*p)) since (*p) is an operation and not a variable. That means if we deference (&amp;c) we get the value in the address space <em>of</em> &#34;c&#34; which is the address for &#34;b&#34; (c := &amp;b).</p></pre>civeng12: <pre><p>This really helped me, thanks!</p></pre>sicueft: <pre><p>No problem, always glad to teach and learn.</p></pre>PsyWolf: <pre><blockquote> <p>No problems if some structs are assigned to one type? </p> </blockquote> <p>I don&#39;t understand this question. Can you clarify?</p></pre>SomeoneUnsuspicious: <pre><p>I figured it out. Silly me, I&#39;d say :D I was mislead because * is next to type, and I thought that declaring var Thorn *Place could overwrite Place for whole eternity.</p></pre>mrfrobozz: <pre><p>I&#39;m glad that you got some good help here, but if you do want to, the <a href="https://blog.gopheracademy.com/gophers-slack-community/" rel="nofollow">Golang Slack</a> has a newbies channel to help with beginners. I&#39;ve found excellent help there in the past.</p></pre>divan0: <pre><p>Hi,</p> <p>so imagine your variables as a real world objects - you have two: Bromberg and Thorn. Also, imagine two fingers, one is pointing to the Bromberg, another pointing to the Thorn - that&#39;s your pointers.</p> <p>Now, if you call a function with parameter, Go will copy the parameter, so only function can work with it. If you pass the &#34;real object&#34; - Go will copy it, so changing x will mean changing x of the copied object. Instead, if you pass a pointer, it will be copied as well, but still will point to the right object, which you will modify.</p> <p>Sorry for that simplistic explanation, but that&#39;s how it essentially is. Does that make sense?</p></pre>SomeoneUnsuspicious: <pre><p>It does make sense, thank you for explaination. Altought I know theory already. I have more problems with pointers syntax - for example, I&#39;m not sure why sometimes I need use just &amp; (as for scanning input) but sometimes I need use both &amp; and *. Doesn&#39;t scanned input need to check for memory adress? Theory basics sounds simple, but differences in using it in different cases are not clear for me.</p></pre>chewxy: <pre><ul> <li><code>&amp;x</code> is the operation to get the address of the variable <code>x</code></li> <li><code>var x *foo</code> is a declaration that <code>x</code> is a pointer to <code>foo</code></li> <li><code>a = *x</code> is an operation to dereference <code>x</code></li> </ul> <p>A majority of the problem is the overloaded semantics of the <code>*</code> symbol, really.</p> <p>Let&#39;s look at a real life code:</p> <pre><code>var buf bytes.Buffer fmt.Fprintf(&amp;buf, &#34;hello %v&#34;, &#34;someone unsuspicious&#34;) </code></pre> <p>Here we declare <code>buf</code> to be a <code>bytes.Buffer</code> in the first line. In the second line, we want to pass this <code>buf</code> to <code>fmt.Fprintf</code> which takes a <code>*bytes.Buffer</code> (it actually takes a <code>io.Writer</code> which only <code>*bytes.Buffer</code> fulfils, not <code>bytes.Buffer</code>, but we&#39;ll just say it for simplicity). </p> <p>How do we pass a pointer to the <code>bytes.Buffer</code> object? We take the address of the variable <code>buf</code>, by using <code>&amp;buf</code>. </p> <p>Can we do something like this?</p> <pre><code>var buf *bytes.Buffer = new(bytes.Buffer) fmt.Fprintf(buf, &#34;Blah&#34;) </code></pre> <p>Yes we can. I&#39;ll leave it to you to figure out the differences</p></pre>yourewelcome_bot: <pre><p>You&#39;re welcome.</p></pre>LadyDascalie: <pre><p>Check this out: <a href="https://play.golang.org/p/WBylHTnl7g" rel="nofollow">https://play.golang.org/p/WBylHTnl7g</a></p> <p>Basically, when passing the city as an argument, you&#39;re passing in a copy of it to said function, so you&#39;re only ever working on that copy, within the limited scope of that function.</p> <p>By using the <code>&amp;</code> operator instead, you&#39;re telling the function to work on the original struct you&#39;re referring to, therefore, your changes stick!</p> <p>Does that make sense? It&#39;s probably badly explained, but hopefully that can help you understand it</p> <p>EDIT: <a href="https://play.golang.org/p/fIUXRuJ80a" rel="nofollow">https://play.golang.org/p/fIUXRuJ80a</a> This should let you see exactly what I mean, I&#39;m printing the addresses of the structs that get passed in and used by the functions</p></pre>SomeoneUnsuspicious: <pre><p>Great explaination, it made pointers more understand-able for me, thank you. Still I&#39;m not sure if everything is clear for me, but just tinkered with some samples I found problematic and I can solve it, so - I suppose I get basics finally. Thank you very, very much</p></pre>LadyDascalie: <pre><p>You are very welcome!</p></pre>gac_web: <pre><p>Pointers are values like structs or numbers. In fact, pointers are integers.</p> <p>But they have a special functionality: they are numbers that refer to another value. They are an address, like 0xe4a5 or 0x2af6 and go is aware of that. When you pass any value to a function, that value is copied, NO EXCEPTION. If you pass a struct value to a function and you change the value inside the function, you change a copy of the struct value and not the struct value itself.</p> <pre><code>foo := SomeStruct{x:1} add1toX(foo) // func(f SomeStruct) // foo.x is still 1 </code></pre> <p>In order to change the struct value outside the function you need to pass a pointer to that struct instead of the struct itself. The value of the pointer is the address of the struct in your computer&#39;s memory, ( 0x5a9b17 for instance ) That way Go knows that you are referring to the outer struct and not a copy and when you do f.x = f.x + 1 , go will know that f is a pointer that points to SomeStruct foo and has a field x. It is called de-referencing.</p> <pre><code>foo := SomeStruct{x:1} add1toX(&amp;foo) // func(f *SomeStruct) // foo.x is now 2 </code></pre> <p>You can also declare a pointer to struct directly</p> <pre><code>foo := &amp;SomeStruct{x:1} add1toX(foo) // func(f *SomeStruct) // foo.x is now 2 </code></pre> <p>Remember that any time you have an operation involving changing a value inside a function call, you need to pass a pointer to that value and not the value directly, or the value will just be copied and any change on the copy won&#39;t affect the original value.</p> <p>So all you need to remember is that : </p> <ul> <li><p>pointers are integers. </p></li> <li><p>when a value is passed to a function that value IS ALWAYS COPIED. and any modification inside the function will not affect the original value</p></li> <li><p>in order to refer to the same value inside the function and outside the function, a pointer to that value needs to be passed to the function instead of the value itself. </p></li> </ul> <p>So most of the time &amp; means &#34;take the address of that variable&#34; and reference this address instead of that variable directly.</p> <p>But when you write a function in Go, you need to use * which is means : accept a pointer of something as an argument.</p> <p>so &amp; is for getting a pointer of something , * is for accepting a pointer of something. Now imagine you want to replace an integer inside a function.</p> <p><a href="https://play.golang.org/p/PsvJ5QJJ2v" rel="nofollow">https://play.golang.org/p/PsvJ5QJJ2v</a></p> <pre><code> a := 1 // a is an integer value func(integerPtr *int){ // integerPtr is a pointer to an integer // replacing 1 with 10 *integerPtr = 10 }(&amp;a) // take and pass the address of a // a is now 10 </code></pre> <p>This is the only time when you need to use * ouside a function signature. it means replace the value of whatever pointer with another value.</p></pre>beknowly: <pre><blockquote> <p>I was trying to use receiver as here, and simply use example from A Tour of Go, but every time change of x was temporary. How should I deal with it?</p> </blockquote> <p><a href="https://play.golang.org/p/HUqBoAPhRU" rel="nofollow">https://play.golang.org/p/HUqBoAPhRU</a></p> <p>Most of the time, you should default to using pointer receivers in Go, unless you specifically need a value receiver. This isn&#39;t a hard rule, but is handy to keep in mind.</p> <p>Value receiver:</p> <pre><code>func (p Place) updateFieldValue() { // p is changed _only_ here in the function, // it will not persist after returning p.city = &#34;seattle&#34; } </code></pre> <p>Pointer receiver:</p> <pre><code>func (p *Place) updateFieldPointer() { // because p is a pointer to a Place, changes to p // persist after returning p.city = &#34;chicago&#34; } </code></pre> <p>Calling the method looks exactly the same for both pointer and value receivers. No special syntax required.</p> <pre><code>var thePlace Place thePlace.city = &#34;denver&#34; thePlace.updateFieldValue() // thePlace.city is still &#34;denver&#34; thePlace.updateFieldPointer() // thePlace.city changed to &#34;chicago&#34; </code></pre></pre>

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

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