那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

計(jì)算機(jī)是如何從內(nèi)存中進(jìn)行取指的

machao1680 ? 來源:21ic ? 作者:21ic ? 2022-10-19 09:27 ? 次閱讀

指針對(duì)于C來說太重要。然而,想要全面理解指針,除了要對(duì)C語言有熟練的掌握外,還要有計(jì)算機(jī)硬件以及操作系統(tǒng)等方方面面的基本知識(shí)。所以本文盡可能的通過一篇文章完全講解指針。

為什么需要指針?

指針解決了一些編程中基本的問題。

第一,指針的使用使得不同區(qū)域的代碼可以輕易的共享內(nèi)存數(shù)據(jù)。當(dāng)然小伙伴們也可以通過數(shù)據(jù)的復(fù)制達(dá)到相同的效果,但是這樣往往效率不太好。

因?yàn)橹T如結(jié)構(gòu)體等大型數(shù)據(jù),占用的字節(jié)數(shù)多,復(fù)制很消耗性能。

但使用指針就可以很好的避免這個(gè)問題,因?yàn)槿魏晤愋偷闹羔樥加玫淖止?jié)數(shù)都是一樣的(根據(jù)平臺(tái)不同,有4字節(jié)或者8字節(jié)或者其他可能)。

第二,指針使得一些復(fù)雜的鏈接性的數(shù)據(jù)結(jié)構(gòu)的構(gòu)建成為可能,比如鏈表,鏈?zhǔn)蕉鏄涞鹊取?/p>

第三,有些操作必須使用指針。如操作申請(qǐng)的堆內(nèi)存。

還有:C語言中的一切函數(shù)調(diào)用中,值傳遞都是“按值傳遞”的。

如果我們要在函數(shù)中修改被傳遞過來的對(duì)象,就必須通過這個(gè)對(duì)象的指針來完成。

計(jì)算機(jī)是如何從內(nèi)存中進(jìn)行取指的?

計(jì)算機(jī)的總線可以分為3種:數(shù)據(jù)總線,地址總線和控制總線。這里不對(duì)控制總線進(jìn)行描述。數(shù)據(jù)總線用于進(jìn)行數(shù)據(jù)信息傳送。數(shù)據(jù)總線的位數(shù)一般與CPU的字長一致。一般而言,數(shù)據(jù)總線的位數(shù)跟當(dāng)前機(jī)器int值的長度相等。例如在16位機(jī)器上,int的長度是16bit,32位機(jī)器則是32bit。這個(gè)計(jì)算機(jī)一條指令最多能夠讀取或者存取的數(shù)據(jù)長度。大于這個(gè)值,計(jì)算機(jī)將進(jìn)行多次訪問。這也就是我們說的64位機(jī)器進(jìn)行64位數(shù)據(jù)運(yùn)算的效率比32位要高的原因,因?yàn)?2位機(jī)要進(jìn)行兩次取指和運(yùn)行,而64位機(jī)卻只需要一次!
地址總線專門用于尋址,CPU通過該地址進(jìn)行數(shù)據(jù)的訪問,然后把處于該地址處的數(shù)據(jù)通過數(shù)據(jù)總線進(jìn)行傳送,傳送的長度就是數(shù)據(jù)總線的位數(shù)。地址總線的位數(shù)決定了CPU可直接尋址的內(nèi)存空間大小,比如CPU總線長32位,其最大的直接尋址空間長232KB,也就是4G。這也就是我們常說的32位CPU最大支持的內(nèi)存上限為4G(當(dāng)然,實(shí)際上支持不到這個(gè)值,因?yàn)橐徊糠謱ぶ房臻g會(huì)被映射到外部的一些IO設(shè)備和虛擬內(nèi)存上。現(xiàn)在通過一些新的技術(shù),可以使32位機(jī)支持4G以上內(nèi)存,但這個(gè)不在這里的討論范圍內(nèi))。 一般而言,計(jì)算機(jī)的地址總線和數(shù)據(jù)總線的寬度是一樣的,我們說32位的CPU,數(shù)據(jù)總線和地址總線的寬度都是32位。

計(jì)算機(jī)訪問某個(gè)數(shù)據(jù)的時(shí)候,首先要通過地址總線傳送數(shù)據(jù)存儲(chǔ)或者讀取的位置,然后在通過數(shù)據(jù)總線傳送需要存儲(chǔ)或者讀取的數(shù)據(jù)。一般地,int整型的位數(shù)等于數(shù)據(jù)總線的寬度,指針的位數(shù)等于地址總線的寬度。 計(jì)算機(jī)的基本訪問單元 學(xué)過C語言的人都知道,C語言的基本數(shù)據(jù)類型中,就屬char的位數(shù)最小,是8位。我們可以認(rèn)為計(jì)算機(jī)以8位,即1個(gè)字節(jié)為基本訪問單元。小于一個(gè)字節(jié)的數(shù)據(jù),必須通過位操作來進(jìn)行訪問。 內(nèi)存訪問方式

如圖1所示,計(jì)算機(jī)在進(jìn)行數(shù)據(jù)訪問的時(shí)候,是以字節(jié)為基本單元進(jìn)行訪問的,所以可以認(rèn)為,計(jì)算每次都是從第p個(gè)字節(jié)開始訪問的。訪問的長度將由編譯器根據(jù)實(shí)際類型進(jìn)行計(jì)算,這在后面將會(huì)進(jìn)行講述。 85c610d2-4f42-11ed-a3b6-dac502259ad0.png 內(nèi)存訪問方式 ? 想要了解更多,就去翻閱計(jì)算機(jī)組成原理和編譯原理吧。 ? sizeof關(guān)鍵字 sizeof關(guān)鍵字是編譯器用來計(jì)算某些類型的數(shù)據(jù)的長度的,以字節(jié)為基本單位。例如:

sizeof(char)=1;     
sizeof(int)=4;

sizeof(Type)的值是在編譯的時(shí)候就計(jì)算出來了的,可以認(rèn)為這是一個(gè)常量!

指針是什么?

我們知道:C語言中的數(shù)組是指一類類型,數(shù)組具體區(qū)分為 int 類型數(shù)組,double類型數(shù)組,char數(shù)組 等等。

同樣指針這個(gè)概念也泛指一類數(shù)據(jù)類型,int指針類型,double指針類型,char指針類型等等。

通常,我們用int類型保存一些整型的數(shù)據(jù),如 int num = 97 , 我們也會(huì)用char來存儲(chǔ)字符:char ch = 'a'。

我們也必須知道:任何程序數(shù)據(jù)載入內(nèi)存后,在內(nèi)存都有他們的地址,這就是指針。

而為了保存一個(gè)數(shù)據(jù)在內(nèi)存中的地址,我們就需要指針變量。

因此:指針是程序數(shù)據(jù)在內(nèi)存中的地址,而指針變量是用來保存這些地址的變量。

85e288c0-4f42-11ed-a3b6-dac502259ad0.png

在我個(gè)人的理解中,可以將指針理解成int整型,只不過它存放的數(shù)據(jù)是內(nèi)存地址,而不是普通數(shù)據(jù),我們通過這個(gè)地址值進(jìn)行數(shù)據(jù)的訪問,假設(shè)它的是p,意思就是該數(shù)據(jù)存放位置為內(nèi)存的第p個(gè)字節(jié)。

