詳解C語言/C++指標:篇1
詳解C語言/C++指標:篇2
文章目錄
- 1. 什么是指標?
- 2. 空指標、野指標
- 3. 指標與一維陣列
- 4. 指標與二維陣列
- 5. 指標與函式
- 6. 指標與字串
- 7. 指標的指標
- 8. 指標作為引數
- 9. 智能指標auto_ptr、unique_ptr等
1. 什么是指標?
指標是一種地址值!例如: 0x000012ae
什么是指標型別?
指標型別是一種新型別!格式: 型別 *
常見的指標型別:
int *:整型指標型別char *:字符型指標型別float *:浮點型指標型別- …
什么是指標變數?
顧名思義,用指標型別定義的變數就是指標變數,
如何定義指標變數?
//指標型別 變數名;
int* a; //定義一個整型指標變數
char* b; //定義一個字符型指標變數
float* c; //定義一個浮點型指標變數
...
指標變數怎么賦值?
指標變數存放的是地址值!
什么型別的指標變數,就應該指向該型別變數的地址!
給指標變數賦值?
利用&運算子可以獲取變數的記憶體地址,
int a=10; //假設a的地址為0×2000
int *p=&a; //定義一個整型指標變數p,存放整形變數a的地址,此時p的值為0x2000
int c=*p; //取地址所在記憶體中的內容
2. 空指標、野指標
空型別指標:void *
void*可以指向任何型別的地址
int a=10;
char b='a';
float c=12.345;
void *pa = &a; //指向整型變數地址
void *pb = &b; //指向字符型變數地址
void *pc = &c; //指向浮點型變數地址
// 從void *指標取資料
int a1 = (*(int*)pa);
int b1 = (*(char*)pb);
int c1 = (*(float*)pc);
什么是野指標?
定義:指向一個非法的或已銷毀的記憶體的指標
危害:對系統造成不可預知的危害!
給指標賦初值NULL:
#define NULL ((void*)0)
int *p=NULL;
指標p被free或 delete之后,只是把指標所指的記憶體給釋放掉,沒有改變指標的值,此時p淪落為“野指標”,
p = (int*)malloc(4);
free(p); //被銷毀的記憶體地址,此時p淪為野指標
p = NULL; //置空,避免野指標
3. 指標與一維陣列
陣列名本身就是一個指標(地址)!
int a[5] = {1,2,3,4,5};
int *p=a;
陣列名代表了陣列的首地址!a與&a[0]相等!
p指向陣列首地址
- 操作陣列方式一,把指標變數當陣列名用
for(int i=0;i<5;i++){
printf("%d\n", p[i]);
}
-
指標作操作陣列二
指標+1或-1是向上或向下偏移 sizeof(陣列型別)個位元組
for(int i=0;i<5;i++){
printf("%d\n", *(p+i));
}
- 操作陣列方式三,把指標變數
自加++來獲得每個元素的首地址
for(int i=0;i<5;i++){
printf("%d\n", *(p++));
}
陣列名a是一個常量,值無法改變,所以不能用于++,--
指標與陣列名的區別:
- 指標是一個變數,可以變,可以++,–
- 陣列名是一個常量,不可變,無法++,–
4. 指標與二維陣列
二維陣列: 可以理解為是一個一維陣列,只不過陣列元素又是一維陣列!
指向二維陣列的指標(行指標):型別 (*p)[N];,陣列的第二維長度為N
int a[2][3]={{1,2,3},{2,5,6}};
int (*p)[3];
p = a; //p指向陣列首地址
雖然四個地址值完全一樣,但含義不同:
a:行指標a[0]:整型指標&a[0]:行指標&a[0][0]:整型指標
5. 指標與函式
函式名本身就是一個指標(地址)!
int sum(int a, int b)
{
return a+b;
}
sum:就是函式的地址值
函式指標變數定義:
回傳值 (*變數名)(引數1, 引數2, ..., 引數N);
示例:
int sum(int a, int b){return a+b}
int (*pSum)(int a, int b); //函式指標變數
pSum = sum; //給指標賦地址值
函式指標型別定義:
typedef 回傳值 (*型別)(引數1, 引數2, ...,引數N);
函式指標使用當成函式一樣使用!
//p是一個函式指標,指向的函式,引數整型,
//回傳值又是一個函式指標(引數、回傳值都是整型)
int (*(*p)(int))(int);
()的優先級最高,因此p先與結合,說明p首先是個指標A,再與后面()結合,說明該指標A指向的內容是一個函式A,再與括號中的int結合,說明該函式的引數是一個int,再與(*p)前面的結合,說明該函式的回傳值是一個指標B,再與最后面的()結合,說明該指標B是函式指標,
6. 指標與字串
字串可以看成一個無名字符陣列!
字串常量本身就是一個地址!
“hello”在記憶體中:
字串常量實質就是一段記憶體(首地址來標識)!
// 給指標變數賦予字串常量的首地址!
char *p = "hello";
char *p = NULL;
p = "hello"; // 把hello的首地址給它
p = "world"; // 把world的首地址給它
字串常量的值不能改變!
7. 指標的指標
指向指標的指標!
什么是指標型別?
格式:型別 *
常見的指標的指標型別:
int**:整型指標的指標型別char**:字符型指標的指標型別float**:浮點型指標的指標型別- …
int a=10;
int b=20;
int c=30;
//指標陣列,本身陣列名是一個地址,陣列元素又是一個地址,雙重指標
int *d[3] = {&a,&b,&c};
//定義指標的指標
int **p = d; // 指標的指標就是為指標陣列而生的
// 指標操作陣列方法一(把指標當做陣列名來用了!)
for(int i=0;i<5;i++)
{
printf("%d\n", *p[i]);
}
// 指標操作陣列方法二()
for(int i=0;i<5;i++)
{
printf("%d\n", *(*(p+i)));
}
8. 指標作為引數
交換兩個數:
- 第一種寫法:( 交換失敗)
// 交換失敗,函式內僅僅是實參的拷貝,與實參無關
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
- 第二種寫法:( 交換成功)
// 交換成功,實參的地址傳入,直接操作實參的內容
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
交換兩個指標變數?
void swap2(int **a, int **b)
{
int* temp = *a;
*a = *b;
*b = *temp;
}
int *pA = &a;
int *pB = &b;
swap2(pA, pB);
9. 智能指標auto_ptr、unique_ptr等
面向物件+指標!
什么是智能指標?
能自動釋放指向的記憶體,并置空指標!
如何實作智能指標?
思路:將基本型別指標封裝為類物件指標(這個類肯定是個模板,以適應不同基本型別的需求),并在解構式里撰寫 delete陳述句洗掉指標指向的記憶體空間,
#include <memory>
using namespace std;
template <class T>
class SmartPointer
{
public:
SmartPointer(T *p)
{
ptr = p;
}
~SmartPointer()
{
delete ptr; //自動洗掉記憶體,防止泄露
ptr = NULL; //自動置空,防止野指標
}
// 多載*運算子
T operator *()
{
return *ptr;
}
// 多載->運算子
T* operator ->()
{
return ptr;
}
private:
T* ptr;
}
void main()
{
SmartPointer<int> sp(new int(123));
//獲取指標內容
int a = *sp;
}
STL庫提供了四種智能指標:
auto_ptr(廢棄)unique_ptrshared_ptrweak_ptr
auto_ptr( c++11廢棄)
- 退出生存期后,自動銷毀,
- 不能指向陣列,這是因為其內部實作,在解構式里執行了
delete_ptr,而不是delete[] ptr - 不可能存在多個
auto_ptr指向同一個物件,這是最重要的,也就是說,auto_ptr不能用作STL容器的元素,
主要由于上邊這個原因,auto_ptr在C++11中已經被廢棄了(雖然仍然可以使用它),推薦用unique_ptr取代之,
int *p = new int(4);
auto_ptr<int> ap(p);
cout << *ap << endl;
unique_prt
- 獨享所有權、一個非空的
std::unique_ptr總是擁有它所指向的資源,
轉移一個std::unique_ptr將會把所有權也從源指標轉移給目標指標(源指標被置空) - 拷貝一個
std::unique_ptr將不被允許,因為如果你拷貝一個std::unique_ptr那么拷貝結束后,這兩個std::unique_ptr都會指向相同的資源,它們都認為自己擁有這塊資源(所以都會企圖釋放),因此std::unique_ptr是一個僅能移動(move only)的型別,
#include <string>
using namespace std;
void main()
{
//獨享型智能指標
unique_ptr<int[]> p(new int[5]{1,2,3,4,5});
cout << p[2] << endl;
//無法拷貝,賦值,因為是獨享的,只能move
unique_ptr<int[]> p2 = move(p);
//如果轉移所有權,那么自己變成空
cout << p2[4] << " " << (p==nullptr) << endl;
}
shared_ptr
-
auto_ptr和unique_ptr都只能一個智能指標參考物件,而shared_ptr則是可以多個智能指標同時擁有一個物件, -
shared_ptr實作方式就是使用參考計數,這一技術在COM中是用來管理COM物件生命周期的一個方式,這種方式使得多個智能指標同時對所參考的物件有擁有權,同時在參考計數減到0之后也會自動釋放記憶體,也實作了auto_ptr和unique_ptr的資源釋放的功能
void main()
{
//共享型智能指標
shared_ptr<int> sp(new int(10));
cout << sp.unique() << endl; //是否唯一持有者
shared_ptr<int> sp2 = sp; //第二個shared_ptr,構造拷貝函式
cout << (sp==sp2) << " " << (sp.use_count() == 2) << endl;
*sp2 = 100; //使用解參考運算子修改被指物件
cout << *sp << endl; //另一個shared_ptr也同時被修改
sp.reset(); //釋放
cout << (sp==nullptr) << " " << sp2.use_count() << endl;
}
weak_ptr
weak_ptr是為了配合shared_ptr而引入的一種智能指標,它更像是shared_ptr的一個助手而不是智能指標,因為它不具有普通指標的行為, 沒有多載operator*和->,它從一個shared_ptr或者另一個weak_ptr物件構造,獲得觀測權,但weak_ptr沒有共享資源,它的構造不會引起指標參考計數的增加
void main()
{
//共享型智能指標
shared_ptr<int> sp(new int(10));
cout << sp.unique() << endl; //是否唯一持有者
weak_ptr<int> wp = sp; //配合shared_ptr的作業
cout << sp.use_count() << endl;
shared_ptr<int> sp2 = sp; //第二個shared_ptr,構造拷貝函式
cout << (sp==sp2) << " " << wp.use_count() << endl;
*sp2 = 100; //使用解參考運算子修改被指物件
cout << *sp << endl; //另一個shared_ptr也同時被修改
sp.reset(); //釋放
cout << (sp==nullptr) << " " << wp.use_count() << endl;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/282659.html
標籤:其他
上一篇:rand函式和srand函式詳解
