
擎星逆行亦無懼,焚膏繼晷恒窮年!
文章目錄
- 前言
- 正文
- 一、指標型別的引出
- 二、指標和指標變數
- 1、指標的地位
- 2、什么是指標
- 3、什么是指標變數
- 三、指標型別
- 1、指標型別的介紹
- 2、指標型別的意義
- 1、指標的解參考
- 2、指標的前后移動
- 四、野指標
- 1、野指標概念
- 2、野指標的成因
- 1、指標未初始化
- 2、指標越界
- 3、指標指向空間釋放
- 4、無效指標
- 3、如何規避野指標
- 五、指標的運算
- 1、指標+-整數
- 2、指標- 指標
- 3、指標的關系運算
- 六、指標和陣列
- 七、二級指標
- 八、指標陣列
- 結語
前言
Hello everyone ! forever 今天開始跟大家分享一下自己之前學習指標的一些知識和一些經驗,這里指標當時我是分為兩個階段來學的,一個就是今天要給大家介紹的指標初階,還有一個就是后面要給大家介紹的指標進階版,
說到指標,很多人其實挺頭疼的,確實 forever 第一次接觸到指標的時候其實和你們一樣的,哈哈哈哈哈,直接腦仁兒疼,啊哈哈哈,但是當你靜下心來時,先去學其簡單的知識,再一步一步往深走,你就會發現慢慢的就會得心應手啦!因此,別怕,克服恐懼的最好方法就是面對他并且戰勝它,
故而希望今天這篇指標初階能帶大家入門,給大家更多的啟發和幫助,助大家尋找到學習指標的方向!
正文
一、指標型別的引出
之前的函式內容里面,forever 給大家講了C語言在函式呼叫時,引數傳遞有兩種方式,一種是傳值呼叫,引數在傳遞時一般采用的都是值傳遞的方式,此時被調函式只對形參進行修改,不會回傳到主調函式,不會修改主調程式中實參變數的值,但在實際應用中,有時候還需要將被調函式對形參的修改結果回傳到主調函式,使得實參變數的值直接被修改,這里就出現了另一種傳址呼叫,C語言中可以將變數的地址作為實參傳遞給被調函式,這時候被調函式修改該形參存盤的地址時就相當于直接修改主調函式里面實參的地址值上的值,這就起到了修改實參的值,像這樣的形參儲存的是地址值,它的型別就是“地址型別”,C語言把這種型別稱為指標型別,
二、指標和指標變數
1、指標的地位
指標是C語言中的一個重要概念,它是C語言的精華之一,用指標可以實作函式引數的參考傳遞,減少傳遞引數的開銷,還能直接對記憶體地址進行操作,實作動態存盤管理;使得程式簡潔、緊湊、高效、靈活,
2、什么是指標
指標是記憶體中最小單元的一個編號,也就是地址,變數的指標也就是變數的地址,
我們口頭上常說的指標指的是指標變數,指標變數——用來存放地址的變數,
代碼示例如下:
#include <stdio.h>
int main()
{
int a = 10;
int* p;//定義指標變數p
p = &a;//給指標變數p賦地址值
printf("%p\n%p\n%d\n", p, &a, *p);
return 0;
}
運行結果:

總結:
1、這里int* 是變數p的型別 ,p 是指標變數,是用來存放地址的變數;
2、p 等于 &a,因此說明 p 中是 a 的地址,*p等于 a 的值,所以 *p 中就是 a 的值,
3、什么是指標變數
指標變數顧名思義,就是一個變數,因此必須先定義后使用,
指標變數:我們可以通過&(取地址運算子)取出變數的記憶體真實地址,把地址可以存放到一個變數中,這個變數就是指標變數,
總結:指標變數,是用來存放地址的變數,(存放在指標變數中的值都被當作地址處理)
三、指標型別
1、指標型別的介紹
指標變數是用來存放地址的,該地址所代表的變數可能是各種不同型別的,所以,定義一個指標變數時候,必須同時指定它所指向的變數的型別,稱之為基型別,這里就有了指標型別之說,雖然指標變數存放的都是地址,但如果指向的基型別不同,指標變數的型別也就不同,故不能互相賦值,
#include <stdio.h>
int main()
{
int a = 10;
char b = 20;
double c = 30;
int* p1;// int* 型別指標變數
p1 = &a;
char* p2;//char* 型別指標變數
p2 = &b;
double* p3;//double* 型別指標變數
p3 = &c;
printf("sizeof(p1) = %d\nsizeof(p1) = %d\nsizeof(p1) = %d\n", sizeof(p1), sizeof(p2), sizeof(p3));
return 0;
}
在32位機上運行結果:

在64位機上運行結果:

總結:
1、int*、char* 等這是變數的型別;
2、指標的大小在32位機上是4個位元組,在64位機上是8個位元組,
2、指標型別的意義
1、指標的解參考
解參考:就是參考它所指向的變數的值
(1)int* 型別的解參考(這里主要看其記憶體發生的變化)
#include <stdio.h>
int main()
{
int a = 0x22334455;
int* p = &a;
*p = 0;//解參考,給 a 賦值為0
return 0;
}
int* 型別解參考記憶體變化如下兩張圖,解參考前:此時給a賦值為0x22334455,在記憶體中是倒著存放的如下的 55 44 33 22

解參考后:在 int* 型別這里解參考后直接四個位元組都被改變了,其值都變為00 00 00 00

(2)char* 型別解參考
#include <stdio.h>
int main()
{
int a = 0x99887766;
char* p = (char*)&a;//型別轉換
*p = 0;//解參考操作
return 0;
}
char* 型別解參考記憶體變化如下兩張圖,解參考前:a 中賦值為0x99887766,記憶體中存放的是66 77 88 99

解參考后:給 a 賦值為0,記憶體中變化為00 77 88 99

總結:
指標型別決定了在解參考時候一次能訪問幾個位元組(指標的權限) int* 和float* 型別一次能訪問4個位元組 char* 一次能訪問一個位元組,
2、指標的前后移動
#include <stdio.h>
int main()
{
int a = 10;
int* p1 = &a;
printf("%p\n%p\n%p\n", p1 - 1, p1, p1 + 1);//加一代表指標向后走一步
char b = 20;
char* p2 = &b;
printf("%p\n%p\n%p\n",p2-1, p2, p2 + 1);//減一代表指標向前走一步
return 0;
}
運行結果:

總結:
1.指標型別決定了指標向前或向后走一步,走多大距離(單位是位元組);
2.這里int 型別向前或向后走4個位元組,char 向前向后走1個位元組,
例題:利用指標列印陣列
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
for (int i = 0; i < 10; i++)
{
scanf("%d", p + i);//p+i等價于&arr[i]
}
for (int i = 0; i < 10; i++)
{
p + i;//p+i實作地址后移動
printf("%d ", *(p+i));//*(p+i)即可以列印每一位地址上的值
}
return 0;
}
運行結果:

四、野指標
1、野指標概念
野指標就是指標指向位置是不可知的(隨機的、不正確的、沒有明確的),
2、野指標的成因
1、指標未初始化
全域變數未初始化,系統會給其默認值為0;區域變數未初始化,系統會給其隨機值,
錯誤示例: 指標未初始化示例
#include <stdio.h>
int main()
{
int* p; //野指標
*p = 20;//此時直接給其賦值是錯誤操作
return 0;
}
vs2019 報警告資訊:

2、指標越界
這里一般就和陣列越界有關,forever 之前在陣列里面詳細介紹過陣列越界啦!因此,這里就不在過多浪費大家時間啦~
3、指標指向空間釋放
int* test()
{
int a = 20;
return &a;//屬于回傳區域變數的地址
}
int main()
{
int* p=test();
printf("hehe\n");
printf("%d", *p);
return 0;
}
運行結果:

總結:
1.當a開辟一塊空間之后,然后test函式結束之后空間就釋放了,此時你主函式里的陳述句再去訪問就無法訪問到其地址,這里就屬于野指標
2.如果按照正常思維這里輸出的值是20,但是這里是野指標現象,因此出現錯誤,不過大家注意這里輸出的 5 只是隨機值,
4、無效指標
int main()
{
int* p=NULL;//NULL指的是空指標,NULL實際上和0一個意思
return 0;
}
3、如何規避野指標
1、指標初始化
2、小心指標越界
3、指標指向的空間釋放及時的置為空指標
4、避免返回區域變數的地址
5、指標使用前檢查有效性,在指標不為空的時候才使用它
五、指標的運算
1、指標±整數
(上面有講解過)
這里就是前面指標型別里面講的指標的前后移動,實際上就是指標加減整數運算,
利用指標±整數求字串長度
//利用指標+-求字串長度
#include <stdio.h>
int my_strlen(char* ch)
{
int count = 0;
while (*ch != '\0')
{
count++;//這里是計數器
ch++;//指標++,計數器統計一次,地址向下走一位
}
return count;
}
int main()
{
char ch[] = "abcdef";
int len = my_strlen(ch);
printf("%d\n", len);
return 0;
}
運行結果:

2、指標- 指標
前提:兩個指標指向的是同一塊空間
指標-指標=的到陣列的長度
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
printf("%d\n", &arr[0] - &arr[9]);
return 0;
}
運行結果:

利用指標-指標求字串長度
//利用指標-指標求字串長度
#include <stdio.h>
int my_strlen(char* ch)
{
char* first = ch;//定義指標變數first來存盤陣列中第一個字串的位置
while (*ch != '\0')
{
ch++;//依次向后,從而獲取最后一個字串的位置
}
return ch-first;//最后一個字串的位置減去初始位置,就是字符個數,即字串長度
}
int main()
{
char ch[] = "abcdefgh!";
int len = my_strlen(ch);
printf("%d\n", len);
return 0;
}
運行結果:

3、指標的關系運算
來看看一段代碼:
//指標的關系運算
#include <stdio.h>
#define N 5
int main()
{
int arr[N];
int* p = arr;
int* i;
for (i = &arr[N]; i > &arr[0];)
{
*--p=0;
}
return 0;
}
分析圖如下:

錯誤版本,再來看看這段代碼簡化版:
//指標的關系運算錯誤展示
#include <stdio.h>
#define N 5
int main()
{
int arr[N];
int* p = arr;
int* i;
for (i = &arr[N-1]; i >= &arr[0];i--)
{
*i=0;
}
return 0;
}
分析圖如下:

總結:
標準規定:
允許向陣列元素的指標與指向陣列最后一個元素后面的那個記憶體位置的指標比較,但是不允許與指向第一個元素之前的那個記憶體位置的指標進行比較,
六、指標和陣列
指標——地址
陣列——一組相同型別的資料
指標和陣列本身是兩種事物,其聯系是:陣列可以通過指標來訪問
來看看一下代碼:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (int i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));//通過指標列印陣列的值
}
printf("\n");
for (int i = 0; i < sz; i++)
{
printf("%p==%p\n", p + i, &arr[i]);//通過指標直接訪問陣列的地址
}
return 0;
}
運行結果:

七、二級指標
定義一個a變數需要開辟一塊空間,然后將a地址存到指標變數pa里面去,int pa里的pa是int*型別的變數,既然為變數,那么也會開辟一塊空間,pa也有自己的地址,
二級指標變數就是存放一級指標地址的一個變數,上代碼:
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa; //ppa是一個二級指標,,用來存放一級指標pa的地址
int*** pppa = &ppa; //pppa是一個三級指標,用來存放二級指標ppa的地址
printf("%p\n%p\n%p\n", pa, ppa,pppa);
printf("\n");
printf("%p\n%p\n%p\n", &a, &pa,&ppa);
return 0;
}
運行結果:

解釋說明:
int** ppa = &pa;
這里最后一個是說明ppa是指標變數,第一個和int 連接在一起說明ppa的指向物件是pa,也指pa是int型別的
int** pppa = &pa;
同理最后一個 * 是指pppa是一個指標變數,前兩個 ** 和int連接在一起說明pppa指向的物件是ppa,也指ppa是 int** 型別的
畫圖分析:

通過修改多級指標的地址從而修改初始地址的值:
//通過修改多級指標地址從而修改其值
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;
int*** pppa = &ppa;
***pppa = 100;
printf("%d\n", a);
return 0;
}
運行結果:

總結:
二級指標是用來存放一級指標變數的地址的
三級指標是用來存放二級指標變數的地址的
八、指標陣列
指標陣列實際上是陣列,是存放指標的陣列,它的每一個元素都是指標,
看代碼分析:
#include<stdio.h>
int main()
{
int arr[10];//整型陣列—存放整型的陣列
char brr[10];//字符陣列—存放字符的陣列
int a = 10;
int b = 20;
int c = 30;
//指標陣列—存放指標的陣列
int* crr[5] = { &a,&b,&c };//指標陣列—存放整型指標的陣列,這里只存了三個指標,陣列其他空間都默認為空指標
return 0;
}
如圖分析:

簡單的使用:
通過指標陣列找到陣列里元素對應的值
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* crr[5] = { &a,&b,&c };//指標陣列—存放整型指標的陣列
for (int i = 0; i < 3; i++)
{
printf("%d\n", *(crr[i])); //通過指標陣列找到陣列里的地址所對應的值
}
return 0;
}
運行結果:

結語
今天,forever 帶來了大家最頭疼的指標,這里先介紹指標初階到這里,后面再繼續給大家帶來指標進階,以至于完全拿下指標不是問題,當然,forever 也是根據自己之前的學習知識和經驗總結而來,若有什么不足或錯誤之處,還望大佬們積極指出來,對我進行批評指標哈!
謝謝觀看!
再見啦!
以上代碼均可運行,所用編譯環境為 vs2019 ,運行時注意加上編譯頭檔案#define _CRT_SECURE_NO_WARNINGS 1
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/404208.html
標籤:其他
上一篇:python猜數字小游戲制作