當(dāng)然,我們不能像對(duì)int類型的數(shù)據(jù)那樣進(jìn)行各種加減乘除操作,這是編譯器不允許的,因?yàn)檫@樣錯(cuò)是非常危險(xiǎn)的!

圖2就是對(duì)指針的描述,指針的值是數(shù)據(jù)存放地址,因此,我們說,指針指向數(shù)據(jù)的存放位置。

85ef39f8-4f42-11ed-a3b6-dac502259ad0.png

指針的長度

我們使用這樣的方式來定義一個(gè)指針:

Type *p;
我們說p是指向type類型的指針,type可以是任意類型,除了可以是char,short, int, long等基本類型外,還可以是指針類型,例如int *, int **, 或者更多級(jí)的指針,也可是是結(jié)構(gòu)體,類或者函數(shù)等。于是,我們說:
int * 是指向int類型的指針; int **,也即(int *) *,是指向int *類型的指針,也就是指向指針的指針; int ***,也即(int **) *,是指向int**類型的指針,也就是指向指針的指針的指針; …我想你應(yīng)該懂了
struct xxx *,是指向struct xxx類型的指針;
其實(shí),說這么多,只是希望大家在看到指針的時(shí)候,不要被int ***這樣的東西嚇到,就像前面說的,指針就是指向某種類型的指針,我們只看最后一個(gè)*號(hào),前面的只不過是type類型罷了。
細(xì)心一點(diǎn)的人應(yīng)該發(fā)現(xiàn)了,在“什么是指針”這一小節(jié)當(dāng)中,已經(jīng)表明了:指針的長度跟CPU的位數(shù)相等,大部分的CPU是32位的,因此我們說,指針的長度是32bit,也就是4個(gè)字節(jié)!注意:任意指針的長度都是4個(gè)字節(jié),不管是什么指針!(當(dāng)然64位機(jī)自己去測一下,應(yīng)該是8個(gè)字節(jié)吧。。。)
于是:
Type *p;
izeof(p)的值是4,Type可以是任意類型,char,int, long, struct, class, int **…
以后大家看到什么sizeof(char*), sizeof(int *),sizeof(xxx *),不要理會(huì),統(tǒng)統(tǒng)寫4,只要是指針,長度就是4個(gè)字節(jié),絕對(duì)不要被type類型迷惑!

為什么程序中的數(shù)據(jù)會(huì)有自己的地址?

弄清這個(gè)問題我們需要從操作系統(tǒng)的角度去認(rèn)知內(nèi)存。

電腦維修師傅眼中的內(nèi)存是這樣的:內(nèi)存在物理上是由一組DRAM芯片組成的。

8601b7f4-4f42-11ed-a3b6-dac502259ad0.jpg

而作為一個(gè)程序員,我們不需要了解內(nèi)存的物理結(jié)構(gòu),操作系統(tǒng)將RAM等硬件和軟件結(jié)合起來,給程序員提供的一種對(duì)內(nèi)存使用的抽象。

這種抽象機(jī)制使得程序使用的是虛擬存儲(chǔ)器,而不是直接操作和使用真實(shí)存在的物理存儲(chǔ)器。

所有的虛擬地址形成的集合就是虛擬地址空間。

8630076c-4f42-11ed-a3b6-dac502259ad0.jpg

在程序員眼中的內(nèi)存應(yīng)該是下面這樣的。

86671630-4f42-11ed-a3b6-dac502259ad0.png

也就是說,內(nèi)存是一個(gè)很大的,線性的字節(jié)數(shù)組(平坦尋址)。每一個(gè)字節(jié)都是固定的大小,由8個(gè)二進(jìn)制位組成。

最關(guān)鍵的是,每一個(gè)字節(jié)都有一個(gè)唯一的編號(hào),編號(hào)從0開始,一直到最后一個(gè)字節(jié)。

如上圖中,這是一個(gè)256M的內(nèi)存,他一共有256x1024x1024 = 268435456個(gè)字節(jié),那么它的地址范圍就是 0 ~268435455 。

由于內(nèi)存中的每一個(gè)字節(jié)都有一個(gè)唯一的編號(hào)。

因此,在程序中使用的變量,常量,甚至數(shù)函數(shù)等數(shù)據(jù),當(dāng)他們被載入到內(nèi)存中后,都有自己唯一的一個(gè)編號(hào),這個(gè)編號(hào)就是這個(gè)數(shù)據(jù)的地址。

指針就是這樣形成的。

下面用代碼說明


