在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

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

3天內不再提示

C語言中指針的定義與使用

C語言編程學習基地 ? 來源:C語言編程學習基地 ? 作者:C語言編程學習基地 ? 2022-10-13 17:00 ? 次閱讀

指針也就是內存地址,指針變量是用來存放內存地址的變量,不同類型的指針變量所占用的存儲單元長度是相同的,而存放數據的變量因數據的類型不同,所占用的存儲空間長度也不同。有了指針以后,不僅可以對數據本身,也可以對存儲數據的變量地址進行操作。

計算機中所有的數據都必須放在內存中,不同類型的數據占用的字節數不一樣,例如 int 占用 4 個字節,char 占用 1 個字節。為了正確地訪問這些數據,必須為每個字節都編上號碼,就像門牌號、身份證號一樣,每個字節的編號是唯一的,根據編號可以準確地找到某個字節。

在計算機中, 所有的數據都是存放在存儲器中的, 不同的數據類型占有的內存空間的大小各不相同。內存是以字節為單位的連續編址空間, 每一個字節單元對應著一個唯一的編號, 這個編號被稱為內存單元的地址。比如: int類型占兩個字節, char類型占1個字節等。內存為變量分配存儲空間的首個字節單元的地址, 稱之為該變量的地址。地址用來標識每一個存儲單元, 方便用戶對存儲單元中的數據進行正確的訪問。在高級語言中地址形象地稱為指針, CPU 訪問內存時需要的是地址,而不是變量名和函數名!變量名和函數名只是地址的一種助記符,當源文件被編譯和鏈接成可執行程序后,它們都會被替換成地址。編譯和鏈接過程的一項重要任務就是找到這些名稱所對應的地址。

需要注意的是 變量名和函數名為我們提供了方便,讓我們在編寫代碼的過程中可以使用易于閱讀和理解的英文字符串,不用直接面對二進制地址,那場景簡直讓人崩潰。雖然變量名、函數名、字符串名和數組名在本質上是一樣的,它們都是地址的助記符,但在編寫代碼的過程中,我們認為變量名表示的是數據本身,而函數名、字符串名和數組名表示的是代碼塊或數據塊的首地址。

指針相對于一個內存單元來說,指的是單元的地址,該單元的內容里面存放的是數據。在C語言中,允許用指針變量來存放指針,因此,一個指針變量的值就是某個內存單元的地址或稱為某內存單元的指針。

eb8f1c20-4ad2-11ed-a3b6-dac502259ad0.png

指針的定義與使用

變量的指針與指針變量:

在C語言中,允許用一個變量來存放指針,這種變量稱為指針變量。指針變量的值就是某份數據的地址,這樣的一份數據可以是數組、字符串、函數,也可以是另外的一個普通變量或指針變量

變量的指針就是變量的存儲地址,指針變量就是存儲指針的變量。指針變量是存放一個變量地址的變量,不同于其他類型變量,它是專門用來存放內存地址的,也稱為地址變量。定義指針變量的一般形式為:類型說明符 * 變量名 *

類型說明符表示指針變量所指向變量的數據類型;* 表示這是一個指針變量;變量名表示定義的指針變量名,其值是一個地址,例如:char * p1;表示p1是一個指針變量,它的值是某個字符變量的地址

//定義指針變量與定義普通變量非常類似,不過要在變量名前面加星號*,格式為:
int *fp;//*表示這是一個指針變量,fp是一個指向int數據類型的指針
float *a,*b; //表示a和b都是指針變量,都指向一個為float數據類型的指針

指針變量的使用:

取地址運算符&:單目運算符&是用來取操作對象的地址。

例:&i 為取變量 i 的地址。對于常量表達式、寄存器變量不能取地址(因為它們存儲在存儲器中,沒有地址)。

指針運算符 * (間接尋址符):與&為逆運算,作用是通過操作對象的地址,獲取存儲的內容。

例:x = &i x 為 i 的地址,*x 則為通過 i 的地址,獲取 i 的內容。

//賦值
int a = 100;//定義了一個a的整形變量
int *p_a = &a;//將一個int類型的指針p_a,p_a指向了a(也叫p_a指向了a的地址)
在定義指針變量 p_a 的同時對它進行初始化,并將變量 a 的地址賦予它,此時 p_a 就指向了 a。
值得注意的是,p_a 需要的一個地址,a 前面必須要加取地址符&,否則是不對的。

和普通變量一樣,指針變量也可以被多次寫入,只要你想,隨時都能夠改變指針變量的值:

//定義普通變量
float a = 99.5, b = 10.6;
char c = '@', d = '#';
//定義指針變量
float *p1 = &a;//P1指向a的地址
char *p2 = &c;//p2指向c的地址
//修改指針變量的值
p1 = &b;//將p1改變指向為b
p2 = &d;//將p2改變指向為a

* 是一個特殊符號,表明一個變量是指針變量,定義 p1、p2 時必須帶 *。而給 p1、p2 賦值時,因為已經知道了它是一個指針變量,就沒必要多此一舉再帶上 *,后邊可以像使用普通變量一樣來使用指針變量。也就是說,定義指針變量時必須帶 *,給指針變量賦值時不能帶 *。

//注意
不允許把一個數賦予指針變量
int *p;
p = &a;
*p = 100;//這樣是錯誤的


或者:
int b=200;
int *a;
a=b; //這樣也錯誤,因為沒有加上取地址符&

指針變量存儲了數據的地址,通過指針變量能夠獲得該地址上的數據:

#include 
int main(){
    int a = 66;//定義整形變量
    int *p = &a; //定義int的指針變量并指向a變量的地址
    printf("%d, %d
", a, *p);  //兩種方式都可以輸出a的值
    return 0;
}


//假設 a 的地址是 0X1000,p 指向 a 后,p 本身的值也會變為 0X1000,*p 表示獲取地址 0X1000 上的數據,
也即變量 a 的值。所以從運行結果看,*p 和 a 是等價的。


CPU 讀寫數據必須要知道數據在內存中的地址,普通變量和指針變量都是地址的助記符,雖然通過 *p 和 a 獲取到的數據一樣,
但它們的運行過程稍有不同:a 只需要一次運算就能夠取得數據,而 *p 要經過兩次運算,多了一層“間接”。


//程序被編譯和鏈接后,a、p 被替換成相應的地址。使用 *p 的話,要先通過地址 0XF0A0 取得變量 p 本身的值,
這個值是變量 a 的地址,然后再通過這個值取得變量 a 的數據


也就是說,使用指針是間接獲取數據,使用變量名是直接獲取數據,前者比后者的代價要高。

可以用指針來改變被指向那個變量的值 如:

#include 
int main(void){
    int a = 1, b = 66, c = 2;//定義普通變量
    int *p = &a;  //定義指針變量并指向a的地址
    *p = b;  //通過指針變量將a的值改變了 (因為在這里,*p指向了a 就等于*p和a身處同一個內存空間了,
                                                                所以對*p修改 就相當于對a修改)
    c = *p;  //把指針p的值的賦值給了C (根據前面說的,相當于將a賦值給了C)
    printf("%d, %d, %d, %d
", a, b, c, *p);//所以他們的值都是同一個了
    return 0;
}
*在不同的場景下有不同的作用:*可以用在指針變量的定義中,
表明這是一個指針變量,以和普通變量區分開;使用指針變量時在前面加*表示獲取指針指向的數據,或者說表示的是指針指向的數據本身。

也就是說,定義指針變量時的*和使用指針變量時的*意義完全不同。以下面的語句為例:

int *p = &a;//這里表示指向a的地址
*p = 100;  //這里表示獲取指針所指向的數據

其他一些騷操作:

int x, y, *px = &x, *py = &y;
y = *px + 5;  //表示把x的內容加5并賦給y,*px+5相當于(*px)+5
y = ++*px;  //px的內容加上1之后賦給y,++*px相當于++(*px)
y = *px++;  //相當于y=(*px)++
py = px;  //把一個指針的值賦給另一個指針

關于“&”和“*”

“&”和“ * ”都是右結合的。假設有變量 x = 10,則*&x 的含義是,先獲取變量 x 的地址,再獲取地址中的內容。因為“&”和“ * ”互為逆運算,所以 x = *&x。

假設有一個 int 類型的變量 a, pa 是指向a的指針,那么*&a和&*pa分別是什么意思呢?

*&a可以理解為*(&a),&a表示取變量 a 的地址(等價于 pa),*(&a)表示取這個地址上的數據(等價 * pa),繞來繞去,又回到了原點,*&a仍然等價于 a。

&*pa可以理解為&(*pa),*pa表示取得 pa 指向的數據(等價于 a),&(*pa)表示數據的地址(等價于 &a),所以&*pa等價于 pa。

野指針與空指針

空指針是說,這個指針沒有指向一塊有意義的內存,比如說:char* k; 這里這個k就叫空指針.我們并未讓它指向任意點.

又或者char* k = NULL; 這里這個k也叫空指針,因為它指向NULL 也就是0,注意是整數0,不是'?'

一個空指針我們也無法對它進行取內容操作,空指針只有在真正指向了一塊有意義的內存后,我們才能對它取內容.也就是說要這樣 k = "hello world!"; 這時k就不是空指針了.

對于空指針值,一般的文檔中傾向于用 NULL 表示,而沒有直接說成 0。但是我們應該清楚:對于指針類型來說,返回 NULL 和 返回 0 是完全等價的,因為 NULL 和 0 都表示 “null pointer”(空指針)。一句話, 空指針是什么,就是一個被賦值為0的指針,在沒有被具體初始化之前,其值為0.(百度解釋)

如 :

int *a;//定義一個指針a=NULL;//讓這個指針指向空a =0;//這樣也是讓一個指針指向空的方式

注意:void* 這不叫空指針,這叫無確切類型指針.這個指針指向一塊內存,卻沒有告訴程序該用何種方式來解釋這片內存.所以這種類型的指針不能直接進行取內容的操作.必須先轉成別的類型的指針才可以把內容解釋出來.

還有'?',這也不是空指針所指的內容. '?'是表示一個字符串的結尾而已,并不是NULL的意思

void*因為是表示不知道要指向什么東西的指針,計算時于char相同(但不相通)

野指針不同于空指針,空指針是指一個指針的值為null,而野指針的值并不為null,野指針會指向一段實際的內存,只是它指向哪里我們并不知情,或者是它所指向的內存空間已經被釋放,所以在實際使用的過程中,我們并不能通過指針判空去識別一個指針是否為野指針。避免野指針只能靠我們自己養成良好的編程習慣

野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)指針變量在定義時如果未初始化,其值是隨機的,指針變量的值是別的變量的地址,意味著指針指向了一個地址是不確定的變量,此時去解引用就是去訪問了一個不確定的地址,所以結果是不可知的。(百度解釋)

