指針類型
下面通過代碼進行講解
#include<iostream>
using namespace std;
int main()
{
int* pt = new int;
double* ptt = new double;
cout << pt << endl;
cout << ptt << endl;
*pt = 100.00;
*ptt = 12.1234;
cout << *pt << endl;
cout << *ptt << endl;
return 0;
}
這裏可以看到,我們通過new申請得到的兩塊地址的長度是一致的,但是爲什麼在cout的時候,編譯器知道該輸出int還是輸出double呢?這就與指針的類型有關了
我們在new int的時候,new找到一個長度爲四字節的內存塊,把起始地址放在pt中,ptt也是同理,,由於地址在計算機中的表示形式都是一樣的,最起碼在同一臺計算機中是這樣的,所以pt和ptt存儲的內容長度都一致,但是由於pt在定義的時候就定義成了int *類型的變量,所以在cout<<*pt的時候,編譯器就知道,從pt存儲的那塊地址開始的四個字節按照int的方式進行解釋
delete
delete用來釋放new申請得到的空間,注意 是new 得到的空間,另外new和delete應該成對出現,然後不要對同一個指針,因爲第一次delete已經把指針所保存的空間歸還給操作系統了,連續兩次delete雖然不會報錯,但是會返回錯誤的結束碼.
對於數組的申請
int *pt = new int[5]; delete [] pt;
指針和數組
基本操作
直接講解書中代碼,進行引出
#include<iostream>
using namespace std;
int main()
{
double wages[3]{ 1000.0, 2000.0, 3000.0 };
short stacks[3]{ 3,2,1 };
double* pw = wages;
short* ps = &stacks[0];
short(* ps_2)[3] = &stacks;
cout << "pw= " << pw << " *pw=" << *pw << endl;
pw = pw + 1;
cout << "after add 1 to pw\n";
cout << "pw= " << pw << " *pw=" << *pw << endl;
cout << "ps= " << ps << " *ps=" << *ps << endl;
ps = ps + 1;
cout << "after add 1 to ps\n";
cout << "ps= " << ps << " *ps=" << *ps << endl;
cout << "*(stacks+1)=" << *(stacks + 1) << endl;
cout << "sizeof(wages):" << sizeof(wages) << endl;
cout << "sizeof(ps):" << sizeof(ps) << endl;
cout << "&stacks[2]=" << &stacks[2] << endl;
cout << "ps_2+1=" << ps_2 + 1 << endl;
return 0;
}
運行結果
pw= 010FFAB4 *pw=1000
after add 1 to pw
pw= 010FFABC *pw=2000
ps= 010FFAA4 *ps=3
after add 1 to ps
ps= 010FFAA6 *ps=2
*(stacks+1)=2
sizeof(wages):24
sizeof(ps):4
&stacks[2]=010FFAA8
ps_2+1=010FFAAA
對結果進行解釋,由於C++一般情況下將數組名結束爲數組的第一個元素的地址(特殊情況下面會講),所以pw的值也就是&wages[0],所以pw也就是wages[0],然後pw=pw+1,對指針變量加1後,其增加的值等於指向的類型佔用的字節數,這點很重要!!! 所以這裏pw的值和pw都編程了wages[1]的了
指針ps的結果和pw一致,然後對於 *(stacks + 1) ,可以看出這種寫法與stacks[1]的結果一致,所以大多數情況下,可以用相同的方式使用指針名和數組名,所以ps[0]這種寫法也可以得到stacks[0]的值
最後的兩個sizeof的結果,如果是sizeof(wages),得到的結果就是元素的大小*數組的長度,如果是sizeof(指針)的話,得到的結果與編譯器或操作系統的位數有關,在vs2019中,如果最上面設置的是x86,則指針大小得到4,如果是x64,則得到8
不解釋爲首元素地址的特殊情況
- 對數組取地址
同時也是上面這段代碼最後一個需要注意的地方,就是short(* ps_2)[3] = &stacks;,我們這裏對數組進行了取地址,這時就會得到整個數組的地址,由於整個數組是一個有着3個short元素的數組,雖然ps_2和&stacks[0](也就是stack)的值是一樣的,但是從概念上說,&stacks[0]是一個4字節的內存塊的地址,而ps_2是一個12字節的內存塊的地址,因此最後將ps_2+1得到的地址比&stacks[2]還大了四個字節
指針數組和數組指針的簡單理解
所以這裏把ps_2聲明成了 short (*)[3],這是一個數組指針,這個怎麼理解呢,由於括號的優先級,所以ps_2先和星號結合,成爲一個指針,然後指向的元素是有着三個元素的short數組(數組指針,首先是個指針,指向一個數組,所以叫數組指針)
然後如果我們去掉括號的話,就成了 short *ps_2[3],ps_2將先和[3]結合,所以就解釋爲了,ps_2是一個數組,數組的元素是指向short類型的指針,先是一個數組,再是一個指針,所以叫它指針數組
指針和字符串
同樣也是講解書中代碼,然後引出相關知識
#include<iostream>
using namespace std;
int main()
{
char animal[20]{ "bear" };
const char* bird = "wren";
char* ps;
cout << animal << " and " << bird << endl;
//cout << ps << endl; 編譯錯誤,使用了未初始化的局部變量ps
//cout << ps << endl; 如果把ps初始化爲nullptr,則運行不會報錯,但是會返回錯誤的結束碼
cout << "Enter a kind of animal:";
cin >> animal;
ps = animal;
cout << ps << "!\n";
cout << "Before using strcpy():\n";
cout << animal << " at " << (int*)animal << endl;
cout << ps << " at " << (int*)ps << endl;
ps = new char[strlen(animal) + 1];
strncpy_s(ps,strlen(animal) + 1,animal,strlen(animal));
cout << "after use:\n";
cout << animal << " at " << (int*)animal << endl;
cout << ps << " at " << (int*)ps << endl;
delete[] ps;
return 0;
}
bear and wren
Enter a kind of animal:tigger
tigger!
Before using strcpy():
tigger at 00BFFE4C
tigger at 00BFFE4C
after use:
tigger at 00BFFE4C
tigger at 00EC4968
- 爲什麼"wren"一定要通過const char *去存儲?
因爲"wren"實際表示的是字符串的地址,因此將地址只能賦給指針,而且由於字符串字面值是常量,所以要是const,表示 bird 是一個指針,指向一個const char的數據,不能對指針指向的數據進行修改 - 一般來說,提供給cout一個指針,他將打印地址,但如果指針類型爲char *,則cout將顯示指向的字符串,但如果要顯示地址得話,就需要想上面一樣轉換一下再輸出
- 如果要進行字符串副本的話,不能直接用 = 因爲這樣實際上是讓兩個指針指向了一個地址,就像上面 ps =animal之後的結果,兩個的地址一樣, 這樣並不安全,通常的做法是 先給ps開闢空間,然後用strcpy函數進行拷貝
當然,如果使用string的話,就避免了這些問題,這些就不用我們考慮了,之前有介紹string的自動擴容 - C++不保證字符串字面值被唯一的存儲,也就是說如果在程序中多次使用了 “bear” 這個字符串常量,則編譯器可能存儲該字符串的多個副本,也可能只存儲一個副本,這種與編譯器有關的我們就不做深究了,我在vs2019和DEV C++上測試,都是隻存儲了一個
堆,棧和內存泄漏
#include<iostream>
using namespace std;
int main()
{
{
int* a = new int;
}
delete a; //直接報錯,因爲a的作用域已經結束了
return 0;
}
書上說的就是這麼兩漢代碼,我們在{}中 new 了一個空間給a,但是花括號結束也就代表着出了a的作用域,在括號外就無法通過a去delete了
二級指針初探
#include<iostream>
using namespace std;
struct mytest {
int test;
};
int main()
{
mytest t1, t2, t3;
t1.test = 1996;
t2.test = 1997;
t3.test = 1998;
mytest * arp[3] = { &t1, &t2, &t3 };
mytest* *pd = arp;
cout << pd[0]->test << endl;//1996
cout << (*(pd + 0))->test << endl;//1996
cout << (*pd)[0].test << endl;//1996
cout << pd[0]->test << endl;//1996
cout << arp[0]->test << "--" << (*(arp + 0))->test << "--" << (*(*(arp + 0))).test << endl;//1996--1996--1996
}
由於arp是一個指針數組,所以他的每一個元素都是指針,所以arp[0]就需要用間接成員運算符也就是箭頭去訪問成員,由於前面介紹過 arp[0] 和 (arp+0)是一回事,但是最後那個((*(arp + 0)))就直接拿到的是這個結構體,所以可以使用 . 去訪問元素
然後arp由於是一個數組的名稱,因此它是第一個元素的地址,但其第一個元素爲指針,所以pd就是一個指針變量,它指向一個mytest 的指針,所以*pd就是結構體指針,需要用箭頭去訪問,然後(*pd)[0].test相當於做了兩次 * 號操作,所以可以直接用 . 去訪問