#include 
int main(void)
{
  char ch = 'a';
  int num = 97;
  printf("ch 的地址:%p
",&ch);  //ch 的地址:0028FF47
  printf("num的地址:%p
",&num); //num的地址:0028FF40
  return 0;
}

86738ed8-4f42-11ed-a3b6-dac502259ad0.png

指針的值實(shí)質(zhì)是內(nèi)存單元(即字節(jié))的編號(hào),所以指針單獨(dú)從數(shù)值上看,也是整數(shù),他們一般用16進(jìn)制表示。

指針的值(虛擬地址值)使用一個(gè)機(jī)器字的大小來存儲(chǔ)。

也就是說,對(duì)于一個(gè)機(jī)器字為w位的電腦而言,它的虛擬地址空間是0~2w - 1 ,程序最多能訪問2w個(gè)字節(jié)。

這就是為什么xp這種32位系統(tǒng)最大支持4GB內(nèi)存的原因了。

我們可以大致畫出變量ch和num在內(nèi)存模型中的存儲(chǔ)。(假設(shè) char占1個(gè)字節(jié),int占4字節(jié))

86da5942-4f42-11ed-a3b6-dac502259ad0.png

變量和內(nèi)存

為了簡單起見,這里就用上面例子中的 int num = 97 這個(gè)局部變量來分析變量在內(nèi)存中的存儲(chǔ)模型。

86e703fe-4f42-11ed-a3b6-dac502259ad0.png

已知:num的類型是int,占用了4個(gè)字節(jié)的內(nèi)存空間,其值是97,地址是0028FF40。我們從以下幾個(gè)方面去分析。

1、內(nèi)存的數(shù)據(jù)

內(nèi)存的數(shù)據(jù)就是變量的值對(duì)應(yīng)的二進(jìn)制,一切都是二進(jìn)制。

97的二進(jìn)制是 : 00000000 00000000 00000000 0110000 , 但使用的小端模式存儲(chǔ)時(shí),低位數(shù)據(jù)存放在低地址,所以圖中畫的時(shí)候是倒過來的。

2、內(nèi)存數(shù)據(jù)的類型

內(nèi)存的數(shù)據(jù)類型決定了這個(gè)數(shù)據(jù)占用的字節(jié)數(shù),以及計(jì)算機(jī)將如何解釋這些字節(jié)。

num的類型是int,因此將被解釋為 一個(gè)整數(shù)。

3、內(nèi)存數(shù)據(jù)的名稱

內(nèi)存的名稱就是變量名。實(shí)質(zhì)上,內(nèi)存數(shù)據(jù)都是以地址來標(biāo)識(shí)的,根本沒有內(nèi)存的名稱這個(gè)說法,這只是高級(jí)語言提供的抽象機(jī)制 ,方便我們操作內(nèi)存數(shù)據(jù)。

而且在C語言中,并不是所有的內(nèi)存數(shù)據(jù)都有名稱,例如使用malloc申請(qǐng)的堆內(nèi)存就沒有。

4、內(nèi)存數(shù)據(jù)的地址

如果一個(gè)類型占用的字節(jié)數(shù)大于1,則其變量的地址就是地址值最小的那個(gè)字節(jié)的地址。

因此num的地址是 0028FF40。內(nèi)存的地址用于標(biāo)識(shí)這個(gè)內(nèi)存塊。

5、內(nèi)存數(shù)據(jù)的生命周期

num是main函數(shù)中的局部變量,因此當(dāng)main函數(shù)被啟動(dòng)時(shí),它被分配于棧內(nèi)存上,當(dāng)main執(zhí)行結(jié)束時(shí),消亡。

如果一個(gè)數(shù)據(jù)一直占用著他的內(nèi)存,那么我們就說他是“活著的”,如果他占用的內(nèi)存被回收了,則這個(gè)數(shù)據(jù)就“消亡了”。

C語言中的程序數(shù)據(jù)會(huì)按照他們定義的位置,數(shù)據(jù)的種類,修飾的關(guān)鍵字等因素,決定他們的生命周期特性。

實(shí)質(zhì)上我們程序使用的內(nèi)存會(huì)被邏輯上劃分為:棧區(qū),堆區(qū),靜態(tài)數(shù)據(jù)區(qū),方法區(qū)。

不同的區(qū)域的數(shù)據(jù)有不同的生命周期。

無論以后計(jì)算機(jī)硬件如何發(fā)展,內(nèi)存容量都是有限的,因此清楚理解程序中每一個(gè)程序數(shù)據(jù)的生命周期是非常重要的。

指針運(yùn)算

N多的面試會(huì)考這種東西了:

Type *p; 
p++;
然后問你p的值變化了多少。
其實(shí),也可以認(rèn)為這是在考編譯器的基本知識(shí)。因此p的值并不像表面看到的+1那么簡單,編譯器實(shí)際上對(duì)p進(jìn)行的是加sizeof(Type)的操作。 看一個(gè)一段代碼的測試結(jié)果:

86f8852a-4f42-11ed-a3b6-dac502259ad0.png

這里注釋掉char一行的原因是因?yàn)閏out<<(char*)會(huì)被當(dāng)成字符串輸出,而不是char的地址)

執(zhí)行結(jié)果: 8720f29e-4f42-11ed-a3b6-dac502259ad0.png ? ? ? ? ?觀察結(jié)果,可以看出,他們的增長結(jié)果分別是:

2(sizeof(short))     
4(sizeof(int))    
4(sizeof(long))
8(sizeof(longlong))
4(sizeof(float))
8(sizeof(double))
12(sizeof(longdouble))

喏,增加的值是不是sizeof(Type)呢?別的什么struct,class之類的,就不驗(yàn)證你,有興趣的自己去驗(yàn)證。 我們?cè)賹?duì)這樣的一段代碼進(jìn)行匯編,查看編譯器是如何進(jìn)行指針的加法操作的:

872e3eae-4f42-11ed-a3b6-dac502259ad0.png

匯編結(jié)果:

873c59ee-4f42-11ed-a3b6-dac502259ad0.jpg

8756d242-4f42-11ed-a3b6-dac502259ad0.png

注意看注釋部分的結(jié)果,我們看到,piv的值顯示加了4(sizeof(int)),然后又加了16(4*sizeof(int))。

指針變量和指向關(guān)系

用來保存指針的變量,就是指針變量。

如果指針變量p1保存了變量 num的地址,則就說:p1指向了變量num,也可以說p1指向了num所在的內(nèi)存塊 ,這種指向關(guān)系,在圖中一般用 箭頭表示。

8769ff16-4f42-11ed-a3b6-dac502259ad0.png

上圖中,指針變量p1指向了num所在的內(nèi)存塊 ,即從地址0028FF40開始的4個(gè)byte 的內(nèi)存塊。

定義指針變量

C語言中,定義變量時(shí),在變量名前寫一個(gè) * 星號(hào),這個(gè)變量就變成了對(duì)應(yīng)變量類型的指針變量。必要時(shí)要加( ) 來避免優(yōu)先級(jí)的問題。

引申:C語言中,定義變量時(shí),在定義的最前面寫上typedef ,那么這個(gè)變量名就成了一種類型,即這個(gè)類型的同義詞。

int a ; //int類型變量 a
int *a ; //int* 變量a
int arr[3]; //arr是包含3個(gè)int元素的數(shù)組
int (* arr )[3]; //arr是一個(gè)指向包含3個(gè)int元素的數(shù)組的指針變量




//-----------------各種類型的指針------------------------------


int* p_int; //指向int類型變量的指針 


double* p_double; //指向idouble類型變量的指針 


struct Student *p_struct; //結(jié)構(gòu)體類型的指針


int(*p_func)(int,int); //指向返回類型為int,有2個(gè)int形參的函數(shù)的指針 


int(*p_arr)[3]; //指向含有3個(gè)int元素的數(shù)組的指針 


int** p_pointer; //指向 一個(gè)整形變量指針的指針

指針的2個(gè)重要屬性

指針也是一種數(shù)據(jù),指針變量也是一種變量,因此指針 這種數(shù)據(jù)也符合前面變量和內(nèi)存主題中的特性。

這里要強(qiáng)調(diào)2個(gè)屬性:指針的類型,指針的值。

int main(void)
{
  int num = 97;
  int *p1 = #
  char* p2 = (char*)(&num);


  printf("%d
",*p1);  //輸出 97
  putchar(*p2);     //輸出 a
  return 0;
}

指針的值:很好理解,如上面的num 變量 ,其地址的值就是0028FF40 ,因此 p1的值就是0028FF40。

數(shù)據(jù)的地址用于在內(nèi)存中定位和標(biāo)識(shí)這個(gè)數(shù)據(jù),因?yàn)槿魏?個(gè)內(nèi)存不重疊的不同數(shù)據(jù)的地址都是不同的。

指針的類型:指針的類型決定了這個(gè)指針指向的內(nèi)存的字節(jié)數(shù)并如何解釋這些字節(jié)信息。

一般指針變量的類型要和它指向的數(shù)據(jù)的類型匹配。

由于num的地址是0028FF40,因此 p1 和 p2 的值都是0028FF40

*p1 : 將從地址0028FF40 開始解析,因?yàn)閜1是int類型指針,int占4字節(jié),因此向后連續(xù)取4個(gè)字節(jié),并將這4個(gè)字節(jié)的二進(jìn)制數(shù)據(jù)解析為一個(gè)整數(shù) 97。

*p2 : 將從地址0028FF40 開始解析,因?yàn)閜2是char類型指針,char占1字節(jié),因此向后連續(xù)取1個(gè)字節(jié),并將這1個(gè)字節(jié)的二進(jìn)制數(shù)據(jù)解析為一個(gè)字符,即'a'。

