學校上過數學系的 C 課程, 上機 VC 操做作課堂做業過, 後來就一直動態類型語言.. 直到學 Go 類型卡住了.. 被學長帶着重學 C, 如下是開頭的筆記:程序員
10 我的的性別信息, 最少用多少 bits 來存儲?編程
實際計算機最小的處理單位是 8bits.api
最少是 16bits, 順序 10bits 的 01 來表示性別.數組
假如還有變性人, 還有每一個人性別位置, 多少位?安全
每一個人 5 種狀況 (5^10)bits,編程語言
> 5 ^ 10 / 8 / 8 / 8 / 8 / 8 / 8 / 8 4.656612873077393
(8*8)bits函數
編程語言裏只能操做四種位數, 8, 15, 32, 64bits. 就算用到只須要 20bits, 仍是會用到 32 bits. 屬於硬件設計範圍.ui
表示 34 個數字, 須要多少 bits, 34 種不一樣的狀況, 只要能區分?編碼
34 個不一樣狀況須要 64bits操作系統
8bits 能表示多少數字?
256, 可以表示 0~255, 非負是 -127~128
-1 的表示是 11111111, -1 會用來表示最大
未知大小的數據, 經過一個字典, 每一個字符串一個須要, 而後按序號取. 空間能夠動態從新申請, 而後獲取地址, 這樣就能夠表示很大的數據.
34bits 的內存不能表示 4G 以上的地址. 由於 32bits 能表示 1024 * 1024 * 4.
存儲 100*32bits 的數據, 先在堆裏動態申請內存, 而後記錄地址, 每次 new 新建對象, 都申請到固定大小的內容, 而後存儲地址, 引用, 指針.
32bits 電腦的指針是 32bits 的, 引用對象的值表示地址. 若是引用對象的值是 0, 那麼就是 null
JS undefined 是有指向內存, 但內存沒有數據, 而 null 是地址都是 0.
系統會提供虛擬地址. 進程的虛擬地址是從 1 開始的, 0 在各類語言裏都是 null.
打印一下 Go 裏的地址...
package main import ( "fmt" ) func main() { a := 1 fmt.Println(&a) }
C 不保存類型的信息.
值類型比引用類型更快, 由於引用類型都會涉及到內存申請. 坑爹的 Python 全是引用類型...
申請的內存要記得釋放.. 或者垃圾回收..
存放程序自己, 彙編代碼, 操做系統管理的內存. 程序不可修改, 能夠讀取. 進程的內存是連續的, 能夠經過計算推測.
存放指令和操做數. 好比 1 + 1
是 ld 1, ld 1, add
.
變量就是寄存器的地址. 1 + a
是載入 1 和 a 的地址. 基礎類型能夠直接在程序裏用, 不須要內存. ld 1, lda 00001, add
存放程序必定會用到的內些固定數據. 好比字符串常量, 但數字能夠直接在程序區用, 而字符串不能出如今程序區.
須要在編譯時能獲得. 運行時動態的數據無法知道.
存放程序函數和局部變量. 棧的大小固定, 通常爲 2m, 從棧空開始, 先進後出的. 是人爲劃分的一塊連續虛擬內存. 操做相似 push pop.
int i = 4
這樣就會進棧 4.
函數執行完畢函數內的棧會被清空, 不一樣的函數之間不會干擾.
{
和 }
表示一個塊級做用域, 其中標記了進棧和清棧的範圍. for 循環的 {
和 }
是有塊級做用域的.
一個進程只有一個棧. 函數執行時, 參數按照逆序先進棧, 函數執行完畢後包括參數一塊兒退棧, 但寫入一個返回值進棧. 逆序是約定爲了讀取方便.
變量賦值會在原先的數據上寫, 而不會從新進棧.
棧是編譯器管理的, 程序員不能處理, 但用 api alloca
能夠申請內存
使用 API alloc
和 free
.
alloc 的參數是申請內存的字節數. 返回值是申請的內存
好比申請字符串 "hello world"
, 代碼是:
char[] c = alloc(11*2)
這時 char 類型的長度對應是 2, 採用 utf16 編碼. 而後進行賦值:
cc[0] = 'h'; cc[1] = 'e';
而後拷貝字符串:
char[] c1 =alloc (11*2) cc[0] = 'h'; cc[1] = 'e'; // ... char[] c2 =alloc (11*2) strncpy(c2, 11, c1)
最後釋放內存:
free(c1); free(c2);
因此字符串拼接很慢.. 涉及到建立和拷貝.
拷貝字符串就...
char[] r1="ss" char[] r2="ss" char[] r3 = alloc (2 * (strlen(r1) + strlen(r2))) strcopy(r3, strlen(r1), r1) strcopy(r3 + strlen(r1), strlen(r1), r2)
申請一段內存, 而後擴展長度的僞代碼:
char[] a = alloc (2) char[] b = alloc (6) strncpy(b, 2, a) free(a) a = b
更通用的 API memcpy(dest,size,src)
如何遍歷輸出當前函數下全部變量的值?
思路, 在開頭結尾取內存地址, 而後遍歷:
int* start =alloca(4) int* end =alloca(4)
指針能夠加減進行偏移, 根據不一樣的類型不一樣大小的便宜:
int 類型的移動 4bits, char 移動是 2bits.
沒有具體類型的 void* 不能移動, 由於沒有大小信息.
char 可能有多種長度, wchar_t.
經常使用的長度和類型的對應:
8 bool byte sbyte 16 char short ushort 32 int float uint 64 long double ulong ? void object string
單位是 bits.
字符串表示有 ASSIC, UTF8, UTF16 多種類型不一樣的長度.
clang 使用, clang code.c
對代碼進行編譯.
unsigned int 在 C 裏能夠賦值 -1, 而不會報錯; 對應 Go 的 uint 賦值 -1 將會報錯.
由於 C 沒有作多餘的檢查, 對應機器真實的操做.
寫盜號是經過分析內存中數據的位置, 推算變量佔用的空間, 經過手動操做指針獲取數據獲得的.
安全的編程語言會關閉這種可能性.
經過指針實現多返回值.
Point p = {1, 2, 3};
Struct 類型, 數據是存在棧上的, 由於 p 是變量, 變量是存放在棧上的.
這裏的 p 對應數據長度固定, 整個都是存在放棧上的.
Stuct 相似數組, 名稱很大程度是語法糖, *pb
就是 pb[0]
p.x
和 (&p)->x
是一回事, 指針使用 ->
, 非指針使用 .