這些天筆試經常碰到指針的題目,故做了一個整理。
指針在應用中的各種類型:
(1) int *ip; //(char* cp,float* fp,double* dp) 一級指針變量
(2) int **ip; //(char** cp,float** fp,double** dp) 二級指針變量
(3) const int* ip; //常量指針
(4) int* const ip; //指針常量
(5) const int* const icp; //常量指針常量
(6) int *ip[10]; //指針數組
(7) int (*ip)[10]; //數組指針
(8) int *f(int a); //char* copy(char* s1, char* s2) 指針函數
(9) int (*gp)(int); //函數指針
(10) int (*gp[10])(int); //函數指針的數組
下面針對這11中情況作一一解說
(1) int *ip; //(char* cp,float* fp,double* dp) 一級指針變量
一級指針是我們接觸最多,也是最簡單的指針。
#include <iostream>
using namespace std;
int main(void)
{
char *p = "hello world";
int i = 0;
while(p[i] != '\0')
cout << p[i++];
cout << '\n';
int *ip, iq;
iq = 5;
ip = &iq;
}
上面程序體現了二個問題:1)指針的增減運算 2)定義指針一個*只能修飾一個指針
指針的增減是以該類型的實體大小為單位的(但對指針作乘除是沒有意義的)
對char指針加 1 實際增加了1個字節
對float指針加 1 實際增加了4個字節
對int指針加 1 實際增加了4個字節
定義指針一個*只能修飾一個指針
程序中的 ip是一個指針,而iq是一個整數變量
在很多c/c++程序,指針跟數組進行替換使用,這樣以使得我們覺得指針跟數值是等價,但是數組跟指針還是有很大區別的,請看下面的例子。
#include <iostream>
using namespace std;
void fun(char a[100])
{
cout << sizeof(a) << '\n'; //4
}
int main(void)
{
char a[] = "hello world";
a[0] = 'Y';
char *p = "welcome to c++";
// p[0] = 'X'; //編譯器不能發現該錯誤,但是運行時出錯
cout << sizeof(a) << '\n'; //12
cout << sizeof(p) << '\n'; //4
fun(a);
}
看了上面的例子之後,我們來說說數組跟指針的區別:
數組要麼在靜態存儲區被創建(如全局數組),要麼在棧上被創建。數組名對應著(而不是指向)一塊內存,其地址與容量在生命期內保持不變,只有數組的內容可以改變。
指針可以隨時指向任意類型的內存塊,它的特征是“可變”,所以我們常用指針來操作動態內存。指針遠比數組靈活,但也更危險。
字符數組 a 的容量是 12 個字符,其內容為 hello world\0。a 的內容可以改變,如 a[0]= ‘Y’。指針 p 指向常量字符串“welcome to c++”,(位於靜態存儲區,內容為 welcome to c++\0) ,
常量字符串的內容是不可以被修改的。從語法上看,編譯器並不覺得語句 p[0]= ‘X’有什麼不妥,但是該語句企圖修改常量字符串的內容而導致運行錯誤,
所以我們經常把char *p = "welcome to c++" 寫成 const char *p = "welcome to c++",這樣就能夠使得 p[0] = 'X'語句在編譯的時候就報錯。
sizeof(a)的大小是12(注意別忘了\0) sizeof(p)是一個指針的大小,當把數組作為函數參數進行傳遞時,數組自動退化為指針。
指針參數如何傳遞內存的?
#include <iostream>
#include <string.h>
using namespace std;
char* getStr1()
{
char a[] = "hello world";
return a;
}
char* getStr2()
{
char *a = new char[12];
strcpy(a, "hello world");
return a;
}
char* getStr3()
{
char *a = "hello world";
return a;
}
void getStr4(char *p, int num)
{
p = new char[num];
strcpy(p, "hello world");
}
void getStr5(char **p,int num)
{
*p = new char[num];
strcpy(*p, "hello world");
}
int main(void)
{
cout << "getStr1 " << getStr1() << '\n';
char *str2 = NULL;
str2 = getStr2();
cout << "getStr2 " << str2 << '\n';
str2[0] = 'X';
cout << "getStr2 " << str2 << '\n';
delete [] str2;
char *str3 = NULL;
str3 = getStr3();
cout << "getStr3 " << str3 << '\n';
// str3[0] = 'X'; //編譯通過,但運行出錯
char *str4 = NULL;
int num = 50;
getStr4(str4, num);
cout << "getStr4 " << (str4 == NULL?"NULL": str4) << '\n';
char *str5 = NULL;
getStr5(&str5, num);
cout << "getStr5 " << str5 << '\n';
}
getStr1()輸出的是亂碼,因為在getStr1()裡return的是棧內存,所以當退出函數之後,char a[]也隨著被清除了。
getStr2()能夠正確運行。
getStr3()雖然能夠正確運行,但是不管在什麼地方返回都是常量數據
getStr4()運行結束後,str4仍然還是NULL,因為char *p參數在getStr4()函數運行結束後隨著也被清除了
getStr5()能夠正確運行。
(2) int **ip; //(char** cp,float** fp,double** dp) 二級指針變量
二級指針變量與一級指針變量的關系,跟二維數組與一維數組的關系有些類似,在這就不多說了
(3) const int* ip; //常量指針
(4) int* const ip; //指針常量
(5) const int* const icp; //常量指針常量
這3個有一些相似性,看如下例子。
#include <iostream>
using namespace std;
int main(void)
{
const int a = 10;
int b = 5;
int c = 13;
const int *ip = &a;
int* const cp = &b;
const int* const icp = &c;
*ip = 30; //錯誤,常量指針不能修改指向的常量,*ip只能做右值
ip = &c; //正確,指針本身能夠改變
*cp = 50; //正確,指針常量,指針所指向的值能夠修改
cp = &c; //錯誤,指針常量本身不能夠改變
*icp = 50;//錯誤,常量指針常量不能修改指向的常量
icp = &c; //錯誤,常量指針常量不能夠改變指針本身
}
(6) int *ip[10]; //指針數組
(7) int (*ip)[10]; //數組指針
指針數組:顧名思義,就是說的首先是一個數組吧,然後數組的元素是指針而已
數組指針:指向一個數組的指針
#include <iostream>
using namespace std;
int main()
{
int a = 5, b = 1, c = 10;
int *p1 = &a, *p2 = &b, *p3 = &c;
int *p[3] = {p1, p2, p3}; //指針數組,數組中的每個元素都是一個指針
for(int i = 0; i < 3; i++)
cout << *p[i] << '\n';
int (*ap)[3]; //數組指針
int arr[4][3] = {{1,2},{2,3},{3}};
ap = arr;
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < sizeof(ap[i])/sizeof(int); j++)
cout << ap[i][j] << " ";
cout << '\n';
}
}
(8) int *f(int a); //char* copy(char* s1, char* s2) 指針函數
指針函數是指函數返回值是一個指針類型
如我們在c語言中經常使用strcpy就是這樣一個函數
char* strcpy(char* Dest, const char* Src)
{
assert(Dest != NULL &&Src !=NULL);
char* tmp = Dest;
while((*Dest++ = *Src++) != '\0');
return tmp;
}
看到這函數,我們可能會覺得為什麼要有一個返回值呢?不是把Src的內容都已經復制到Dest中了麼,這個主要是為了完成了鏈式運算,為了是strcpy函數能夠作右值。
(9) int (*gp)(int); //函數指針
(10) int (*gp[10])(int); //函數指針的數組
#include <iostream>
using namespace std;
void fun1(){ cout << "good" << '\n';}
void fun2(){ cout << "better" << '\n';}
void fun3(){ cout << "best" << '\n';}
int main()
{
void (*gp)() = fun1; //函數指針
gp();
void (*gpa[3])();//函數指針數組
gpa[0] = fun1;
gpa[1] = fun2;
gpa[2] = fun3;
for(int i = 0; i < 3; i++)
gpa[i]();
}