同樣的地址,因?yàn)橹羔樀念愋筒煌瑢?duì)它指向的內(nèi)存的解釋就不同,得到的就是不同的數(shù)據(jù)。

取地址

既然有了指針變量,那就得讓他保存其它變量的地址,使用& 運(yùn)算符取得一個(gè)變量的地址。

int add(int a , int b)
{
  return a + b;
}


int main(void)
{
  int num = 97;
  float score = 10.00F;
  int arr[3] = {1,2,3};


  //-----------------------


  int* p_num = #
  float* p_score = &score;
  int (*p_arr)[3] = &arr;      
  int (*fp_add)(int ,int ) = add; //p_add是指向函數(shù)add的函數(shù)指針
  return 0;
}

特殊的情況,他們并不一定需要使用&取地址:

數(shù)組名的值就是這個(gè)數(shù)組的第一個(gè)元素的地址。

函數(shù)名的值就是這個(gè)函數(shù)的地址。

字符串字面值常量作為右值時(shí),就是這個(gè)字符串對(duì)應(yīng)的字符數(shù)組的名稱,也就是這個(gè)字符串在內(nèi)存中的地址。

int add(int a , int b){
  return a + b;
}
int main(void)
{
  int arr[3] = {1,2,3};
  //-----------------------
  int* p_first = arr;
  int (*fp_add)(int ,int ) = add;
  const char* msg = "Hello world";
  return 0;
}

解地址

我們需要一個(gè)數(shù)據(jù)的指針變量干什么?

當(dāng)然使用通過它來操作(讀/寫)它指向的數(shù)據(jù)啦。

對(duì)一個(gè)指針解地址,就可以取到這個(gè)內(nèi)存數(shù)據(jù),解地址的寫法,就是在指針的前面加一個(gè)*號(hào)。

解指針的實(shí)質(zhì)是:從指針指向的內(nèi)存塊中取出這個(gè)內(nèi)存數(shù)據(jù)。

int main(void)
{
  int age = 19;
  int*p_age = &age;
  *p_age = 20; //通過指針修改指向的內(nèi)存數(shù)據(jù)


  printf("age = %d
",*p_age);  //通過指針讀取指向的內(nèi)存數(shù)據(jù)
  printf("age = %d
",age);


  return 0;
}

指針之間的賦

指針賦值和int變量賦值一樣,就是將地址的值拷貝給另外一個(gè)。

指針之間的賦值是一種淺拷貝,是在多個(gè)編程單元之間共享內(nèi)存數(shù)據(jù)的高效的方法。

int* p1 = & num;
int* p3 = p1;


//通過指針 p1 、 p3 都可以對(duì)內(nèi)存數(shù)據(jù) num 進(jìn)行讀寫,如果2個(gè)函數(shù)分別使用了p1 和p3,那么這2個(gè)函數(shù)就共享了數(shù)據(jù)num。

8775a8de-4f42-11ed-a3b6-dac502259ad0.png

空指針(NULL指針)

NULL是C語言標(biāo)準(zhǔn)定義的一個(gè)值,這個(gè)值其實(shí)就是0,只不過為了使得看起來更加具有意義,才定義了這樣的一個(gè)宏,中文的意思是空,表明不指向任何東西。你懂得。不過在此不討論空和零的區(qū)別。

在C語言中,我們讓指針變量賦值為NULL表示一個(gè)空指針,而C語言中,NULL實(shí)質(zhì)是((void*)0),就像前面說的指針可以理解成特殊的int,它總是有值的,p=NULL,其實(shí)就是p的值等于0。對(duì)于不多數(shù)機(jī)器而言,0地址是不能直接訪問的,設(shè)置為0,就表示該指針哪里都沒指向。而在C++中,NULL實(shí)質(zhì)是0。

換種說法:任何程序數(shù)據(jù)都不會(huì)存儲(chǔ)在地址為0的內(nèi)存塊中,它是被操作系統(tǒng)預(yù)留的內(nèi)存塊。
下面代碼摘自 stdlib.h

#ifdef __cplusplus
   #define NULL  0
#else  
   #define NULL  ((void *)0)
#endif
當(dāng)然,就機(jī)器內(nèi)部而言,NULL指針的實(shí)際值可能與此不同,這種情況下,編譯器將負(fù)責(zé)零值和內(nèi)部值之間的翻譯轉(zhuǎn)換。

NULL指針的概念非常有用,它給了你一種方法,表示某個(gè)特定的指針目前并未指向任何東西。例如,一個(gè)用于在某個(gè)數(shù)組中查找某個(gè)特定值的函數(shù)可能返回一個(gè)指向查找到的數(shù)組元素的指針。如果沒找到,則返回一個(gè)NULL指針。

在內(nèi)存的動(dòng)態(tài)分配上,NULL的意義非同凡響,我們使用它來避免內(nèi)存被多次釋放,造成經(jīng)常性的段錯(cuò)誤(segmentation fault)。一般,在free或者delete掉動(dòng)態(tài)分配的內(nèi)存后,都應(yīng)該立即把指針置空,避免出現(xiàn)所以的懸掛指針,致使出現(xiàn)各種內(nèi)存錯(cuò)誤!例如:

87847526-4f42-11ed-a3b6-dac502259ad0.png

free函數(shù)是不會(huì)也不可能把p置空的。像下面這樣的代碼就會(huì)出現(xiàn)內(nèi)存段錯(cuò)誤:

8795b584-4f42-11ed-a3b6-dac502259ad0.png

因?yàn)椋谝淮蝔ree操作之后,p指向的內(nèi)存已經(jīng)釋放了,但是p的值還沒有變化,free函數(shù)改不了這個(gè)值,再free一次的時(shí)候,p指向的內(nèi)存區(qū)域已經(jīng)被釋放了,這個(gè)地址已經(jīng)變成了非法地址,這個(gè)操作將導(dǎo)致段錯(cuò)誤的發(fā)生(此時(shí),p指向的區(qū)域剛好又被分配出去了,但是這種概率非常低,而且對(duì)這樣一塊內(nèi)存區(qū)域進(jìn)行操作是非常危險(xiǎn)的!)
但是下面這段代碼就不會(huì)出現(xiàn)這樣的問題:

87a5ff3e-4f42-11ed-a3b6-dac502259ad0.png

因?yàn)閜的值編程了NULL,free函數(shù)檢測到p為NULL,會(huì)直接返回,而不會(huì)發(fā)生錯(cuò)誤。 這里順便告訴大家一個(gè)內(nèi)存釋放的小竅門,可以有效的避免因?yàn)橥泴?duì)指針進(jìn)行置空而出現(xiàn)各種內(nèi)存問題。這個(gè)方法就是自定義一個(gè)內(nèi)存釋放函數(shù),但是傳入的參數(shù)不知指針,而是指針的地址,在這個(gè)函數(shù)里面置空,如下:

87b4db12-4f42-11ed-a3b6-dac502259ad0.png

結(jié)果:

87c829a6-4f42-11ed-a3b6-dac502259ad0.png


my_free調(diào)用了之后,p的值就變成了0(NULL),調(diào)用多少次free都不會(huì)報(bào)錯(cuò)了!

