C++掃盲篇之指針詳解
前言
指針對于學(xué)習(xí)C/C++的人來說是一道必須邁過去的坎,就像學(xué)習(xí)九陽神功必須要打通任督二脈一樣的道理。雖然說隨著智能指針的普及,很少需要程序員再手動操作原始指針, 但是如果你連原始指針的都沒學(xué)好,那你怎么可能用好智能指針呢?
無論是原始指針還是智能指針,要想用好它就一定要做到知其然,知其所以然。
因?yàn)楸疚拈喿x對象是有了一定指針基礎(chǔ)的童鞋,所以如果你對指針如果是處于一無所知的狀態(tài)的話,建議先去溫習(xí)下指針的基礎(chǔ)知識,不然可能讀起來會打擊你求知的欲望。
指針為什么要有類型
是為了指針運(yùn)算和取值。
當(dāng)使用指針取值的時候需要知道怎么取值,比如按照多少個字節(jié)去取值,這是需要確定才能取到正確的值的,要知道用多少個字節(jié)去取就得知道指針的類型是什么。
我們知道指針的運(yùn)算增加或者減少1意味著需要偏移指針?biāo)硎镜念愋偷拇笮€字節(jié)數(shù),比如說一個int字節(jié)的指針增加1,表示偏移4個字節(jié)(一般情況下int都是4個字節(jié)),所以這也是需要知道指針的類型。
指針和數(shù)組
本來從字面上來說指針和數(shù)組是八竿子打不著的,它們理應(yīng)是井水不犯河水的,怎么就扯上了呢?我們經(jīng)常聽說數(shù)組指針、指針數(shù)組,這些都是什么意思呢?他們到底是指針還是數(shù)組呢?下面將一一為你解答。
指針數(shù)組,首先它是一個數(shù)組,數(shù)組里面的每個元素都是一個指針,例如比如int *p[4] 就是一個指針數(shù)組,因?yàn)檫\(yùn)算符[]的優(yōu)先級運(yùn)算符*的優(yōu)先級高,所以p優(yōu)先和[]組成數(shù)組,然后*和類型int組合成數(shù)組元素的類型。 例如以下程序就是一個指針數(shù)組的示例:
main.c
#include <stdio.h>
int main(){
char *str[3] = {
"我是數(shù)組1",
"我是數(shù)組2",
"我是數(shù)組3"
};
printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
return 0;
}數(shù)組指針,首先它是一個指針,這個指針?biāo)赶虻膶ο笫菙?shù)組,比如這個指針是p,那么通過解引用*p獲得內(nèi)容就是一個數(shù)組,例如int (*p)[4],主意帶上括號, 通常數(shù)組指針也作為一個二維數(shù)組來使用。
二級指針
所謂的二級指針其實(shí)就是一個指向指針的指針,例如int **p就是一個二級指針,它內(nèi)部存放的對象是一個指針,通過一次解引用獲得的是內(nèi)存存放的指針的地址,需要再次對這個內(nèi)部的指針進(jìn)行解引用才能獲取到 這個真是內(nèi)容的值。
理解起來有點(diǎn)繞,那么這個拗口的二級指針有什么作用呢?二級指針在C++中可能用的不多,但是在C中是經(jīng)常使用的一把利器,它通常作為一個函數(shù)的參數(shù),起到在函數(shù)內(nèi)部對一個指針進(jìn)行初始化的作用, 比如經(jīng)典的音視頻處理工具FFmpeg中就大量使用了二級指針。 以下例子展示如何通過二級指針對指針形式賦值:
main.cpp
void initP(int **p){
*p = new int(10);
}
int main() {
int *p = nullptr; // 一個空的指針
initP(&p); // 通過二級指針初始化指針p
std::cout << "*p的值:" << *p << endl;
delete p;
return 0;
}可能在這里就有人和當(dāng)初筆者剛接觸C語言一樣迷惑了,難道不能通過給函數(shù)傳遞一級指針給指針初始化嗎?這是不行的,這是因?yàn)橹祩鬟f的緣故,像深入探討的童鞋們可以寫個例子打印下實(shí)參的具體地址對比下研究下其背后的原理。
指針與多態(tài)綁定
我們都知道C++是一門面向?qū)ο蟮脑O(shè)計(jì)語言,支持多態(tài)就是它的一個重要特性之一,在學(xué)習(xí)C++類的相關(guān)知識的時候老師就告訴我們:*在C++語言中,當(dāng)我們使用基類的引用(或指針)調(diào)用一個虛函數(shù)時將發(fā)生動態(tài)綁定。*也就是 說使用通過父類的指針或引用就能按照實(shí)參的實(shí)際類型是父類還是子類調(diào)用不同的虛函數(shù)。
例如如以下代碼:
main.cpp
class Base{
public:
virtual void print() const{
std::cout << "base print" << endl;
}
virtual ~Base(){
}
};
class Child:public Base{
public:
void print() const override{
std::cout << "Child print" << endl;
}
};
void testPrint(const Base &base){
base.print();
}
int main() {
Base a = Child();
testPrint(a);// 打印Base print
Child b = Child(); // 注意,不能寫成Base b = Child(),否則打印的是Base的print
testPrint(b); // 打印Child print
Base *c = new Child(); // 指針,動態(tài)類型與靜態(tài)類型不一致
testPrint(*c); // 打印Child print
Base &&r = Child(); // 表達(dá)式是右值引用,動態(tài)類型與靜態(tài)類型不一致
testPrint(r); // 打印Child print
return 0;
}為什么在上面的程序中變量a的實(shí)際類型是Child,但是函數(shù)testPrint內(nèi)部調(diào)用的卻是父類的打印方法呢?不是說引用會觸發(fā)多態(tài)嗎?函數(shù)testPrint也是通過引用傳遞的呀, 真是百思不得其jie呀。
要解開這個疑惑就得了解下靜態(tài)類型和動態(tài)類型的知識了。靜態(tài)類型在編譯時總是已知的,首先靜態(tài)類型是變量聲明時的類型或表達(dá)式生成的類型;動態(tài)類型則是變量或表達(dá)式表示的內(nèi)存中的對象的類型,動態(tài)類型直到運(yùn)行時才可知。 如果變量在定義時表達(dá)式既不是引用也不是指針,則它的動態(tài)類型永遠(yuǎn)與靜態(tài)類型一致的,也就是聲明時所指的類型,否則的話靜態(tài)類型可能與動態(tài)類型不一致。
那么有了靜態(tài)類型與動態(tài)類型的概念之后再結(jié)合注釋看上面的示例代碼是不是就有一種撥開云霧見青天的感覺了呢?
函數(shù)指針
函數(shù)指針顧名思義就是指向函數(shù)的指針,它的定義:函數(shù)指針是指向函數(shù)的指針變量。因此“函數(shù)指針”本身首先應(yīng)是指針變量,只不過該指針變量指向函數(shù)。
其聲明方式是:
返回值類型 (*函數(shù)名) (參數(shù))
函數(shù)指針的一個重要用途就是作為函數(shù)的參數(shù),用于在函數(shù)內(nèi)部進(jìn)行指針函數(shù)的調(diào)用,一般用作回調(diào)函數(shù),比如在創(chuàng)建一個POSIX線程的就需要傳遞一個函數(shù)指針用于指明該線程做點(diǎn)什么事情。
以下代碼展示了一個簡單的函數(shù)指針的使用方法:
void testFunc(int a,int b,void (*func)(int c,int d) ){
// do something
func(a,b);
}
void callback(int a,int b){
}
int main() {
testFunc(1,2,callback);
return 0;
}類成員指針
這里類成員指針表示的是指向類的某個對象的非靜態(tài)成員的指針,而不是表示類成員變量的指針,首先需要區(qū)分好這是兩個不同的概念,如果不能好好區(qū)分這兩個概念的童鞋,需要再好好思考一下。
成員指針的類型囊括了類的類型以及成員的類型。當(dāng)初始化一個這樣的指針時,我們令其指向類的某個成員,但是不指定該成員所屬的對象;直到使用成員指針時,才提供成員所屬的對象。
和其他指針一樣,在聲明成員指針時我們也使用*來表示當(dāng)前聲明的名字是一個指針。與普通指針不同的是,成員指針還必須包含成員所屬的類。下面是一個使用的示例:
class Person{
public:
virtual void print() const{
std::cout << "base print" << endl;
}
virtual ~Person(){
}
public:
string lastName;
string firstName;
};
int main() {
string Person::*p; // 聲明了一個類成員指針
p = &Person::firstName; // 成員變量的指針指向了Peron的name
Person person;
person.*p = "hello"; // 使用成員指針
p = &Person::lastName; // 成員變量的指針指向了Peron的lastName
person.*p = "world"; // 使用成員指針
std::cout << person.firstName << " " << person.lastName << std::endl;
return 0;
}至于這個成員指針有什么用處,給筆者的感覺就是重新命了一個別名的感覺,甚至有點(diǎn)脫褲子放屁?但是存在即合理,不是沒有用處,只是筆者見過那種場景而已吧。。。
除了有指向類成員變量的指針外還有指向類成員函數(shù)的指針,這里就不多展開探討了!??!
補(bǔ)充:用指針的指針指向指針數(shù)組
#include<stdio.h>
int change(char **p)
{
int i, j;
for (i = 0; i < 5; i++)
{
for (j = 0; *(*(p + i) + j) != '\0'; j++)//利用指針的指針取二維數(shù)組的元素
{
*(*(p + i) + j) = 'c';
printf("%c", *(*(p + i) + j));
}
printf("\n");
}
return 0;
}
int main(void)
{
char *a[5] = { "hello", "zhuyu", "jiajia", "linux","Ubuntu" };//如果想使用 需使用指針數(shù)組即*a[5] 聲明一個有五個字符串指針的數(shù)組。
//但是由于每個元素都是指針字符串,所以只能夠讀取,而不能夠?qū)懭搿?
change(a);
return 0;
}總結(jié)
到此這篇關(guān)于C++掃盲篇之指針的文章就介紹到這了,更多相關(guān)C++指針掃盲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一起來學(xué)習(xí)C++的動態(tài)內(nèi)存管理
這篇文章主要為大家詳細(xì)介紹了C++的動態(tài)內(nèi)存管理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
使用C++實(shí)現(xiàn)Excel文件與CSV之間的相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了如何使用C++實(shí)現(xiàn)Excel文件與CSV之間的相互轉(zhuǎn)換,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-06-06
C語言實(shí)現(xiàn)手機(jī)電話簿管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)手機(jī)電話簿管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
詳解C語言數(shù)據(jù)結(jié)構(gòu)之棧
這篇文章主要為大家介紹了C語言數(shù)據(jù)結(jié)構(gòu)之棧,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01
QT基于TCP實(shí)現(xiàn)網(wǎng)絡(luò)聊天室程序
這篇文章主要為大家詳細(xì)介紹了QT基于TCP實(shí)現(xiàn)網(wǎng)絡(luò)聊天室程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
c語言程序設(shè)計(jì)文件操作方法示例(CreateFile和fopen)
c主要的文件操作函數(shù)有:CreateFile,CloseHandle,ReadFile,WriteFile,SetFilePointer,GetFileSize。其中的讀寫操作是以字符為單位,獲得文件大小也是以字符為單位。2013-12-12

