系統(tǒng)的內(nèi)存就像是帶有編號(hào)的小房間,如果想要使用內(nèi)存就需要得到房間號(hào),這個(gè)房間號(hào) 就是地址。
通過(guò)地址可以找到指定的內(nèi)存單元。
在C++語(yǔ)言中,有專門用來(lái)存放內(nèi)存單元地址的變量類型,它就是指針類型。
指針是一種數(shù)據(jù)類型,通常所說(shuō)的指針就是指針變量。
指針和地址其實(shí)是一個(gè)意思,所以指針就是地址,地址就是指針。
一、指針的聲明
聲明指針的一般形式如下:
數(shù)據(jù)類型標(biāo)識(shí) *指針變量名
如:
int *p;
float *p;
char *p;
二、指針的賦值
初始化時(shí)賦值:
int i = 100;
int* p = &i;
cout << "i的地址:" << p << endl;
先聲明后賦值:
int i = 100;
int* p;
p = &i;
cout << "i的地址:" << p << endl;
輸出的結(jié)果是:
i的地址:00000018046FFC04
符號(hào)&(取址符)的作用是:獲取變量的地址;
三、指針運(yùn)算
指針變量可以++(p++),也可以--(p--),可以將指針指向上一個(gè)或者下一個(gè)內(nèi)存地址。
先看下代碼:
int i = 100;
int* p;
p = &i;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
輸出結(jié)果是:
i的地址:000000AFDC8FF9A4
i的地址:000000AFDC8FF9A8
i的地址:000000AFDC8FF9AC
i的地址:000000AFDC8FF9B0
i的地址:000000AFDC8FF9B4
當(dāng)前指針的類型是int類型,int占用4個(gè)字節(jié),所以地址和地址之間的跨度是4個(gè)字節(jié)。
再看下代碼:
int16_t i = 6;
int16_t * p;
p = &i;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
p++;
cout << "i的地址:" << p << endl;
我們將int換成了int16_t,int16_t占2個(gè)字節(jié)。輸出結(jié)果是:
i的地址:000000D0F66FF9B4
i的地址:000000D0F66FF9B6
i的地址:000000D0F66FF9B8
i的地址:000000D0F66FF9BA
i的地址:000000D0F66FF9BC
可以看到,地址和地址之間的跨度變成了2個(gè)字節(jié)。
所以,可以使用自增和自減運(yùn)算符將指針指向下(上)一個(gè)地址。
四、指向空的指針和空類型指針
(1)指向空的指針
int * p;
如上代碼的指針p沒有被初始化,沒有被初始化的指針都不可以使用,一旦使用沒有初始化的指針程序?qū)⒈罎?,這是比較危險(xiǎn)的。
有時(shí)候,我們寧愿先將指針指向空:
int * p = NULL;
使用指向空的指針程序不會(huì)崩潰。
如果要輸出p,p的輸出結(jié)果是:
0000000000000000
(2)空類型的指針
void* PI;
使用void修飾數(shù)據(jù)類型的指針?lè)Q為空類型的指針,空類型的指針可以被任意類型接受,如:
int i = 6;
void* PI = &i;
有必要的話,可以使用強(qiáng)制轉(zhuǎn)換:
(int *)PI
五、指針與數(shù)組
指針可以指向數(shù)組
int a[] = {1, 2, 3, 4, 5};
int* p = a; // 指向第一個(gè)元素
cout << p << endl;
cout << *p << endl;
如上代碼,指針變量p指向了數(shù)組a,a默認(rèn)是第一個(gè)元素的地址。
那么如何指向第二個(gè)元素呢?
int* p = a + 1;
或者
int* p = a;
p++;
由于數(shù)組是一個(gè)連續(xù)的空間,所以以上的寫法成立。
所以,指針可以遍歷數(shù)組,代碼如下:
int a[] = {1, 2, 3, 4, 5};
int* p = a;
int len = sizeof(a) / sizeof(int);
for (size_t i = 0; i < len; i++)
{
cout << *p++ << endl;
}
二維數(shù)組也是一樣,也可以使用指針遍歷。
指針可以遍歷字符串,代碼如下:
char str[] = "hello word"; // 聲明并初始化字符串
char* p = str; // 將指針指向字符串str
while (*p != '\0')
{
cout << *p++;
}
cout << endl;
字符串的結(jié)尾必然是字符“\0”,所以可以利用這個(gè)特性遍歷字符串。
指針可以拼接字符串,代碼如下:
char str1[20] = "my name is ";
char str2[] = "zhangsan";
char* p1 = str1;
char* p2 = str2;
// 將指針指向str1的末尾
while (*p1 != '\0')
{
p1++;
}
// 將p2拼接到p2后面
while (*p2 != '\0')
{
*p1++ = *p2++;
}
*p1 = '\0';
cout << str1 << endl;
六、指針在函數(shù)中的使用
先看下代碼,實(shí)現(xiàn)數(shù)據(jù)交換的功能:
#include <iostream>
#include <string>
using namespace std;
void swap(int a, int b);
int main()
{
int x = 3;
int y = 4;
swap(x, y);
cout << "x=" << x << endl;
cout << "y=" << y << endl;
return 0;
}
void swap(int a, int b) {
int tmp;
tmp = a;
a = b;
b = tmp;
}
輸出結(jié)果是:
x=3
y=4
需要在注意的是:swap方法必須聲明,否則默認(rèn)調(diào)用xkeycheck.h中的swap方法;
依據(jù)輸出結(jié)果可以看出,交換功能沒有實(shí)現(xiàn),因?yàn)楫?dāng)x,y做為實(shí)參傳遞給swap時(shí)就會(huì)產(chǎn)生x、y的副本,swap的方法實(shí)現(xiàn)只是x、y副本的交換;
我們可以將交換的代碼放在main方法中,這樣就不會(huì)產(chǎn)生副本:
int x = 3;
int y = 4;
int tmp;
tmp = x;
x = y;
y = tmp;
cout << "x=" << x << endl;
cout << "y=" << y << endl;
但是,數(shù)據(jù)的交換屬于一種功能,從代碼優(yōu)化的角度上來(lái)說(shuō),我們應(yīng)該將數(shù)據(jù)交換功能的代碼單獨(dú)提取出來(lái)用一個(gè)方法來(lái)封裝。
我們可以使用指針的方式實(shí)現(xiàn)數(shù)據(jù)的交換:
#include <iostream>
#include <string>
using namespace std;
void swap(int* a, int* b);
int main()
{
int x = 3;
int y = 4;
swap(&x, &y);
cout << "x=" << x << endl;
cout << "y=" << y << endl;
return 0;
}
void swap(int* a, int* b) {
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
輸出結(jié)果是:
x=4
y=3
依據(jù)輸出結(jié)果得出結(jié)論,使用指針做為參數(shù)的方法,可以真正實(shí)現(xiàn)數(shù)據(jù)的交換。
七、引用
我們可以使用引用的方式實(shí)現(xiàn)數(shù)據(jù)的交換。
在xkeycheck.h中,有一個(gè)swap方法:
_CONSTEXPR20 void swap(_Ty& _Left, _Ty& _Right) noexcept(
is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_assignable_v<_Ty>) {
_Ty _Tmp = _STD move(_Left);
_Left = _STD move(_Right);
_Right = _STD move(_Tmp);
}
這個(gè)函數(shù)的形式參數(shù)變量使用“&”做為變量的引用。那么我們完全可以用引用的方式寫一個(gè)數(shù)據(jù)交換的功能,代碼如下:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int x = 3;
int y = 4;
swap(x, y);
cout << "x=" << x << endl;
cout << "y=" << y << endl;
return 0;
}
void swap(int& a, int& b) {
int tmp;
tmp = a;
a = b;
b = tmp;
}
八、指向函數(shù)的指針
代碼如下:
#include <iostream>
#include <string>
using namespace std;
int sum(int a, int b);
int main()
{
int x = 3;
int y = 4;
int(*p)(int, int) = sum;
int s = (*p)(x, y);
cout << "s=" << s << endl;
return 0;
}
int sum(int a, int b) {
return a + b;
}
int(*p)(int, int) = sum將指針指向函數(shù)sum,(int, int)做為函數(shù)sum的形式參數(shù),(*p)(x, y)將值傳入函數(shù),s是函數(shù)sum的返回值。
九、數(shù)組指針和指針數(shù)組
(1)數(shù)組指針
int a[][2] = {1,2,3,4};
int(*p)[2];
p = a;
a是一個(gè)二維數(shù)組,指針p就是a的行,所以數(shù)組指針也稱指向一維數(shù)組的指針,亦稱行指針。
(2)指針數(shù)組
指針數(shù)組的聲明形式是:
int *a[];
指針數(shù)組中的值都是地址。
演示代碼如下:
int x = 3;
int y = 4;
int *a[] = {&x, &y};
for (size_t i = 0; i < 2; i++)
{
cout << a[i] << endl;
}
輸出結(jié)果是:
00000025E70FFAA4
00000025E70FFAC4
指針數(shù)組中每個(gè)元素占用8個(gè)字節(jié)的內(nèi)存空間。
如果想要輸出具體的值,代碼如下:
int x = 3;
int y = 4;
int *a[] = {&x, &y};
for (size_t i = 0; i < 2; i++)
{
cout << *a[i] << endl;
}
a[i]存儲(chǔ)的是一個(gè)指針(地址),所以只要將a[i]改成 *a[i]即可。
指針數(shù)組的元素可以直接賦值為字符串:
const char* a[] = {"hello", "word"};
for (size_t i = 0; i < 2; i++)
{
cout << a[i] << endl;
}
a[0]就是"hello",a[1]就是"word"。
使用*a[i]可以操作各自元素的字符串。
[本章完...]