另外一個(gè)方式也非常有效,那就是定義FREE宏,在宏里面對(duì)他進(jìn)行置空。例如

87d222ee-4f42-11ed-a3b6-dac502259ad0.png


執(zhí)行結(jié)果同上面一樣,不會(huì)報(bào)段錯(cuò)誤:

87ddf48e-4f42-11ed-a3b6-dac502259ad0.png

(關(guān)于內(nèi)存的動(dòng)態(tài)分配,這是個(gè)比較復(fù)雜的話題,有機(jī)會(huì)再專門開辟一章給各位講述一下吧,寫個(gè)帖子還是很花費(fèi)時(shí)間和精力的,呵呵,寫過的童鞋應(yīng)該都很清楚,所以順便插一句,轉(zhuǎn)帖可以,請(qǐng)注明出處,畢竟,大家都是本著共享的精神來討論問題的,寫的好壞都沒有向你所要什么,請(qǐng)尊重每個(gè)人的勞動(dòng)成果。) 指向空,或者說不指向任何東西。

壞指針

指針變量的值是NULL,或者未知的地址值,或者是當(dāng)前應(yīng)用程序不可訪問的地址值,這樣的指針就是壞指針。

不能對(duì)他們做解指針操作,否則程序會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)誤,導(dǎo)致程序意外終止。

任何一個(gè)指針變量在做解地址操作前,都必須保證它指向的是有效的,可用的內(nèi)存塊,否則就會(huì)出錯(cuò)。

壞指針是造成C語言Bug的最頻繁的原因之一。

下面的代碼就是錯(cuò)誤的示例。




void opp()
{
   int*p = NULL;
   *p = 10;   //Oops! 不能對(duì)NULL解地址
}


void foo()
{
   int*p;
   *p = 10;   //Oops! 不能對(duì)一個(gè)未知的地址解地址
}


void bar()
{
   int*p = (int*)1000; 
   *p =10;   //Oops!  不能對(duì)一個(gè)可能不屬于本程序的內(nèi)存的地址的指針解地址
}

void*類型指針

由于void是空類型,因此void*類型的指針只保存了指針的值,而丟失了類型信息,我們不知道他指向的數(shù)據(jù)是什么類型的,只指定這個(gè)數(shù)據(jù)在內(nèi)存中的起始地址。

如果想要完整的提取指向的數(shù)據(jù),程序員就必須對(duì)這個(gè)指針做出正確的類型轉(zhuǎn)換,然后再解指針。因?yàn)椋幾g器不允許直接對(duì)void*類型的指針做解指針操作。


雖然從字面上看,void的意思是空,但是void指針的意思,可不是空指針的意思,空指針指的是上面所說的NULL指針。
void指針實(shí)際上的意思是指向任意類型的指針。任意類型的指針都可以直接賦給void指針,而不需要進(jìn)行強(qiáng)制轉(zhuǎn)換。
例如:

Type a, *p=&a;
(Type等于char, int, struct, int *…)
void *pv;
pv=p;
就像前面說的,void指針的好處,就在于,任意的指針都可以直接賦值給它,這在某些場合非常有用,因此有些操作對(duì)于任意指針都是相同的。void指針最常用于內(nèi)存管理。最典型的,也是大家最熟知的,就是標(biāo)準(zhǔn)庫的free函數(shù)。它的原型如下:
 void free(void*ptr);
free函數(shù)的參數(shù)可以是任意指針,沒有誰見過free參數(shù)里面的指針需要強(qiáng)壯為void*的吧? malloc, calloc,realloc這些函數(shù)的返回值也是void指針,因?yàn)閮?nèi)存分配,實(shí)際上只需要知道分配的大小,然后返回新分配內(nèi)存的地址就可以了,指針的值就是地址,返回的不管是何種指針,其實(shí)結(jié)果都是一樣的,因?yàn)樗械闹羔橀L度其實(shí)都是32位的(32位機(jī)器),它的值就是內(nèi)存的地址,指針類型只是給編譯器看的,目的是讓編譯器在編譯的時(shí)候能夠正確的設(shè)置指針的值(參見指針運(yùn)算章節(jié))。如果malloc函數(shù)設(shè)置成下面這樣的原型,完全沒有問題。
char*malloc(size_t sz);
實(shí)際上設(shè)置成

Type*malloc(size_t sz);
也是完全正確的,使用void指針的原因,實(shí)際上就像前面說的,void指針意思是任意指針,這樣設(shè)計(jì)更加嚴(yán)謹(jǐn)一些,也更符合我們的直觀理解。如果對(duì)前面我說的指針概念理解的童鞋,肯定明白這一點(diǎn)。

結(jié)構(gòu)體和指針

結(jié)構(gòu)體指針有特殊的語法:-> 符號(hào)

如果p是一個(gè)結(jié)構(gòu)體指針,則可以使用 p ->【成員】 的方法訪問結(jié)構(gòu)體的成員

typedef struct
{
  char name[31];
  int age;
  float score;
}Student;


