在計(jì)算機(jī)科學(xué)中,指針(Pointer)是編程語言中的一個(gè)對(duì)象,利用地址,它的值直接指向(points to)存在電腦存儲(chǔ)器中另一個(gè)地方的值。由于通過地址能找到所需的內(nèi)存單元,可以說地址指向該內(nèi)存單元。因此,將地址形象化的稱為“指針”。意思是通過它能找到以它為地址的內(nèi)存單元。
#includeint main()
{int a = 10;//在內(nèi)存中開辟一塊空間
int* p = &a;//將a的地址取出,放到指針變量p中
return 0;
}
指針的大小對(duì)于32位的機(jī)器,即有32根地址線,因?yàn)槊扛刂肪€能產(chǎn)生正電(1)或負(fù)電(0),所以在32位的機(jī)器上能夠產(chǎn)生的地址信號(hào)就是32個(gè)0/1組成的二進(jìn)制序列:一共 232 個(gè)地址。
同樣的算法,在64位的機(jī)器上一共能產(chǎn)生 264 個(gè)不同的地址。
232 可以用32個(gè)bit位進(jìn)行存儲(chǔ),而8個(gè)bit位等價(jià)于1個(gè)字節(jié),所以在32位的平臺(tái)下指針的大小為4個(gè)字節(jié)。
264 可以用64個(gè)bit位進(jìn)行存儲(chǔ),所以在64位的平臺(tái)下指針的大小為8個(gè)字節(jié)。
指針類型在32位平臺(tái)下指針的大小為4個(gè)字節(jié),在64位平臺(tái)下指針的大小為8個(gè)字節(jié)。
指針有哪些類型?
指針的定義方式是type + *
char * 類型的指針存放的是char類型的變量地址;
int * 類型的指針存放的是int類型的變量地址;
float * 類型的指針存放的是float類型的變量地址等。
1.指針±整數(shù)
2.指針解引用
指針的類型決定了指針解引用的時(shí)候能夠訪問幾個(gè)字節(jié)的內(nèi)容。
總結(jié):
概念:野指針就是指向位置是不可知的(隨機(jī)的、不正確的、沒有明確限制的)指針。
野指針的成因
1.指針未初始化
2.指針越界訪問
3.指針指向的空間被釋放
我們知道,指針變量是用于存放地址的變量。但是指針變量也是變量,是變量就有地址,那么存放指針變量的地址的變量是什么呢?
其實(shí),存放普通變量的地址的指針叫一級(jí)指針,存放一級(jí)指針變量的地址的指針叫二級(jí)指針,存放二級(jí)指針變量地址的指針叫三級(jí)指針,以此類推。
#includeint main()
{int a = 10;
int* p1 = &a;
int** p2 = &p1;
return 0;
}
在這里,我們用一級(jí)指針p1存放了普通常量a的地址,用二級(jí)指針p2存放了一級(jí)指針p1的地址。
這時(shí)如果我們要得到a的值,就有兩種方法:
方法一:對(duì)一級(jí)指針p1進(jìn)行一次解引用操作即可得到a的值,即*p1。
方法二:對(duì)二級(jí)指針p2進(jìn)行一次解引用操作即可得到p1的值,而p1的值就是a的地址,所以再對(duì)p2進(jìn)行一次解引用操作即可得到a的值,也就是對(duì)二級(jí)指針p2進(jìn)行兩次解引用操作即可得到a的值,即**p2。
例:
#includeint main()
{const char* p = "hello csdn.";
printf("%c\n", *p);//打印字符'h'
printf("%s\n", p);//打印字符串"hello csdn."
return 0;
}
指針數(shù)組注意:常量字符串與普通字符串大的區(qū)別是,常量字符串是不可被修改的字符串,既然不能被修改,那么在內(nèi)存中沒有必要存放兩個(gè)一模一樣的字符串,所以在內(nèi)存中相同的常量字符串只有一個(gè)。
指針數(shù)組也是數(shù)組,是用于存放指針的數(shù)組。
int* arr3[5];//數(shù)組arr3包含5個(gè)元素,每個(gè)元素是一個(gè)一級(jí)整型指針。
舉例:
#includeusing namespace std;
int main()
{char a = 'm';
char* p =&a;
char* arr[1];
arr[0] = p;
cout<< *arr[0]<< endl;
return 0;
}
我們定義了一個(gè)p指針指向字符a,將p存到了指針數(shù)組arr[0]中,然后對(duì)arr[0]解引用取到了字符’m’。
數(shù)組指針數(shù)組指針就是指向數(shù)組的指針
#includeint main()
{int arr[10] = {0 };
int(*p)[10] = &arr;
//()優(yōu)先級(jí)高,說明表示指針,是一個(gè)數(shù)組指針
return 0;
}
&數(shù)組名 VS 數(shù)組名對(duì)于一個(gè)數(shù)組的數(shù)組名,它什么時(shí)候代表數(shù)組首元素的地址,什么時(shí)候又代表整個(gè)數(shù)組的地址呢?
數(shù)組名代表整個(gè)數(shù)組的地址的情況其實(shí)只有兩種:
除此之外,所有的數(shù)組名都是數(shù)組首元素地址。
比如:
int arr[5] = {1, 2, 3, 4, 5 };
對(duì)于該數(shù)組arr,只有以下兩種情況數(shù)組名代表整個(gè)數(shù)組的地址:
&arr;
sizeof(arr);//arr單獨(dú)放在sizeof內(nèi)部
應(yīng)用#includeint main()
{int arr[5] = {1, 2, 3, 4, 5 };
int* p1 = arr;//數(shù)組首元素的地址
int(*p2)[5] = &arr;//數(shù)組的地址
printf("%p\n", p1);
printf("%p\n", p2);
printf("%p\n", p1+1);
printf("%p\n", p2+1);
return 0;
}
一維數(shù)組傳參
#includevoid test1(int arr[10])//數(shù)組接收
{}
void test1(int *arr)//指針接收
{}
void test2(int *arr[20])//數(shù)組接收
{}
void test2(int **arr)//指針接收
{}
int main()
{int arr1[10] = {0 };//整型數(shù)組
int *arr2[20] = {0 };//整型指針數(shù)組
test1(arr1);
test2(arr2);
}
整型數(shù)組:
當(dāng)向函數(shù)傳入整型數(shù)組的數(shù)組名時(shí),我們有以下幾種參數(shù)可供接收:
整型指針數(shù)組:
當(dāng)向函數(shù)傳入整型指針數(shù)組的數(shù)組名時(shí),我們有以下幾種參數(shù)可供接收:
注意:一維數(shù)組傳參,函數(shù)形參設(shè)計(jì)時(shí)[ ]內(nèi)的數(shù)字可省略。
二維數(shù)組傳參
#includevoid test(int arr[][5])//數(shù)組接收
{}
void test(int(*arr)[5])//指針接收
{}
int main()
{int arr[3][5] = {0 };//二維數(shù)組
test(arr);
}
當(dāng)向函數(shù)傳入二維數(shù)組的數(shù)組名時(shí),我們有以下幾種參數(shù)可供接收:
注意:二維數(shù)組傳參,函數(shù)形參的設(shè)計(jì)只能省略第一個(gè)[ ]內(nèi)的數(shù)字。
一級(jí)指針傳參
#includevoid print(int* p, int sz)//一級(jí)指針接收
{int i = 0;
for (i = 0; i< sz; i++)
{printf("%d ", *(p + i));
}
}
int main()
{int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;//一級(jí)指針
print(p, sz);
return 0;
}
二級(jí)指針傳參
#includevoid test(int** p)//二級(jí)指針接收
{}
int main()
{int a = 10;
int* pa = &a;
int** paa = &pa;
test(paa);//二級(jí)指針
return 0;
}
當(dāng)我們傳入的參數(shù)為二級(jí)指針時(shí),我們可以用二級(jí)指針的形參對(duì)其進(jìn)行接收,那么當(dāng)函數(shù)形參為二級(jí)指針的時(shí)候,我們可以傳入什么樣的參數(shù)呢?
#includevoid test(int** p)
{}
int main()
{int a = 10;
int* pa = &a;
test(&pa);//可以傳入一級(jí)指針的地址
int** paa = &pa;
test(paa);//可以傳入二級(jí)指針
int* arr[10];
test(arr);//可以傳入一級(jí)指針數(shù)組的數(shù)組名
//...
return 0;
}
總而言之,只要傳入的表達(dá)式最終的類型是二級(jí)指針類型即可傳入。
函數(shù)指針函數(shù)指針的定義
函數(shù)指針就是指向函數(shù)的指針。
和學(xué)習(xí)數(shù)組指針一樣,學(xué)習(xí)函數(shù)指針我們也需要知道三點(diǎn):
例子:
#includeint Add(int x, int y)
{return x + y;
}
int main()
{int(*p)(int, int) = &Add;//取出函數(shù)的地址放在函數(shù)指針p中
return 0;
}
那么,函數(shù)指針p的類型我們是如何創(chuàng)建的呢?
函數(shù)指針的使用
知道了如何創(chuàng)建函數(shù)指針,那么函數(shù)指針應(yīng)該如何使用呢?
1.函數(shù)指針的賦值
int(*p)(int, int) = &Add;
int(*p)(int, int) = Add;
2.通過函數(shù)指針調(diào)用函數(shù)
方法一:我們知道,函數(shù)指針存放的是函數(shù)的地址,那么我們將函數(shù)指針進(jìn)行解引用操作,便能找到該函數(shù)了,于是就可以通過函數(shù)指針調(diào)用該函數(shù)。
#includeint Add(int x, int y)
{return x + y;
}
int main()
{int a = 10;
int b = 20;
int(*p)(int, int) = &Add;
int ret = (*p)(a, b);//解引用找到該函數(shù)
printf("%d\n", ret);
return 0;
}
方法二:我們?cè)诤瘮?shù)指針賦值中說到,函數(shù)名和&函數(shù)名都代表函數(shù)的地址,我們可以賦值時(shí)直接賦值函數(shù)名,那么通過函數(shù)指針調(diào)用函數(shù)的時(shí)候我們就可以不用解引用操作符就能找到函數(shù)了。
#includeint Add(int x, int y)
{return x + y;
}
int main()
{int a = 10;
int b = 20;
int(*p)(int, int) = Add;
int ret = p(a, b);//不用解引用
printf("%d\n", ret);
return 0;
}
函數(shù)指針數(shù)組函數(shù)指針數(shù)組的定義
我們知道,數(shù)組是一個(gè)存放相同類型數(shù)據(jù)的空間,我們已經(jīng)認(rèn)識(shí)了指針數(shù)組,比如:
int* arr[10];//數(shù)組arr有10個(gè)元素,每個(gè)元素的類型是int*
那如果要將一系列相同類型的函數(shù)指針存放到一個(gè)數(shù)組中,那么這個(gè)數(shù)組就叫做函數(shù)指針數(shù)組,比如:
int(*pArr[10])(int, int);
//數(shù)組pArr有10個(gè)元素,每個(gè)元素的類型是int(*)(int,int)
函數(shù)指針數(shù)組的使用 - 模擬計(jì)算器函數(shù)指針數(shù)組的創(chuàng)建只需在函數(shù)指針創(chuàng)建的基礎(chǔ)上加上[ ]即可。
- 比如,你要?jiǎng)?chuàng)建一個(gè)函數(shù)指針數(shù)組,這個(gè)數(shù)組中存放的函數(shù)指針的類型均為int(*)(int,int),如果你要?jiǎng)?chuàng)建一個(gè)函數(shù)指針為該類型,那么該函數(shù)指針的寫法為int(*p)(int,int),現(xiàn)在你要?jiǎng)?chuàng)建一個(gè)存放該指針類型的數(shù)組,只需在變量名的后面加上[]即可,int(*pArr[10])(int,int)。
函數(shù)指針數(shù)組一個(gè)很好的運(yùn)用場(chǎng)景,就是計(jì)算機(jī)的模擬實(shí)現(xiàn):
#includevoid menu()
{printf("|-----------------------|\n");
printf("| 1.Add 2.Sub |\n");
printf("| 3.Mul 4.Div |\n");
printf("| 0.exit |\n");
printf("|-----------------------|\n");
}//菜單
double Add(double x, double y)
{return x + y;
}//加法函數(shù)
double Sub(double x, double y)
{return x - y;
}//減法函數(shù)
double Mul(double x, double y)
{return x*y;
}//乘法函數(shù)
double Div(double x, double y)
{return x / y;
}//除法函數(shù)
int main()
{int input = 0;
double x = 0;//第一個(gè)操作數(shù)
double y = 0;//第二個(gè)操作數(shù)
double ret = 0;//運(yùn)算結(jié)果
double(*pArr[])(double, double) = {0, Add, Sub, Mul, Div };
//函數(shù)指針數(shù)組-轉(zhuǎn)移表
int sz = sizeof(pArr) / sizeof(pArr[0]);//計(jì)算數(shù)組的大小
do
{menu();
printf("請(qǐng)輸入:>");
scanf("%d", &input);
if (input == 0)
printf("退出程序\n");
else if (input >0 && input< sz)
{ printf("請(qǐng)輸入兩個(gè)操作數(shù):>");
scanf("%lf %lf", &x, &y);
ret = pArr[input](x, y);
printf("ret=%lf\n", ret);
}
else
printf("選擇錯(cuò)誤,請(qǐng)重新選擇!\n");
} while (input);//當(dāng)input不為0時(shí)循環(huán)繼續(xù)
return 0;
}
代碼中,函數(shù)指針數(shù)組存放的是一系列參數(shù)和返回類型相同的函數(shù)名,即函數(shù)指針。將0放在該函數(shù)指針數(shù)組的第一位是為了讓用戶輸入的數(shù)字input與對(duì)應(yīng)的函數(shù)指針下標(biāo)相對(duì)應(yīng)。
該代碼若不使用函數(shù)指針數(shù)組,而選擇使用一系列的switch分支語句當(dāng)然也能達(dá)到想要的效果,但會(huì)使代碼出現(xiàn)許多重復(fù)內(nèi)容,而且當(dāng)以后需要增加該計(jì)算機(jī)功能時(shí)又需要增加一個(gè)case語句,而使用函數(shù)指針數(shù)組,當(dāng)你想要增加計(jì)算機(jī)功能時(shí)只需在數(shù)組中加入一個(gè)函數(shù)名即可。
既然存在函數(shù)指針數(shù)組,那么必然存在指向函數(shù)指針數(shù)組的指針。
int(*p)(int, int);
//函數(shù)指針
int(*pArr[5])(int, int);
//函數(shù)指針數(shù)組
int(*(*pa)[5])(int, int) = &pArr;
//指向函數(shù)指針數(shù)組的指針
pa就是一個(gè)指向函數(shù)指針數(shù)組的指針,該函數(shù)指針數(shù)組中每個(gè)元素類型是int(*)(int, int)。
回調(diào)函數(shù)回調(diào)函數(shù)的定義
回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來調(diào)用其所指向的函數(shù)時(shí),我們就說這是回調(diào)函數(shù)。
舉個(gè)簡(jiǎn)單的例子:
void test1()
{printf("hello\n");
}
void test2(void(*p)())
{p(); //指針p被用來調(diào)用其所指向的函數(shù)
}
int main()
{test2(test1);//將test1函數(shù)的地址傳遞給test2
return 0;
}
在該代碼中test1函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是將其地址傳遞給test2函數(shù),在test2函數(shù)中通過函數(shù)指針間接調(diào)用了test1函數(shù),那么函數(shù)test1就被稱為回調(diào)函數(shù)。
回調(diào)函數(shù)的使用 - qsort函數(shù)其實(shí)回調(diào)函數(shù)并不是很難見到,在用于快速排序的庫函數(shù)qsort中便運(yùn)用了回調(diào)函數(shù)。
void qsort(void*base,size_t num,size_t width,int(*compare)(const void*e1,const void*e2));
列如,我們要排一個(gè)整型數(shù)組:
#includeint compare(const void* e1, const void* e2)
{return *((int*)e1) - *((int*)e2);
}//自定義的比較函數(shù)
int main()
{int arr[] = {2, 5, 1, 8, 6, 10, 9, 3, 5, 4 };
int sz = sizeof(arr) / sizeof(arr[0]);//元素個(gè)數(shù)
qsort(arr, sz, 4, compare);//用qsort函數(shù)將arr數(shù)組排序
return 0;
}
最終arr數(shù)組將被排為升序。
注意:qsort函數(shù)默認(rèn)將待排序的內(nèi)容排為升序,如果我們要排為降序可將自定義的比較函數(shù)的兩個(gè)形參的位置互換一下即可。
在qsort函數(shù)中我們傳入了一個(gè)函數(shù)指針,最終qsort函數(shù)會(huì)在其內(nèi)部通過該函數(shù)指針調(diào)用該函數(shù),那么我們的這個(gè)自定義比較函數(shù)就被稱為回調(diào)函數(shù)。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
當(dāng)前名稱:C語言指針全解-創(chuàng)新互聯(lián)
網(wǎng)站鏈接:http://muchs.cn/article38/cspcsp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、企業(yè)建站、小程序開發(fā)、品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站策劃、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容