衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

C語言指針詳解

GReq_mcu168 ? 來源:玩轉單片機 ? 2020-08-05 10:17 ? 次閱讀

1為什么使用指針

假如我們定義了 char a=’A’ ,當需要使用 ‘A’ 時,除了直接調用變量 a ,還可以定義 char *p=&a ,調用 a 的地址,即指向 a 的指針 p ,變量 a( char 類型)只占了一個字節,指針本身的大小由可尋址的字長來決定,指針 p 占用 4 個字節。

但如果要引用的是占用內存空間比較大東西,用指針也還是 4 個字節即可。

使用指針型變量在很多時候占用更小的內存空間。

變量為了表示數據,指針可以更好的傳遞數據,舉個例子:

第一節課是 1 班語文, 2 班數學,第二節課顛倒過來, 1 班要上數學, 2 班要上語文,那么第一節課下課后需要怎樣作調整呢?方案一:課間 1 班學生全都去 2 班, 2 班學生全都來 1 班,當然,走的時候要攜帶上書本、筆紙、零食……場面一片狼藉;方案二:兩位老師課間互換教室。

顯然,方案二更好一些,方案二類似使用指針傳遞地址,方案一將內存中的內容重新“復制”了一份,效率比較低。

在數據傳遞時,如果數據塊較大,可以使用指針傳遞地址而不是實際數據,即提高傳輸速度,又節省大量內存。

一個數據緩沖區 char buf[100] ,如果其中 buf[0,1] 為命令號, buf[2,3] 為數據類型, buf[4~7] 為該類型的數值,類型為 int ,使用如下語句進行賦值:

*(short*)&buf[0]=DataId; *(short*)&buf[2]=DataType; *(int*)&buf[4]=DataValue;

數據轉換,利用指針的靈活的類型轉換,可以用來做數據類型轉換,比較常用于通訊緩沖區的填充。

指針的機制比較簡單,其功能可以被集中重新實現成更抽象化的引用數據形式

函數指針,形如: #define PMYFUN (void*)(int,int) ,可以用在大量分支處理的實例當中,如某通訊根據不同的命令號執行不同類型的命令,則可以建立一個函數指針數組,進行散轉。

在數據結構中,鏈表、樹、圖等大量的應用都離不開指針。

2 指針是什么?

操作系統將硬件和軟件結合起來,給程序員提供的一種對內存使用的抽象,這種抽象機制使得程序使用的是虛擬存儲器,而不是直接操作和使用真實存在的物理存儲器。所有的虛擬地址形成的集合就是虛擬地址空間。

內存是一個很大的線性的字節數組,每個字節固定由 8 個二進制位組成,每個字節都有唯一的編號,如下圖,這是一個 4G 的內存,他一共有 4x1024x1024x1024 = 4294967296 個字節,那么它的地址范圍就是 0 ~ 4294967296 ,十六進制表示就是 0x00000000~0xffffffff ,當程序使用的數據載入內存時,都有自己唯一的一個編號,這個編號就是這個數據的地址。指針就是這樣形成的。

1

#include intmain(void) { charch='a'; intnum=97; printf("ch的地址:%p ",&ch); //ch的地址:00BEFDF7 printf("num的地址:%p ",&num); //num的地址:00BEFDF8 return0; }

指針不僅可以表示變量的地址,還可以存儲各種類型數據的地址,指針變量是用來保存這些地址的變量,與數組類似,依據地址存放的數據類型,指針也分為 int 指針類型, double 指針類型, char 指針類型等等。

綜上,指針的實質就是數據在內存中的地址,而指針變量是用來保存這些地址的變量。

指針變量 和 指向關系

用來保存 指針 的變量,就是指針變量。如果指針變量p保存了變量 num的地址,則就說:p指向了變量num,也可以說p指向了num所在的內存塊,指針變量pp指向了p所在的內存塊,以下面為例:

#include intmain(void) { intnum=97; charch='a'; int*p=# int**pp=&p; char*p1=&ch; printf("num的地址:%p ",&num); printf("指針p的值:%p ",p); printf("指針p的地址:%p ",&p); printf("指針pp的值:%p ",pp); printf("ch的地址:%p ",&ch); return0; }

運行結果

int型的num值為97占4個字節,內存地址為:0113F924,char 型的ch('a')值為97占1個字節,內存地址為:0113F91B。

int型占4個字節

char型占1個字節