int main(void)
{
  Student stu = {"Bob" , 19, 98.0};
  Student*ps = &stu;


  ps->age = 20;
  ps->score = 99.0;
  printf("name:%s age:%d
",ps->name,ps->age);
  return 0;
}

數(shù)組和指針

1、數(shù)組名作為右值的時(shí)候,就是第一個(gè)元素的地址。

int main(void)
{
  int arr[3] = {1,2,3};


  int*p_first = arr;
  printf("%d
",*p_first); //1
  return 0;
}

2、指向數(shù)組元素的指針 支持 遞增 遞減 運(yùn)算。
(實(shí)質(zhì)上所有指針都支持遞增遞減 運(yùn)算 ,但只有在數(shù)組中使用才是有意義的)

int main(void)
{
  int arr[3] = {1,2,3};


  int*p = arr;
  for(;p!=arr+3;p++){
    printf("%d
",*p); 
  }
  return 0;
}

3、p= p+1 意思是,讓p指向原來指向的內(nèi)存塊的下一個(gè)相鄰的相同類型的內(nèi)存塊。

同一個(gè)數(shù)組中,元素的指針之間可以做減法運(yùn)算,此時(shí),指針之差等于下標(biāo)之差。

4、p[n] == *(p+n)

p[n][m] == *( *(p+n)+ m )

5、當(dāng)對(duì)數(shù)組名使用sizeof時(shí),返回的是整個(gè)數(shù)組占用的內(nèi)存字節(jié)數(shù)。當(dāng)把數(shù)組名賦值給一個(gè)指針后,再對(duì)指針使用sizeof運(yùn)算符,返回的是指針的大小。

這就是為什么將一個(gè)數(shù)組傳遞給一個(gè)函數(shù)時(shí),需要另外用一個(gè)參數(shù)傳遞數(shù)組元素個(gè)數(shù)的原因了。

int main(void)
{
  int arr[3] = {1,2,3};


  int*p = arr;
  printf("sizeof(arr)=%d
",sizeof(arr)); //sizeof(arr)=12
  printf("sizeof(p)=%d
",sizeof(p));  //sizeof(p)=4


  return 0;
}

函數(shù)和指針

函數(shù)的參數(shù)和指針

C語言中,實(shí)參傳遞給形參,是按值傳遞的,也就是說,函數(shù)中的形參是實(shí)參的拷貝份,形參和實(shí)參只是在值上面一樣,而不是同一個(gè)內(nèi)存數(shù)據(jù)對(duì)象。

這就意味著:這種數(shù)據(jù)傳遞是單向的,即從調(diào)用者傳遞給被調(diào)函數(shù),而被調(diào)函數(shù)無法修改傳遞的參數(shù)達(dá)到回傳的效果。

void change(int a)
{
  a++;   //在函數(shù)中改變的只是這個(gè)函數(shù)的局部變量a,而隨著函數(shù)執(zhí)行結(jié)束,a被銷毀。age還是原來的age,紋絲不動(dòng)。
}
int main(void)
{
  int age = 19;
  change(age);
  printf("age = %d
",age);  // age = 19
  return 0;
}

有時(shí)候我們可以使用函數(shù)的返回值來回傳數(shù)據(jù),在簡單的情況下是可以的。

但是如果返回值有其它用途(例如返回函數(shù)的執(zhí)行狀態(tài)量),或者要回傳的數(shù)據(jù)不止一個(gè),返回值就解決不了了。

傳遞變量的指針可以輕松解決上述問題。

void change(int* pa)
{
  (*pa)++;  //因?yàn)閭鬟f的是age的地址,因此pa指向內(nèi)存數(shù)據(jù)age。當(dāng)在函數(shù)中對(duì)指針pa解地址時(shí),
        //會(huì)直接去內(nèi)存中找到age這個(gè)數(shù)據(jù),然后把它增1。
}
int main(void)
{
  int age = 19;
  change(&age);
  printf("age = %d
",age);  // age = 20
  return 0;
}

再來一個(gè)老生常談的,用函數(shù)交換2個(gè)變量的值的例子:

#include
void swap_bad(int a,int b);
void swap_ok(int*pa,int*pb);


int main()
{
  int a = 5;
  int b = 3;
  swap_bad(a,b);    //Can`t swap;
  swap_ok(&a,&b);   //OK
  return 0;
}


//錯(cuò)誤的寫法
void swap_bad(int a,int b)
{
  int t;
  t=a;
  a=b;
  b=t;
}


//正確的寫法:通過指針
void swap_ok(int*pa,int*pb)
{
  int t;
  t=*pa;
  *pa=*pb;
  *pb=t;
}

87ee3524-4f42-11ed-a3b6-dac502259ad0.png

8802cd36-4f42-11ed-a3b6-dac502259ad0.png

有的時(shí)候,我們通過指針傳遞數(shù)據(jù)給函數(shù)不是為了在函數(shù)中改變他指向的對(duì)象。

相反,我們防止這個(gè)目標(biāo)數(shù)據(jù)被改變。傳遞指針只是為了避免拷貝大型數(shù)據(jù)。

考慮一個(gè)結(jié)構(gòu)體類型Student。我們通過show函數(shù)輸出Student變量的數(shù)據(jù)。

typedef struct
{
  char name[31];
  int age;
  float score;
}Student;




//打印Student變量信息
void show(const Student * ps)
{
  printf("name:%s , age:%d , score:%.2f
",ps->name,ps->age,ps->score);  
}

我們只是在show函數(shù)中取讀Student變量的信息,而不會(huì)去修改它,為了防止意外修改,我們使用了常量指針去約束。

另外我們?yōu)槭裁匆褂弥羔樁皇侵苯觽鬟fStudent變量呢?

從定義的結(jié)構(gòu)看出,Student變量的大小至少是39個(gè)字節(jié),那么通過函數(shù)直接傳遞變量,實(shí)參賦值數(shù)據(jù)給形參需要拷貝至少39個(gè)字節(jié)的數(shù)據(jù),極不高效。

而傳遞變量的指針卻快很多,因?yàn)樵谕粋€(gè)平臺(tái)下,無論什么類型的指針大小都是固定的:X86指針4字節(jié),X64指針8字節(jié),遠(yuǎn)遠(yuǎn)比一個(gè)Student結(jié)構(gòu)體變量小。

函數(shù)的指針

跟普通的變量一樣,每一個(gè)函數(shù)都是有其地址的,我們通過跳轉(zhuǎn)到這個(gè)地址執(zhí)行代碼來進(jìn)行函數(shù)調(diào)用,只是,跟取普通數(shù)據(jù)不同的在于,函數(shù)有參數(shù)和返回值,在進(jìn)行函數(shù)調(diào)用的時(shí)候,首先需要將參數(shù)壓入棧中,調(diào)用完成后又需要將參數(shù)壓入棧中。既然函數(shù)也是通過地址來進(jìn)行訪問的,那它也可以使用指針來指向,事實(shí)上,每一個(gè)函數(shù)名都是一個(gè)指針,不過它是指針常量和指針常量,它的值是不能改的,指向的值也不能改。

(關(guān)于常量指針和指針常量什么的,有時(shí)間在專門開辟一章來說明const這個(gè)東東吧,也是很有講頭的一個(gè)東東。。。) 函數(shù)指針一般用來干什么呢?函數(shù)指針最常用的場合就是回調(diào)函數(shù)。回調(diào)函數(shù),顧名思義,就是某個(gè)函數(shù)會(huì)在適當(dāng)?shù)臅r(shí)候被別人調(diào)用。當(dāng)期望你調(diào)用的函數(shù)能夠使用你的某些方式去操作的時(shí)候,回調(diào)函數(shù)就很有用,比如,你期望某個(gè)排序函數(shù)在比較的時(shí)候,能夠使用你定義的比較方法去比較。

有過較深入的C編程經(jīng)驗(yàn)的人應(yīng)該都接觸過。C的標(biāo)準(zhǔn)庫中就有使用,例如在strlib.h頭文件的qsort函數(shù),它的原型為:

void qsort(void*__base, size_t __nmemb, size_t __size, int(*_compar)(const void *, const void*)); 其中int(*_compar)(const void *, const void *)就是回調(diào)函數(shù),這個(gè)函數(shù)用于qsort函數(shù)用于數(shù)據(jù)的比較。下面,我會(huì)舉一個(gè)例子,來描述qsort函數(shù)的工作原理

一般,我們使用下面這樣的方式來定義函數(shù)指針:

 typedef int(*compare)(const void *x, const void *y);
這個(gè)時(shí)候,compare就是參數(shù)為const void *, const void *類型,返回值是int類型的函數(shù)。例如:

88370a1a-4f42-11ed-a3b6-dac502259ad0.png

8866cc00-4f42-11ed-a3b6-dac502259ad0.png

用typedef來定義的好處,就是可以使用一個(gè)簡短的名稱來表示一種類型,而不需要總是使用很長的代碼來,這樣不僅使得代碼更加簡潔易讀,更是避免了代碼敲寫容易出錯(cuò)的問題。強(qiáng)烈推薦各位在定義結(jié)構(gòu)體,指針(尤其是函數(shù)指針)等比較復(fù)雜的結(jié)構(gòu)時(shí),使用typedef來定義。

每一個(gè)函數(shù)本身也是一種程序數(shù)據(jù),一個(gè)函數(shù)包含了多條執(zhí)行語句,它被編譯后,實(shí)質(zhì)上是多條機(jī)器指令的合集。

在程序載入到內(nèi)存后,函數(shù)的機(jī)器指令存放在一個(gè)特定的邏輯區(qū)域:代碼區(qū)。

既然是存放在內(nèi)存中,那么函數(shù)也是有自己的指針的。

C語言中,函數(shù)名作為右值時(shí),就是這個(gè)函數(shù)的指針。

void echo(const char *msg)
{
  printf("%s",msg);
}
int main(void)
{
  void(*p)(const char*) = echo;  //函數(shù)指針變量指向echo這個(gè)函數(shù)


  p("Hello ");   //通過函數(shù)的指針p調(diào)用函數(shù),等價(jià)于echo("Hello ")
  echo("World
");
  return 0;
}

const和指針

const到底修飾誰?誰才是不變的?

如果const 后面是一個(gè)類型,則跳過最近的原子類型,修飾后面的數(shù)據(jù)。
(原子類型是不可再分割的類型,如int, short , char,以及typedef包裝后的類型)

如果const后面就是一個(gè)數(shù)據(jù),則直接修飾這個(gè)數(shù)據(jù)。

int main()
{
  int a = 1;


  int const *p1 = &a;    //const后面是*p1,實(shí)質(zhì)是數(shù)據(jù)a,則修飾*p1,通過p1不能修改a的值
  const int*p2 = &a;    //const后面是int類型,則跳過int ,修飾*p2, 效果同上


  int* const p3 = NULL;   //const后面是數(shù)據(jù)p3。也就是指針p3本身是const .


  const int* const p4 = &a; // 通過p4不能改變a 的值,同時(shí)p4本身也是 const
  int const* const p5 = &a; //效果同上


  return 0;


}


typedef int* pint_t; //將 int* 類型 包裝為 pint_t,則pint_t 現(xiàn)在是一個(gè)完整的原子類型


int main()
{


  int a = 1;
  const pint_t p1 = &a; //同樣,const跳過類型pint_t,修飾p1,指針p1本身是const
  pint_t const p2 = &a; //const 直接修飾p,同上


  return 0;


}

深拷貝和淺拷貝

如果2個(gè)程序單元(例如2個(gè)函數(shù))是通過拷貝他們所共享的數(shù)據(jù)的指針來工作的,這就是淺拷貝,因?yàn)檎嬲L問的數(shù)據(jù)并沒有被拷貝。

如果被訪問的數(shù)據(jù)被拷貝了,在每個(gè)單元中都有自己的一份,對(duì)目標(biāo)數(shù)據(jù)的操作相互不受影響,則叫做深拷貝。

88711f0c-4f42-11ed-a3b6-dac502259ad0.png

附加知識(shí)

指針和引用這個(gè)2個(gè)名詞的區(qū)別。他們本質(zhì)上來說是同樣的東西。

指針常用在C語言中,而引用,則用于諸如JavaC#等 在語言層面封裝了對(duì)指針的直接操作的編程語言中。

大端模式和小端模式

1) Little-Endian就是低位字節(jié)排放在內(nèi)存的低地址端,高位字節(jié)排放在內(nèi)存的高地址端。個(gè)人PC常用,Intel X86處理器是小端模式。

2) B i g-Endian就是高位字節(jié)排放在內(nèi)存的低地址端,低位字節(jié)排放在內(nèi)存的高地址端。

采用大端方式進(jìn)行數(shù)據(jù)存放符合人類的正常思維,而采用小端方式進(jìn)行數(shù)據(jù)存放利于計(jì)算機(jī)處理。

有些機(jī)器同時(shí)支持大端和小端模式,通過配置來設(shè)定實(shí)際的端模式。

假如 short類型占用2個(gè)字節(jié),且存儲(chǔ)的地址為0x30。

short a = 1;

如下圖:

88a51c80-4f42-11ed-a3b6-dac502259ad0.png


//測試機(jī)器使用的是否為小端模式。是,則返回true,否則返回false //這個(gè)方法判別的依據(jù)就是:C語言中一個(gè)對(duì)象的地址就是這個(gè)對(duì)象占用的字節(jié)中,地址值最小的那個(gè)字節(jié)的地址。

bool isSmallIndain()
{
   unsigned int val = 'A';
   unsigned char* p = (unsigned char*)&val; //C/C++:對(duì)于多字節(jié)數(shù)據(jù),取地址是取的數(shù)據(jù)對(duì)象的第一個(gè)字節(jié)的地址,也就是數(shù)據(jù)的低地址


   return *p == 'A';
}
審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 計(jì)算機(jī)
    +關(guān)注

    關(guān)注

    19

    文章

    7536

    瀏覽量

    88643
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    3055

    瀏覽量

    74331
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7614

    瀏覽量

    137720

原文標(biāo)題:C語言指針難嗎?純干貨講解(附代碼)

文章出處:【微信號(hào):電子設(shè)計(jì)寶典,微信公眾號(hào):電子設(shè)計(jì)寶典】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    什么是計(jì)算機(jī)系統(tǒng)、計(jì)算機(jī)硬件和計(jì)算機(jī)軟件?

    第一章 計(jì)算機(jī)系統(tǒng)概論1. 什么是計(jì)算機(jī)系統(tǒng)、計(jì)算機(jī)硬件和計(jì)算機(jī)軟件?硬件和軟件哪個(gè)更重要?解:P3計(jì)算機(jī)系統(tǒng):由
    發(fā)表于 07-22 09:06

    32位微機(jī)是計(jì)算機(jī)所用的CPU有何特點(diǎn)

    83、32位微機(jī)是計(jì)算機(jī)所用的CPU:(B)(A)具有32位寄存器(B) 同時(shí)能處理32位二進(jìn)制數(shù)據(jù)(C) 有32個(gè)寄存器(D) 能處理32個(gè)字符84、“64位微型計(jì)算機(jī)”中的64
    發(fā)表于 09-15 08:04

    計(jì)算機(jī)的定義及分類

    一?計(jì)算機(jī)概述計(jì)算機(jī)(computer)俗稱電腦,是現(xiàn)代一種用于高速計(jì)算的電子計(jì)算機(jī)器,可以進(jìn)行數(shù)值計(jì)算
    發(fā)表于 09-16 07:38

    計(jì)算機(jī)運(yùn)算速度是什么

    計(jì)算機(jī)運(yùn)算速度是什么?按傳輸介質(zhì)分類,計(jì)算機(jī)網(wǎng)絡(luò)可分為哪幾種?使用16位二進(jìn)制編碼表示聲音與使用8位二進(jìn)制編碼表示聲音的效果有何不同?
    發(fā)表于 10-19 09:05

    計(jì)算機(jī)串行通信基礎(chǔ)

    1.計(jì)算機(jī)串行通信基礎(chǔ)1.1、通信方式分類計(jì)算機(jī)通信:是計(jì)算機(jī)與外部設(shè)備或計(jì)算機(jī)計(jì)算機(jī)之間的
    發(fā)表于 02-16 06:14

    如何去優(yōu)化計(jì)算機(jī)內(nèi)存

    大多數(shù)計(jì)算和存儲(chǔ)部署如今面臨的情況是,計(jì)算機(jī)內(nèi)存空間都受到了上限的限制。
    發(fā)表于 11-28 17:48 ?1215次閱讀
    如何去優(yōu)化<b class='flag-5'>計(jì)算機(jī)內(nèi)存</b>

    計(jì)算機(jī)中程序到底是如何運(yùn)行的?

    摘要:計(jì)算機(jī)內(nèi)存計(jì)算機(jī)中所有程序的運(yùn)行都在內(nèi)存中進(jìn)行,也就是你電腦的內(nèi)存條中運(yùn)行的。
    的頭像 發(fā)表于 05-03 17:31 ?9254次閱讀
    <b class='flag-5'>計(jì)算機(jī)</b>中程序到底是如何運(yùn)行的?

    mips是計(jì)算機(jī)的什么_mips計(jì)算公式

    mips是計(jì)算機(jī)的“運(yùn)算速度”。計(jì)算機(jī)的運(yùn)算速度通常是每秒鐘所能執(zhí)行加法指令數(shù)目,常用百萬次/秒(Million Instructions Per Second,MIPS)來表示。
    的頭像 發(fā)表于 05-26 15:57 ?5w次閱讀
    mips是<b class='flag-5'>指</b><b class='flag-5'>計(jì)算機(jī)</b>的什么_mips<b class='flag-5'>計(jì)算</b>公式

    計(jì)算機(jī)內(nèi)存中進(jìn)行過程

    計(jì)算機(jī)的總線可以分為3種:數(shù)據(jù)總線,地址總線和控制總線。這里不對(duì)控制總線進(jìn)行描述。數(shù)據(jù)總線用于進(jìn)行數(shù)據(jù)信息傳送。數(shù)據(jù)總線的位數(shù)一般與CPU的字長一致。 一般而言,數(shù)據(jù)總線的位數(shù)跟當(dāng)前機(jī)器int值
    的頭像 發(fā)表于 09-29 18:41 ?1305次閱讀
    <b class='flag-5'>計(jì)算機(jī)</b><b class='flag-5'>從</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>中進(jìn)行</b><b class='flag-5'>取</b><b class='flag-5'>指</b>過程

    計(jì)算機(jī)基礎(chǔ)知識(shí)之內(nèi)存

    計(jì)算機(jī)進(jìn)行 「數(shù)據(jù)處理」 的設(shè)備,而程序表示的就是處理順序和數(shù)據(jù)結(jié)構(gòu)。由于處理對(duì)象(數(shù)據(jù))是存儲(chǔ)在 「內(nèi)存」 和 「磁盤」 上的,因此我們今天來聊聊內(nèi)存和磁盤。
    的頭像 發(fā)表于 03-31 16:14 ?1710次閱讀
    <b class='flag-5'>計(jì)算機(jī)</b>基礎(chǔ)知識(shí)之<b class='flag-5'>內(nèi)存</b>

    計(jì)算機(jī)通信的規(guī)則

    計(jì)算機(jī)通信的規(guī)則主要是網(wǎng)絡(luò)協(xié)議,它定義了計(jì)算機(jī)如何在通信中進(jìn)行數(shù)據(jù)交換和通信的規(guī)范和格式。現(xiàn)代計(jì)算機(jī)通信中使用最廣泛的協(xié)議是TCP/IP協(xié)議。
    發(fā)表于 05-09 16:51 ?2367次閱讀

    計(jì)算機(jī)內(nèi)存和外存別再分不清了

    內(nèi)存,是大家對(duì)內(nèi)部存儲(chǔ)器的簡稱,當(dāng)然,我們也可以叫內(nèi)部存儲(chǔ)器為主存。它是計(jì)算機(jī)中最重要的部件之一。** 內(nèi)存計(jì)算機(jī)運(yùn)行過程中存放需要運(yùn)行的程序和數(shù)據(jù)的地方。
    的頭像 發(fā)表于 05-26 11:03 ?1.1w次閱讀
    <b class='flag-5'>計(jì)算機(jī)</b>的<b class='flag-5'>內(nèi)存</b>和外存別再分不清了

    微機(jī)原理和計(jì)算機(jī)組成原理的區(qū)別

    微機(jī)原理和計(jì)算機(jī)組成原理是計(jì)算機(jī)科學(xué)中兩個(gè)重要的主題,它們雖然有一定的關(guān)聯(lián),但也存在一些區(qū)別。本文將詳細(xì)闡述微機(jī)原理和計(jì)算機(jī)組成原理的區(qū)別,并從不同的角度對(duì)它們進(jìn)行分析比較。 首先,我
    的頭像 發(fā)表于 01-14 14:56 ?3687次閱讀

    計(jì)算機(jī)內(nèi)存容量有什么作用

    計(jì)算機(jī)內(nèi)存容量,作為一個(gè)核心概念,在計(jì)算機(jī)科學(xué)、信息技術(shù)以及日常使用中扮演著至關(guān)重要的角色。它不僅直接關(guān)系到計(jì)算機(jī)處理數(shù)據(jù)的能力,還影響著用戶體驗(yàn)、系統(tǒng)性能以及多任務(wù)處理的效率。在深
    的頭像 發(fā)表于 09-10 14:47 ?1259次閱讀

    虛擬內(nèi)存對(duì)計(jì)算機(jī)性能的影響

    ,它允許計(jì)算機(jī)使用硬盤空間來模擬物理內(nèi)存。當(dāng)物理內(nèi)存(RAM)不足以容納當(dāng)前所有活動(dòng)的程序和數(shù)據(jù)時(shí),操作系統(tǒng)會(huì)將部分?jǐn)?shù)據(jù)RAM移動(dòng)到硬盤上的一個(gè)特殊區(qū)域,稱為交換空間(swap sp
    的頭像 發(fā)表于 12-04 09:17 ?1244次閱讀
    百家乐群的微博| 百家乐技巧心得| 网络百家乐官网可信吗| 大发888赢钱| 百家乐破解版下载| 百家乐官网象棋赌博| bet365注册哪家好| 钱隆百家乐大师| 免费百家乐官网预测软件| 百家乐官网游戏台| 38坊| 至尊百家乐| 真人百家乐免费开户送钱| 百家乐官网真人斗地主| 鱼台县| 大发888体育真人| 百家乐赌场娱乐网规则| 百家乐在线作弊| 百家乐官网怎么骗人| 合肥市| 云鼎娱乐城怎么存钱| 钱隆百家乐智能| 百家乐傻瓜式投注法| 百家乐官网大赌场娱乐网规则| 新昌县| 易胜博网| 赌球者| 大世界百家乐娱乐网| 24山家坐向| 蓝盾百家乐官网赌场娱乐网规则| 百家乐官网游戏机压法| 利来国际网上| 大发888充值500| 英皇百家乐的玩法技巧和规则| 百家乐在线直播| 广发百家乐官网的玩法技巧和规则| 百家乐官网澳门色子| 禄丰县| 大发888平台| 澳门玩百家乐00| 百家乐板路|