下面說說哪些情況下會產生野指針,以及怎樣避免。

1、指針變量的值未被初始化: 聲明一個指針的時候,沒有顯示的對其進行初始化,那么該指針所指向的地址空間是亂指一氣的。如果指針聲明在全局數據區,那么未初始化的指針缺省為空,如果指針聲明在棧區,那么該指針會隨意指向一個地址空間。所以良好的編程習慣就是在聲明指針的時候就對其進行初始化,如果暫時不知道該初始化成什么值,就先把指針置空。

int main(void){
int *a;//野指針


if(a!=NULL){
....
}


/*
int *a; 
a=NULL/0;  正確的引用
*/
}

2、指針所指向的地址空間已經被free或delete:在堆上malloc或者new出來的地址空間,如果已經free或delete,那么此時堆上的內存已經被釋放,但是指向該內存的指針如果沒有人為的修改過,那么指針還會繼續指向這段堆上已經被釋放的內存,這時還通過該指針去訪問堆上的內存,就會造成不可預知的結果,給程序帶來隱患,所以良好的編程習慣是:內存被free或delete后,指向該內存的指針馬上置空。

void func()
{
    int *ptr = new int[5];
    delete [ ]ptr;
    // 執行完delete后,ptr野指針


  //還應該這樣做:ptr=NULL; 正確


}

3、指針操作超越了作用域,如果在一個程序塊中讓一個指針指向那個塊中的某個變量,但是那個變量只是在塊中有效,出了那個程序塊,此變量就無效了,此時指向它的指針也就變成了野指針

void func()
{
    int *ptr = nullptr;
    {
        int a = 10;
        ptr = &a;
    } // a的作用域到此結束


    int b = *ptr;    // ptr指向的a,但是a已經被回收,所以ptr變成野指針
  //還應該這樣做:ptr=NULL; 正確
}

所以 使用指針時應當注意”規避“:初始化時置 NULL,釋放時置 NULL

3、指針的運算

C 指針是一個用數值表示的地址。因此,您可以對指針執行算術運算。可以對指針進行四種算術運算:++、--、+、-。遞增遞減加減,兩個指針相減