num的地址為:0113F924,num的值為 97 ,指針 p 指向 num 的內存塊,指針 p 地址為:0113F90C,p的內存保存的值就是num的地址0113F924。

0x0113F90C存儲的內容為地址0113F924

指針變量 pp 指向 指針 p,指針 pp 內存值為 指針 p 的地址:0113F90C,形成了只想指針的指針。

指針pp為指向指針p的指針

定義指針變量

C語言中,定義變量時,在變量名 前 寫一個 * 星號,這個變量就變成了對應變量類型的指針變量。必要時要加( ) 來避免優先級的問題。

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

inta;//int類型變量a int*a;//int*變量a intarr[3];//arr是包含3個int元素的數組 int(*arr)[3];//arr是一個指向包含3個int元素的數組的指針變量 int*p_int;//指向int類型變量的指針 double*p_double;//指向idouble類型變量的指針 structStudent*p_struct;//結構體類型的指針 int(*p_func)(int,int);//指向返回類型為int,有2個int形參的函數的指針 int(*p_arr)[3];//指向含有3個int元素的數組的指針 int**p_pointer;//指向一個整形變量指針的指針

取地址

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

intadd(inta,intb) { returna+b; } intmain(void) { intnum=97; floatscore=10.00F; intarr[3]={1,2,3}; int*p_num=# float*p_score=&score; int(*p_arr)[3]=&arr; int(*fp_add)(int,int)=add;//p_add是指向函數add的函數指針 return0; }

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

數組名的值就是這個數組的第一個元素的地址。

函數名的值就是這個函數的地址。

字符串字面值常量作為右值時,就是這個字符串對應的字符數組的名稱,也就是這個字符串在內存中的地址。

intadd(inta,intb){ returna+b; } intmain(void) { intarr[3]={1,2,3}; int*p_first=arr; int(*fp_add)(int,int)=add; constchar*msg="Helloworld"; return0; }

解地址

對一個指針解地址,就可以取到這個內存數據,解地址 的寫法,就是在指針的前面加一個 * 號。

解指針的實質是:從指針指向的內存塊中取出這個內存數據。

intmain(void) { intage=19; int*p_age=&age; *p_age=20;//通過指針修改指向的內存數據 printf("age=%d",*p_age);//通過指針讀取指向的內存數據 printf("age=%d",age); return0; }

空指針

空指針在概念上不同于未初始化的指針。空指針可以確保不指向任何對象或函數;而未初始化的指針則可能指向任何地方。空指針不是野指針。

在C語言中,我們讓指針變量賦值為NULL表示一個空指針,而C語言中,NULL實質是 ((void*)0) , 在C++中,NULL實質是0。

#ifdef__cplusplus #defineNULL0 #else #defineNULL((void*)0) #endif

void*類型指針

void是一種特殊的指針類型,可以用來存放任意對象的地址。一個void指針存放著一個地址,這一點和其他指針類似。不同的是,我們對它到底儲存的是什么對象的地址并不了解。

doublea=2.3; intb=5; void*p=&a; cout<

由于void是空類型,只保存了指針的值,而丟失了類型信息,我們不知道他指向的數據是什么類型的,只指定這個數據在內存中的起始地址,如果想要完整的提取指向的數據,程序員就必須對這個指針做出正確的類型轉換,然后再解指針。

數組和指針

同類型指針變量可以相互賦值,數組不行,只能一個一個元素的賦值或拷貝

數組在內存中是連續存放的,開辟一塊連續的內存空間。數組是根據數組的下進行訪問的。指針很靈活,它可以指向任意類型的數據。指針的類型說明了它所指向地址空間的內存。

數組所占存儲空間的內存:sizeof(數組名) 數組的大小:sizeof(數組名)/sizeof(數據類型),在32位平臺下,無論指針的類型是什么,sizeof(指針名)都是 4 ,在 64 位平臺下,無論指針的類型是什么,sizeof(指針名)都是 8 。

數組名作為右值的時候,就是第一個元素的地址

intmain(void) { intarr[5]={1,2,3,4,5}; int*p_first=arr; printf("%d",*p_first);//1 return0; }

指向數組元素的指針 支持 遞增 遞減 運算。p= p+1意思是,讓p指向原來指向的內存塊的下一個相鄰的相同類型的內存塊。在數組中相鄰內存就是相鄰下標元素。

函數與指針

函數的參數和指針

C語言中,實參傳遞給形參,是按值傳遞的,也就是說,函數中的形參是實參的拷貝份,形參和實參只是在值上面一樣,而不是同一個內存數據對象。這就意味著:這種數據傳遞是單向的,即從調用者傳遞給被調函數,而被調函數無法修改傳遞的參數達到回傳的效果。

voidchange(inta) { a++;//在函數中改變的只是這個函數的局部變量a,而隨著函數執行結束,a被銷毀。age還是原來的age,紋絲不動。 } intmain(void) { intage=60; change(age); printf("age=%d",age);//age=60 return0; }

有時候我們可以使用函數的返回值來回傳數據,在簡單的情況下是可以的,但是如果返回值有其它用途(例如返回函數的執行狀態量),或者要回傳的數據不止一個,返回值就解決不了了。

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

voidchange(int*pa) { (*pa)++;//因為傳遞的是age的地址,因此pa指向內存數據age。當在函數中對指針pa解地址時, //會直接去內存中找到age這個數據,然后把它增1。 } intmain(void) { intage=160; change(&age); printf("age=%d",age);//age=61 return0; }

比如指針的一個常見的使用例子:

#include #include #include voidswap(int*,int*); intmain() { inta=5,b=10; printf("a=%d,b=%d ",a,b); swap(&a,&b); printf("a=%d,b=%d ",a,b); return0; } voidswap(int*pa,int*pb) { intt=*pa;*pa=*pb;*pb=t; }

在以上的例子中,swap函數的兩個形參pa和pb可以接收兩個整型變量的地址,并通過間接訪問的方式修改了它指向變量的值。在main函數中調用swap時,提供的實參分別為&a,&b,這樣就實現了pa=&a,pb=&b的賦值過程,這樣在swap函數中就通過*pa修改了 a 的值,通過*pb修改了 b 的值。因此,如果需要在被調函數中修改主調函數中變量的值,就需要經過以下幾個步驟:

定義函數的形參必須為指針類型,以接收主調函數中傳來的變量的地址;

調用函數時實參為變量的地址;

在被調函數中使用*間接訪問形參指向的內存空間,實現修改主調函數中變量值的功能。

指針作為函數的形參的另一個典型應用是當函數有多個返回值的情形。比如,需要在一個函數中統計一個數組的最大值、最小值和平均值。當然你可以編寫三個函數分別完成統計三個值的功能。但比較啰嗦,如:

intGetMax(inta[],intn) { intmax=a[0],i; for(i=1;ia[i])min=a[i]; } returnmin; } doubleGetAvg(inta[],intn) { doubleavg=0; inti; for(i=0;i

其實我們完全可以在一個函數中完成這個功能,由于函數只能有一個返回值,可以返回平均值,最大值和最小值可以通過指針類型的形參來進行實現:

doubleStat(inta[],intn,int*pmax,int*pmin) { doubleavg=a[0]; inti; *pmax=*pmin=a[0]; for(i=1;ia[i])*pmin=a[i]; } returnavg/n; }

函數的指針

一個函數總是占用一段連續的內存區域,函數名在表達式中有時也會被轉換為該函數所在內存區域的首地址。我們可以把函數的這個首地址賦予一個指針變量,使指針變量指向函數所在的內存區域,然后通過指針變量就可以找到并調用該函數。這種指針就是函數指針。

函數指針的定義形式為:

returnType (*pointerName)(param list);

returnType 為函數返回值類型,pointerNmae 為指針名稱,param list 為函數參數列表。參數列表中可以同時給出參數的類型和名稱,也可以只給出參數的類型,省略參數的名稱,這一點和函數原型非常類似。

用指針來實現對函數的調用:

#include //返回兩個數中較大的一個 intmax(inta,intb) { returna>b?a:b; } intmain() { intx,y,maxval; //定義函數指針 int(*pmax)(int,int)=max;//也可以寫作int(*pmax)(inta,intb) printf("Inputtwonumbers:"); scanf("%d%d",&x,&y); maxval=(*pmax)(x,y); printf("Maxvalue:%d ",maxval); return0; }

結構體和指針

結構體指針有特殊的語法:-> 符號

如果p是一個結構體指針,則可以使用 p ->【成員】 的方法訪問結構體的成員

typedefstruct { charname[31]; intage; floatscore; }Student; intmain(void) { Studentstu={"Bob",19,98.0}; Student*ps=&stu; ps->age=20; ps->score=99.0; printf("name:%sage:%d ",ps->name,ps->age); return0; }

const 和 指針

指向常量的指針,值不能改變,指向可改變

常指針值能改變,指向不可改變

指向常量的常指針,都不能改變

#include intmain() { //1可改變指針 constinta=10; int*p=&a; *p=1000; printf("*p=%d ",*p); //2可改變指針 constb=10; int*pb=&b; pb=p; printf("*pb=%d ",*pb); //3 constc=10; int*constpc=&c; *pc=1000; //pc=pb;不能改變 //4 constd=10; const*intconstpd=&d; //*pd=1000;不能改變 printf(" "); return0; }

深拷貝和淺拷貝

如果2個程序單元(例如2個函數)是通過拷貝 他們所共享的數據的 指針來工作的,這就是淺拷貝,因為真正要訪問的數據并沒有被拷貝。如果被訪問的數據被拷貝了,在每個單元中都有自己的一份,對目標數據的操作相互 不受影響,則叫做深拷貝。

#include usingnamespacestd; classCopyDemo { public: CopyDemo(intpa,char*cstr)//構造函數,兩個參數 { this->a=pa; this->str=newchar[1024];//指針數組,動態的用new在堆上分配存儲空間 strcpy(this->str,cstr);//拷貝過來 } //沒寫,C++會自動幫忙寫一個復制構造函數,淺拷貝只復制指針,如下注釋部分 //CopyDemo(CopyDemo&obj) //{ //this->a=obj.a; //this->str=obj.str;//這里是淺復制會出問題,要深復制 //} CopyDemo(CopyDemo&obj)//一般數據成員有指針要自己寫復制構造函數,如下 { this->a=obj.a; //this->str=obj.str;//這里是淺復制會出問題,要深復制 this->str=newchar[1024];//應該這樣寫 if(str!=0) strcpy(this->str,obj.str);//如果成功,把內容復制過來 } ~CopyDemo()//析構函數 { deletestr; } public: inta;//定義一個整型的數據成員 char*str;//字符串指針 }; intmain() { CopyDemoA(100,"hello!!!"); CopyDemoB=A;//復制構造函數,把A的10和hello!!!復制給B cout<<"A:"<

根據上面實例可以看到,淺復制僅復制對象本身(其中包括是指針的成員),這樣不同被復制對象的成員中的對應非空指針會指向同一對象,被成員指針引用的對象成為共享的,無法直接通過指針成員安全地刪除(因為若直接刪除,另外對象中的指針就會無效,形成所謂的野指針,而訪問無效指針是危險的;

除非這些指針有引用計數或者其它手段確保被指對象的所有權);而深復制在淺復制的基礎上,連同指針指向的對象也一起復制,代價比較高,但是相對容易管理。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據
    +關注

    關注

    8

    文章

    7139

    瀏覽量

    89579
  • C語言
    +關注

    關注

    180

    文章

    7614

    瀏覽量

    137717
  • 變量
    +關注

    關注

    0

    文章

    613

    瀏覽量

    28466

原文標題:C語言指針詳解

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    指針被釋放后就變成了空指針

    指針被釋放后,是不是就變成了空指針?有好多同學提出了這樣的問題。 借用《C專家編程》上面的一段代碼,可以很好的解釋這個問題。 ? ? #include int main(){ char *s
    的頭像 發表于 01-22 09:23 ?72次閱讀

    C語言程序設計教程第4版第8講:指針

    C語言指針講解
    發表于 11-20 14:10 ?0次下載

    C語言指針學習筆記

    本文從底層內存分析,徹底讓讀者明白C語言指針的本質。
    的頭像 發表于 11-05 17:40 ?306次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b><b class='flag-5'>指針</b>學習筆記

    C語言指針運算符詳解

    C語言中,當你有一個指向數組中某個元素的指針時,你可以對該指針執行某些算術運算,例如加法或減法。這些運算可以用來遍歷數組中的元素,如ptr[i]等價于*(ptr + i)。然而,如果
    的頭像 發表于 10-30 11:16 ?306次閱讀

    C語言與Java語言的對比

    C語言和Java語言都是當前編程領域中的重要成員,它們各自具有獨特的優勢和特點,適用于不同的應用場景。以下將從語法特性、內存管理、跨平臺性、性能、應用領域等多個方面對C
    的頭像 發表于 10-29 17:31 ?433次閱讀

    C語言指針詳細解析

    可以對數據本身,也可以對存儲數據的變量地址進行操作。 指針是一個占據存儲空間的實體在這一段空間起始位置的相對距離值。在C/C++語言中,指針
    發表于 09-14 10:03

    面試常考+1:函數指針指針函數、數組指針指針數組

    在嵌入式開發領域,函數指針指針函數、數組指針指針數組是一些非常重要但又容易混淆的概念。理解它們的特性和應用場景,對于提升嵌入式程序的效率和質量至關重要。一、
    的頭像 發表于 08-10 08:11 ?989次閱讀
    面試常考+1:函數<b class='flag-5'>指針</b>與<b class='flag-5'>指針</b>函數、數組<b class='flag-5'>指針</b>與<b class='flag-5'>指針</b>數組

    面試中的高頻問題:指針函數與函數指針,你能完美應對嗎?

    一直覺得C語言較其他語言最偉大的地方就是C語言中的指針,有些人認為
    的頭像 發表于 06-22 08:11 ?1842次閱讀
    面試中的高頻問題:<b class='flag-5'>指針</b>函數與函數<b class='flag-5'>指針</b>,你能完美應對嗎?

    Keil+C51中對雙數據指針的直接利用

    Keil+C51中對雙數據指針的直接利用
    發表于 06-18 10:15 ?0次下載

    PLC編程語言C語言的區別

    在工業自動化和計算機編程領域中,PLC(可編程邏輯控制器)編程語言C語言各自扮演著重要的角色。盡管兩者都是編程語言,但它們在多個方面存在顯著的區別。本文將從多個維度深入探討PLC編程
    的頭像 發表于 06-14 17:11 ?3235次閱讀

    提高C代碼可讀性的編寫技巧與策略

    指針C 語言的靈魂,是 C 比其他語言更靈活,更強大的地方。所以學習 C
    發表于 04-23 18:25 ?577次閱讀

    C語言函數指針六大應用場景詳解

    函數指針是一種非常強大的編程工具,它可以讓我們以更加靈活的方式編寫程序。在本文中,我們將介紹 6 個函數指針的高級應用場景,并貼出相應的代碼案例和解釋。
    的頭像 發表于 04-23 18:19 ?970次閱讀

    C語言基礎-為什么要使用C

    當今最流行的 Linux 操作系統和 RDBMS(Relational Database Management System:關系數據庫管理系統) MySQL 都是使用 C 語言編寫的。
    發表于 03-25 11:20 ?489次閱讀

    C語言指針用法

    C語言編程中善用指針可以簡化一些任務的處理,而對于一些任務(比如動態內存分配),必須要有指針才行的。也就是說精通C
    發表于 03-05 14:22 ?391次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言</b>的<b class='flag-5'>指針</b>用法

    怎么理解指針指針

    怎么理解指針指針?其實這個概念并不難,只是把它放到實際應用中,容易造成困擾。
    的頭像 發表于 02-23 16:46 ?1286次閱讀
    怎么理解<b class='flag-5'>指針</b>的<b class='flag-5'>指針</b>?
    百家乐官网视频麻将游戏| 百家乐英皇娱乐场开户注册| 三亚市| 香港百家乐六合彩| 百家乐官网破解辅助| AG百家乐大转轮| 百家乐官网视频游戏盗号| 黄金百家乐的玩法技巧和规则| 真人百家乐官网分析软件是骗局 | 香港六合彩开奖历史记录| 百家乐路子分析| 衡山县| 自贡百家乐赌场娱乐网规则| 可以玩百家乐官网的博彩公司| 太阳城sun866| 百家乐官网真人秀| 大发888充钱| 百家乐官网7人桌布| 潮安县| 百家乐赢钱公式论| 缅甸百家乐官网的玩法技巧和规则 | 百家乐官网赌场视屏| 金利娱乐城代理| 最好的百家乐好评平台都有哪些| 百家乐官网庄闲几率| 大发888怎么能不卡| 虎和鼠做生意和财吗| 奔驰百家乐官网游戏| 大发888娱乐城加速器| 百家乐官网资深 | 清远市| 威尼斯人娱乐城投注| 24葬书-葬法| 独赢百家乐官网全讯网| 大发888娱乐城建账号| 百家乐赢钱皇冠网| 川宜百家乐官网注册号| 大发888游戏代冲省钱技巧| 真人百家乐怎么对冲| 百家乐官网平注胜进与负追| 蒙特卡罗国际网址|