重學 C 的筆記

學校上過數學系的 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 + 1ld 1, ld 1, add.

變量就是寄存器的地址. 1 + a 是載入 1 和 a 的地址. 基礎類型能夠直接在程序裏用, 不須要內存. ld 1, lda 00001, add

  1. 靜態數據區

存放程序必定會用到的內些固定數據. 好比字符串常量, 但數字能夠直接在程序區用, 而字符串不能出如今程序區.

須要在編譯時能獲得. 運行時動態的數據無法知道.

存放程序函數和局部變量. 棧的大小固定, 通常爲 2m, 從棧空開始, 先進後出的. 是人爲劃分的一塊連續虛擬內存. 操做相似 push pop.

int i = 4 這樣就會進棧 4.

函數執行完畢函數內的棧會被清空, 不一樣的函數之間不會干擾.

{} 表示一個塊級做用域, 其中標記了進棧和清棧的範圍. for 循環的 {} 是有塊級做用域的.

一個進程只有一個棧. 函數執行時, 參數按照逆序先進棧, 函數執行完畢後包括參數一塊兒退棧, 但寫入一個返回值進棧. 逆序是約定爲了讀取方便.

變量賦值會在原先的數據上寫, 而不會從新進棧.

棧是編譯器管理的, 程序員不能處理, 但用 api alloca 能夠申請內存

  1. 堆是徹底給程序控制的, 動態內存

使用 API allocfree.

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 是一回事, 指針使用 ->, 非指針使用 .

相關文章
相關標籤/搜索