#include
int main(void) {
  int a=10; 
  int *pa = &a,*pA=&a;


  double b = 22.2;
  double *pb = &b;


  char c = 'C';
  char *pc = &c;


  //最初的值
  printf("1- %#x %#x %#x 
", &a, &b, &c);//%#x表示 轉換成十六進制帶格式輸出地址,
                                                 //效果為在輸出前加0x
  printf("2- %#x %#x %#x 
", pa, pb, pc);


  //指針加法
  pa += 2; pb += 2; pc += 2;
  printf("3- %#x %#x %#x 
", pa, pb, pc);


  //指針減法
  pa -= 2; pb -= 2; pc -= 2;
  printf("4- %#x %#x %#x
", pa, pb, pc);


  //指針的比較
  if (pa == pA) {
    printf("5=%d
", *pA);


  }
  else {
    printf("6=%d
", *pa);
  }


  return 0;


}


//從運算結果可以看出:pa、pb、pc 每次加 1,它們的地址分別增加 4、8、1,正好是 int、double、char 類型的長度;
                            減 2 時,地址分別減少 8、16、2,正好是 int、double、char 類型長度的 2 倍。


/*簡單的概括就是:
指針的每一次遞增,它其實會指向下一個元素的存儲單元。
指針的每一次遞減,它都會指向前一個元素的存儲單元。
指針在遞增和遞減時跳躍的字節數取決于指針所指向變量數據類型長度,比如 int 就是 4 個字節。




指針變量除了可以參與加減運算,還可以參與比較運算。當對指針變量進行比較運算時,比較的是指針變量本身的值,也就是數據的地址。
如果地址相等,那么兩個指針就指向同一份數據,否則就指向不同的數據。當然還有其他邏輯運算符


上面的代碼(第一個例子)在比較 pa 和 pA 的值時,pa 已經指向了 a 的上一份數據,所以它們不相等。而 a 的上一份數據又不知道是什么,
所以會導致 printf() 輸出一個沒有意義的數,這正好印證了上面的觀點,不要對指向普通變量的指針進行加減運算


注意:不能對指針變量進行乘法、除法、取余等其他運算,除了會發生語法錯誤,也沒有實際的含義。
#include//指針的加減法其實上的地址上的移動
int main(void) {
  char a[] = {2,3,4,5,6};
  char *p = &a[0];
  char *p1 = &a[10];


  printf("p1-p=%d
", p1 - p);
  printf("p=%p
", p);
  printf("p1=%p
", p1);


  //這里如果運算為大于零,就是真 輸出 0
  //如果運算為小于零,就是假 輸出   -1
  int *t = a[0];
  int *k = a[3];
  printf("* %d
", t-k);


  int b[] = { 1,2,3,4,5,6 };
  int *q = &b[0];
  int *q1 = &b[6];
  printf("q1-q=%d
", q1 - q);
  printf("%p
", q1);
  printf("%p
", q);


  return 0;
}

常見的指針運算:

*(++p): 先移動指針,取下一個單元的值

*(p++): 先取出當前單元的值,再移動指針

( * p)++ : 先取出當前單元的值,再使當前單元的值加1 (指針不移動)

++( * p) : 先使當前單元的值加1,再取出當前單元的值 (指針不移動)

指針的類型轉換:

int *p=&i;

void *q=(void * )p;

這并沒有改變p所指向的變量的類型,而是讓后人用不同的眼光通過p看它所指的變量

意思是:這里的p我不再當你是int了,認為你就是個void類型的

注意 由于優先級的問題 *p++和 * (p++)是等價的

取地址符 &補充:

獲得變量的地址,它的操作必須是變量,

int i,printf("%x",&i); 取得i的地址并輸出。

int i,printf("%p",&i); 取得i的地址并輸出。

地址的大小是否于int相同取決于編譯器

注意 使用指針的時候的類型,無論指向什么類型,所有的指針的大小都是一樣的,因為都是地址,但是指向不同類型的指針是不能相互賦值的,這是為了避免用錯指針。

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

    關注

    13

    文章

    4314

    瀏覽量

    85851
  • C語言
    +關注

    關注

    180

    文章

    7604

    瀏覽量

    136845
  • 指針
    +關注

    關注

    1

    文章

    480

    瀏覽量

    70564

原文標題:【零基礎學C語言】知識總結十:指針及其相關知識(一)

文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學習基地】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    C語言中指針定義

    上一節中,我們利用了一個小知識來介紹了一下指針,在上面這個例子中,小明和小麗手中的這個杯子就好比我們C語言中的變量,它確實是實實在在存放一些有具體意義的數據。這個杯套就類似于C
    發表于 08-15 16:24 ?1823次閱讀
    <b class='flag-5'>C</b><b class='flag-5'>語言中指針</b>的<b class='flag-5'>定義</b>

    C語言中指針的命令行參數

    指針C語言最重要也是最難理解的部分,它在我們平時的工作中無處不在。
    的頭像 發表于 09-26 10:18 ?1154次閱讀

    C語言中void指針的基本認識及典型應用

    C語言中,*類型就是指針類型,比如 int *p,double *q,雖然是不一樣的指針,但是大小卻一樣sizeof(p) == sizeof(q),因為它們都是同一種類型*類型的。
    發表于 02-01 09:26 ?1101次閱讀

    C語言中指針函數和函數指針的概念及應用示例

    C語言中指針函數和函數指針是強大且常用的工具。它們允許我們以更靈活的方式處理函數和數據,進而擴展程序的功能。
    發表于 08-16 16:14 ?903次閱讀

    C語言中多級指針的概念和使用方法

    多級指針C語言中是一種特殊的指針類型,它可以指向其他指針指針
    發表于 08-16 16:16 ?1107次閱讀

    C語言中指針的基本概念和用法

    C語言中指針是一項重要的概念,它允許我們直接訪問和操作內存地址。
    發表于 08-17 15:30 ?781次閱讀

    談談C語言中指針有什么好處,請各位高手們談談自己的體會

    談談C語言中指針有什么好處,請各位高手們談談自己的體會...
    發表于 09-01 17:38

    語言中指針的介紹

    的內容。在C語言中, 允許用一個變量來存放指針,這種變量稱為指針變量。因此,一個指針變量的值就是某個內存單元的地址或稱為某內存單元的指針。圖
    發表于 09-10 14:22

    C語言中指針的介紹非常詳細

    C語言中指針的介紹非常詳細 C語言中指針的介紹非常詳細
    發表于 12-25 10:39 ?57次下載

    c語言函數指針定義,指針函數和函數指針的區別

     往往,我們一提到指針函數和函數指針的時候,就有很多人弄不懂。下面就由小編詳細為大家介紹C語言中函數指針
    發表于 11-16 15:18 ?3627次閱讀

    基于C語言中指針的基本用法解析

    C語言中其它的知識都學得可以,唯獨指針搞不懂。如果是這樣,我可以很負責的告訴你,對于這門編程語言,你等于是沒學。所以學好指針對于初學者是非
    的頭像 發表于 01-09 15:12 ?4801次閱讀

    C語言中的野指針是怎么來的?

    一、什么是野指針指針C語言的靈魂,同時也是很容易讓人犯錯的重難點,用錯了指針將是一個災難。 指針
    的頭像 發表于 06-01 16:43 ?2562次閱讀

    C語言中指針(重點)超詳細

    C語言中指針1、指針是什么2、指針指針類型2.1、指針
    發表于 01-13 14:10 ?11次下載
    <b class='flag-5'>C</b><b class='flag-5'>語言中</b>的<b class='flag-5'>指針</b>(重點)超詳細

    C語言中什么是指針數組

    C語言中一個數組,若其元素均為指針類型數據,稱為指針數組,也就是說,指針數組中的每一個元素都存放一個地址,相當于一個
    的頭像 發表于 03-10 15:26 ?1774次閱讀

    簡述C語言中指針重點

    C語言中一個函數可以返回一個整型值、字符值、實型值等,也可以返回指針型的數據,即地址,其概念與以前類似,只是返回的值的類型是指針類型。
    的頭像 發表于 03-10 15:28 ?609次閱讀
    主站蜘蛛池模板: 99久久免费精品视频| 亚洲成人一级片| 亚洲国产成人久久三区| 美女被免费网站视频九色| 亚洲午夜久久| 傲视影院午夜毛片| 四虎影院观看视频| 国产精品高清一区二区三区| 黄色靠逼网站| 亚洲欧洲色天使日韩精品| 黄频网站免费大全在线观看| 久热福利| 一个人看aaaa免费中文| 国产亚洲新品一区二区| 国产伦精品一区二区三区免| 曰本三级香港三级人妇99视频| 久久精品五月天| 国产偷啪视频一区| 亚洲成a人片毛片在线| 成人的天堂视频一区二区三区| 午夜色a大片在线观看免费| 四虎影院永久| 天天宗合网| 最好看免费中文字幕2018视频| 久久免费手机视频| 国产免费福利网站| 四虎国产精品永久在线| 婷婷五月天.com| 国产在线精品一区免费香蕉| 狠狠色综合久久婷婷| 性夜影院爽黄a爽免费看网站| 色资源在线观看| 日日拍拍| 性欧美性free| 天天干小说| 伊人最新网址| 337p亚洲精品色噜噜狠狠| 视频一区在线观看| 精品卡1卡2卡三卡免费视频| 国产精品福利午夜在线观看| 色妞视频资源在线观看|