如果該函數會修改receiver,此時一定要用指針
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:主機域名、網站空間、營銷軟件、網站建設、舒城網站維護、網站推廣。
如果receiver是 struct 并且包含互斥類型 sync.Mutex ,或者是類似的同步變量,receiver必須是指針,這樣可以避免對象拷貝
如果receiver是較大的 struct 或者 array ,使用指針則更加高效。多大才算大?假設struct內所有成員都要作為函數變量傳進去,如果覺得這時數據太多,就是struct太大
如果receiver是 struct , array 或者 slice ,并且其中某個element指向了某個可變量,則這個時候receiver選指針會使代碼的意圖更加明顯
如果receiver使較小的 struct 或者 array ,并且其變量都是些不變量、常量,例如 time.Time ,value receiver更加適合,因為value receiver可以減少需要回收的垃圾量。
以下代碼在VC6.0以上版本測試通過!
輸出結果:6
#include stdio.h
int main(void)
{
int a[2][2] = {{1,2}, {3,4}};
int b[2][2] = {{5,6}, {7,8}};
int (*p1)[2] = a;
int (*p2)[2] = b;
int (*q[2])[2] = {p1, p2}; 這樣才是正確的定義!
printf("%d\n", *(*q[1]+1));
return 0;
}
但在tc2.0和bc3.1中提示非法初始化!
但把
int (*q[2])[2] = {p1, p2};
改成
int (*q[2])[2];
q[0] = p1;
q[1] = p2;
可以通過!
原因暫不清楚,估計是老舊的編譯器不支持太復雜的定義!
其實最好的方法是使用typedef,簡單明了,可讀性大大提升!
#include stdio.h
int main(void)
{
typedef int (*PA)[2]; 使用typedef
int a[2][2] = {{1,2}, {3,4}};
int b[2][2] = {{5,6}, {7,8}};
int (*p1)[2] = a;
int (*p2)[2] = b;
PA q[2]= {p1, p2}; 這樣可讀性是否大大的增加?!
printf("%d\n", *(*q[1]+1));
return 0;
}
tips: *號,可以指向指針類型內存地址上的值,號,可以獲取值類型的內存地址
每一個變量都有內存地址,可以通過變量來操作內存地址中的值,即內存的大小
go語言中獲取變量的內存地址方法:通過 符號可以獲取變量的地址
定義:普通變量存儲的是對應類型的值,這些類型就叫值類型
變量b,在內存中的地址為:0x1040a124,在這個內存地址上存儲的值為:156
定義:指針類型的變量存儲的是?個地址,所以?叫指針類型或引?類型
b 是值類型,它指向的是內存地址上的值
a是指針類型,它指向的是b的內存地址
指針類型定義,語法: var 變量名 *類型
指針類型在定義完成后,默認為空地址,即空指針(nil)
在定義好指針變量后,可以通過***** 符號可以獲取指針變量指向的變量
在這里的 *a 等價于 b,通過修改 *a ,最終修改的是值類型b的值
這里a,d是值類型,b,c是指針類型
d就相當于把a內存地址上值,在內存中從新開辟了一塊空間存儲,d和a互不影響
b,c相當于指向了a的內存地址,當使用*號引用出內存地址上的變量上,修改值得,a的值也會跟著改變
Go語言里面的指針和C++指針一樣,都是指向某塊內存的地址值,可以解引用,不同只是在于C++里可以直接對指針做算術運算而Go里面不行。
不知道你有沒有聽過這么一句:在使用 map 時盡量不要在 big map 中保存指針。好吧,你現(xiàn)在已經聽過了:)為什么呢?原因在于 Go 語言的垃圾回收器會掃描標記 map 中的所有元素,GC 開銷相當大,直接GG。
這兩天在《Mastering Go》中看到 GC 這一章節(jié)里面對比 map 和 slice 在垃圾回收中的效率對比,書中只給出結論沒有說明理由,這我是不能忍的,于是有了這篇學習筆記。扯那么多,Show Your Code
這是一個簡單的測試程序,保存字符串的 map 和 保存整形的 map GC 的效率相差幾十倍,是不是有同學會說明明保存的是 string 哪有指針?這個要說到 Go 語言中 string 的底層實現(xiàn)了,源碼在 src/runtime/string.go里,可以看到 string 其實包含一個指向數據的指針和一個長度字段。注意這里的是否包含指針,包括底層的實現(xiàn)。
Go 語言的 GC 會遞歸遍歷并標記所有可觸達的對象,標記完成之后將所有沒有引用的對象進行清理。掃描到指針就會往下接著尋找,一直到結束。
Go 語言中 map 是基于 數組和鏈表 的數據結構實現(xiàn)的,通過 優(yōu)化的拉鏈法 解決哈希沖突,每個 bucket 可以保存 8 對鍵值,在 8 個鍵值對數據后面有一個 overflow 指針,因為桶中最多只能裝 8 個鍵值對,如果有多余的鍵值對落到了當前桶,那么就需要再構建一個桶(稱為溢出桶),通過 overflow 指針鏈接起來。
因為 overflow 指針的緣故,所以無論 map 保存的是什么,GC 的時候就會把所有的 bmap 掃描一遍,帶來巨大的 GC 開銷。官方 issues 就有關于這個問題的討論, runtime: Large maps cause significant GC pauses #9477
無腦機翻如下:
如果我們有一個map [k] v,其中k和v都不包含指針,并且我們想提高掃描性能,則可以執(zhí)行以下操作。
將“ allOverflow [] unsafe.Pointer”添加到 hmap 并將所有溢出存儲桶存儲在其中。 然后將 bmap 標記為noScan。 這將使掃描非???,因為我們不會掃描任何用戶數據。
實際上,它將有些復雜,因為我們需要從allOverflow中刪除舊的溢出桶。 而且它還會增加 hmap 的大小,因此也可能需要重新整理數據。
最終官方在 hmap 中增加了 overflow 相關字段完成了上面的優(yōu)化,這是具體的 commit 地址。
下面看下具體是如何實現(xiàn)的,源碼基于 go1.15,src/cmd/compile/internal/gc/reflect.go 中
通過注釋可以看出,如果 map 中保存的鍵值都不包含指針(通過 Haspointers 判斷),就使用一個 uintptr 類型代替 bucket 的指針用于溢出桶 overflow 字段,uintptr 類型在 GO 語言中就是個大小可以保存得下指針的整數,不是指針,就相當于實現(xiàn)了 將 bmap 標記為 noScan, GC 的時候就不會遍歷完整個 map 了。隨著不斷的學習,愈發(fā)感慨 GO 語言中很多模塊設計得太精妙了。
差不多說清楚了,能力有限,有不對的地方歡迎留言討論,源碼位置還是問的群里大佬 _