C/C++中指針和援用之相干成績深刻研討。本站提示廣大學習愛好者:(C/C++中指針和援用之相干成績深刻研討)文章只能為提供參考,不一定能成為您想要的結果。以下是C/C++中指針和援用之相干成績深刻研討正文
1、根本常識
指針和援用的聲明方法:
聲明指針: char* pc;
聲明援用: char c = 'A'
char& rc = c;
它們的差別:
①從景象上看,指針在運轉時可以轉變其所指向的值,而援用一旦和某個對象綁定後就不再轉變。這句話可以懂得為:指針可以被從新賦值以指向另外一個分歧的對象。然則援用則老是指向在初始化時被指定的對象,今後不克不及轉變,然則指定的對象其內容可以轉變。
②從內存分派上看,法式為指針變量分派內存區域,而不為援用分派內存區域,由於援用聲明時必需初始化,從而指向一個曾經存在的對象。援用不克不及指向空值。
③從編譯上看,法式在編譯時分離將指針和援用添加到符號表上,符號表上記載的是變量名及變量所對應地址。指針變量在符號表上對應的地址值為指針變量的地址值,而援用在符號表上對應的地址值為援用對象的地址值。符號表生成後就不會再改,是以指針可以轉變指向的對象(指針變量中的值可以改),而援用對象不克不及改。這是應用指針不平安而應用援用平安的重要緣由。從某種意義下去說援用可以被以為是不克不及轉變的指針。
④不存在指向空值的援用這個現實意味著應用援用的代碼效力比應用指針的要高。由於在應用援用之前不須要測試它的正當性。相反,指針則應當老是被測試,避免其為空。
⑤實際上,關於指針的級數沒無限制,然則援用只能是一級。
以下:
int** p1; // 正當。指向指針的指針
int*& p2; // 正當。指向指針的援用
int&* p3; // 不法。指向援用的指針長短法的
int&& p4; // 不法。指向援用的援用長短法的
留意上述讀法是從左到右。
法式1:
#include "stdio.h"
int main(void)
{
// 聲明一個char型指針pc,且讓它指向空值
char* pc = 0;
char a = 'a';
// 聲明一個援用rc,且讓它援用變量a
char& rc = a;
printf("%d, %c\n", pc, rc);
char *pc2;
// 聲明一個指針,但可以不初始化
pc2 = pc;
// char& rc2;
// 下面語句編譯時,會發生以下毛病:
// error C2530: 'rc2' : references must be initialized
// 即,運用必需初始化
// rc = *pc;
// 下面語句編譯不會有成績,但運轉時,會報以下毛病:
// "0x00401057"指令援用的"0x00000000"內存。該內存不克不及為"read"
// 解釋援用在任何情形下,都不克不及指向空值
return 0;
}
法式2:
#include <iostream>
#include <string>
using namespace std;
int main(void)
{
string s1("Hello");
string s2("World");
// printf("%s\n", s1); 不克不及用printf輸入s1,而應當用cout
cout << "s1的地址 = "<< &s1 << endl;// &s1 = 0012FF64
cout << "s2的地址 = "<< &s2 << endl;// &s2 = 0012FF54
string& rs = s1; // 1. 界說一個援用rs,rs援用s1
cout << "援用rs的地址 = " << &rs << endl; // &rs = 0012FF64
string* ps = &s1; //界說一個指針ps, ps指向s1
cout << "指針ps的地址 = " << ps << endl;// ps = 0012FF64
cout << rs << ", " << *ps << endl; // Hello, Hello
// 假如沒有#include <string>,下面的語句在編譯的時刻,會湧現以下毛病:
// error C2679: binary '<<' : no operator defined which takes a right-
// hand operand of type 'class std::basic_string<char,struct
// std::char_traits<char>,class std::allocator<char> >'
// (or there is no acceptable conversion)
rs = s2; // 2. rs仍然援用s1, 然則s1如今的值是"World"
ps = &s2; // ps如今指向s2
cout << "援用rs的地址 = " << &rs << endl; // &rs = 0012FF64 未轉變
cout << "援用rs的值 = " << rs << endl; // rs = "World" 已轉變
cout << "指針ps的地址 = " << ps << endl;// ps = 0012FF54 已轉變
cout << "指針ps所指地址的內容 = " << *ps << endl; // *ps = World已轉變
cout << "s1的地址 = "<< &s1 << endl;// 3. &s1 = 0012FF64 未轉變
cout << "s1的值 = " << s1 << endl; // 4. s1 = World 已轉變
return 0;
}
可以以為:
援用就是變量的別號,在援用初始化的時刻就曾經肯定,今後不克不及再轉變。見法式2的粗體字語句。第1句,聲清楚明了rs援用s1,s1的值為”Hello”,從這今後,rs現實上就相當於變量s1了,或許從更實質的意義下去說,rs的地址就是初始化時s1的地址了,今後都不會再轉變。這應當比擬好懂得,好比我們在法式中界說了一個變量a,不論我們若何給a賦值,但它的地址是不會轉變的;
第2句,rs仍然指向初始化時s1的地址,但此處的賦值就相當於從新給s1賦值,是以我們從第3句和第4句可以看到,s1的地址並沒有產生變更,然則其值曾經產生了變更。
2、作為參數傳遞
應用援用的這個特征,可以用它作為函數的傳出參數。如法式3:
#include <iostream>
#include <string>
using namespace std;
int newEvaluation(string& aStr)
{
string bStr("Hello,");
aStr = bStr + aStr;
return 0;
}
int main(void)
{
string aStr("Patrick!");
newEvaluation(aStr);
std::cout << aStr << endl; // 輸入成果:"Hello, Patrick!"
return 0;
}
而普通變量,則不克不及從函數外部傳值出來,好比法式4:
#include <iostream>
#include <string>
using namespace std;
int newEvaluation(string aStr)
{
string bStr("Hello,");
aStr = bStr + aStr;
return 0;
}
int main(void)
{
string aStr("Patrick!");
newEvaluation(aStr);
std::cout << aStr << endl; // 輸入成果:"Patrick!",aStr的值沒有變更
return 0;
}
固然法式3援用傳遞的方法也能夠寫成指針傳遞的方法,如法式5:
#include <iostream>
#include <string>
using namespace std;
int newEvaluation(string* const aStr)
{
string bStr("Hello,");
*aStr = bStr + *aStr;
return 0;
}
int main(void)
{
string aStr("Patrick!");
newEvaluation(&aStr);
std::cout << aStr << endl; // 輸入成果:"Hello, Patrick!"
return 0;
}
留意法式中的陷井,如法式6:
#include <iostream.h>
int *pPointer;
void SomeFunction()
{
int nNumber;
nNumber = 25;
//讓指針指向nNumber
pPointer = &nNumber;
}
void main()
{
SomeFunction();//為pPointer賦值
//為何這裡掉敗了?為何沒有獲得25
cout << "Value of *pPointer: " << *pPointer << endl;
}
這段法式先挪用了SomeFunction函數,創立了個叫nNumber的變量,接著讓指針pPointer指向了它。可是成績出在哪兒呢?當函數停止後,nNumber被刪失落了,由於這一個部分變量。部分變量在界說它的函數履行完後都邑被體系主動刪失落。也就是說當SomeFunction 函數前往主函數main()時,這個變量曾經被刪失落,但pPointer還指著變量已經用過的但如今已不屬於這個法式的區域。
雖然在SomeFunction中應用所謂的靜態分派內存。法式7中也存在陷井:
#include <iostream.h>
int *pPointer;
void SomeFunction()
{
int intNumber = 25;
// 讓指針指向一個新的整型
pPointer = new int;
pPointer = &intNumber;
}
void main()
{
SomeFunction(); // 為pPointer賦值
cout<< "Value of *pPointer: " << *pPointer << endl;
delete pPointer;
}
緣由也如下面所言,intNumber的感化規模僅限於SomeFunction中,分開了SomeFunction,那末intNumber就不存在了,那末&intNumber即intNumber的地址就變得沒成心義了,是以,該地址所指向的值是不肯定的。假如改成上面的法式就不會有成績了。
法式8:
#include <iostream.h>
int *pPointer;
void SomeFunction()
{
int intNumber = 25;
// 讓指針指向一個新的整型
pPointer = new int(intNumber);
}
void main()
{
SomeFunction(); // 為pPointer賦值
cout<< "Value of *pPointer: " << *pPointer << endl;
delete pPointer;
}
3、指針的指針
後面說到,指針是沒有級數限制的。
法式9:
#include<stdio.h>
#include<stdlib.h>
void main(void)
{
int i, j;
int a[10], b[3][4], *p1, *p2, **p3;
for(i = 0; i < 10; i++)
scanf("%d", &a[i]);
for(i = 0; i < 3; i++)
for(j = 0; j < 4; j++)
scanf("%d", &b[i][j]);
p1 = a;
p3 = &p1;
for(i = 0; i < 10; i++)
printf("%4d", *(*p3+i));
printf("\n");
for(p1 = a; p1 - a < 10; p1++)
{
p3 = &p1;
printf("%4d", **p3);
}
printf("\n");
for(i = 0; i < 3; i++)
{
p2 = b[i];
p3 = &p2;
for(j = 0; j < 4; j++)
printf("%4d",*(*p3+j));
printf("\n");
}
for(i = 0; i < 3; i++)
{
p2 = b[i];
for(p2 = b[i]; p2-b[i] < 4; p2++)
{
p3 = &p2;
printf("%4d", **p3);
}
printf("\n");
}
}
輸入的成果:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
11 12 13 14
15 16 17 18
19 20 21 22
11 12 13 14
15 16 17 18
19 20 21 22
4、函數指針和函數援用
函數指針是C++最年夜的長處之一。和應用通俗指針比擬,高等法式員只需有能夠都更情願應用援用,由於援用更輕易處置一些。但是,當處置函數時,函數援用比較函數指針就未必有這個優勢了。現有的代碼很少應用函數援用。上面將向引見若何函數指針、若何應用函數援用和分離在甚麼情形下應用它們。
① 函數指針的例子
#include <iostream>
void print(int i)
{
std::cout << i << std::endl;
}
void multiply(int& nDest, int nBy)
{
nDest *= nBy;
}
void print_something()
{
std::cout << "something" << std::endl;
}
int sayHello()
{
std::cout << "Hello, World!" << std::endl;
return 10;
}
int main()
{
void (*pFunction_1)(int);
pFunction_1 = &print;
pFunction_1(1);
// 輸入成果為1
void (*pFunction_2)(int&, int) = &multiply;
int i = 1;
pFunction_2(i, 10);
std::cout << "i = " << i << std::endl;
// 輸入成果為10
void (*pFunction_3)();
pFunction_3 = &print_something;
pFunction_3();
// 輸入成果為something
int (*pFunction_4)();
pFunction_4 = &sayHello;
int a = pFunction_4();
// 輸入成果為Hello, World!
std::cout << a << std::endl;
// 輸入成果為10
return 0;
}
② 函數援用的例子
#include <iostream>
void print(int i)
{
std::cout << i << std::endl;
}
void print2(int i)
{
std::cout << i << std::endl;
}
void multiply(int& nDest, int nBy)
{
nDest *= nBy;
}
void print_something()
{
std::cout << "something" << std::endl;
}
int sayHello()
{
std::cout << "Hello, World!" << std::endl;
return 10;
}
int main()
{
// void (&rFunction_1)(int);
// 毛病:未初始化援用!援用必需初始化
void (&rFunction_2)(int) = print;
rFunction_2(1);
// 輸入1
rFunction_2 = print2;
rFunction_2(2);
// 輸入2
void (&rFunction_3)(int&, int) = multiply;
int i = 1;
rFunction_3(i, 10);
std::cout << i << std::endl;
// 輸入10
void (&rFunction_4)() = print_something;
rFunction_4();
// 輸入something
int (&rFunction_5)();
rFunction_5 = sayHello;
int a = rFunction_5(); // 輸入Hello, World!
std::cout << a << std::endl;
// 輸入10
return 0;
}
③ 函數指針和函數援用作為函數參數
#include <iostream>
void print(int i)
{
std::cout << i << std::endl;
}
void print2(int i)
{
std::cout << i * 2 << std::endl;
}
void printSomething()
{
std::cout << "Something" << std::endl;
}
void sayHello()
{
std::cout << "Hello, World!" << std::endl;
}
void call_p_func(void (*func)(int))
{
func(1);
func(2);
func(3);
}
void call_r_func(void (&func)(int))
{
func(1);
func(2);
func(3);
}
void call_p_function(void (*func)())
{
func();
}
int main()
{
std::cout << "函數指針作為參數" << std::endl;
call_p_func(&print);
call_p_func(&print2);
call_p_function(&printSomething);
call_p_function(&sayHello);
call_p_function(sayHello);
// 下面兩句關於某些編譯器來講是一樣的,然則推舉應用前者的寫法,
// 如許可所以法式的可讀性更好一些
std::cout << "函數援用作為參數" << std::endl;
call_r_func(print);
call_r_func(print2);
return 0;
}
總結:
函數指針的聲明應用方法:
<想要指向的函數之前往類型>(*函數指針的稱號)<想要指向的函數之參數類型…>
如要想聲明一個函數指針指向以下函數:
void print(int i)
{
std::cout << i << std::endl;
}
那末便可以以下操作:
void (*pFunction)(int);
然後以下用函數的地址給pFunction賦值:
pFunction = &print;
在然後,pFunction便可以和函數print一樣應用了,好比,
pFunction(1);
等等。
函數援用的聲明和應用方法:
<欲援用的函數之前往類型>(&函數援用的稱號)<欲援用的函數之參數類型…>=<欲援用的函數的稱號>,至所以如斯,是援用在聲明的時刻必需初始化,援用不克不及指向空值。
如要想聲明一個函數援用指向以下函數:
void print(int i)
{
std::cout << i << std::endl;
}
那末便可以以下操作:
void (&rFunction)(int)=print;
在然後,rFunction便可以和函數print一樣應用了,好比,
rFunction(1);
等等。
5、const潤飾指針和援用
年夜致而言,const潤飾指針和援用分三種情形,即const潤飾指針、const潤飾援用和const潤飾指針的援用。上面分離評論辯論之。
① const潤飾指針
const潤飾指針又分為三種情形,即const潤飾指針自己、const潤飾指針所指的變量(或對象)和const潤飾指針自己和指針所指的變量(或對象)。
a. const潤飾指針自己
在這類情形下,指針自己是常量,不克不及轉變,任何修正指針自己的行動都長短法的,例如:
double pi = 3.1416;
double* const PI = π
double alpha = 3.14;
PI = α // 毛病。由於指針PI是常量,不克不及再被轉變。
*PI = alpha; // OK。固然指針PI不克不及被轉變,但指針所指的變量或許對象可變。
b. const潤飾指針指向的變量(或對象)
在這類情形下,指針自己可以轉變,但const所潤飾的指針所指向的對象不克不及被轉變,例如:
double pi = 3.1416;
const double* PI = π
double alpha = 3.14;
*PI = alpha;// 毛病。由於PI所指向的內容是常量,是以*PI不克不及被轉變。
PI = α// OK。固然指針所指的內容不克不及被轉變,但指針PI自己可轉變。從而經由過程這類方法轉變*PI。
c. const潤飾指針自己和指針所指向的變量(或對象)
在這類情形下,指針自己和指針指向的變量(或對象)均不克不及被轉變,例如:
double pi = 3.146;
const double* const PI = π
//double const* const PI = π
cout << "PI = " << PI << endl;
cout << "*PI = " << *PI << endl;
double alpha = 3.14;
//*PI = alpha; // 毛病。由於PI所指向的內容是常量,是以*PI不克不及被轉變。
//PI = α // 毛病。由於指針PI是常量,不克不及再被轉變。
② const潤飾援用
const潤飾援用沒有指針潤飾指針那末龐雜,只要一種情勢。援用自己不克不及被轉變,但所指向的對象是可以被轉變的,見下面“1、根本常識”。
double pi = 3.1416;
//const double& PI = pi;
double const& PI = pi; //和下面一句是等價的
//double& const PI = pi;//有成績。許多編譯器會發生warning
cout << PI << endl;
③ const潤飾指針援用
我們用例子來講明。
double pi = 3.14;
const double* pPI = π
//const double*& rPI = π //毛病。不克不及將double* 轉換成const double *&
const double*& rPI = pPI; //OK。聲明指針援用的准確辦法
解釋:const double*& rPI = π 為何會湧現毛病呢?我們曉得,援用是被援用對象的別號,正由於如斯,因為rPI是pPI的別號,是以rPI和pPI的類型必需完整分歧。從下面的代碼段我們可以看到,rPI的類型是const double*,而&pi的類型是double*,是以這句法式是毛病的。
上面這段代碼和 ① 中的b中的情況對應(即內容弗成變,指針可變):
double pi = 3.1416;
double api = 3.14;
const double* pPI = π
const double* pAPI = &api;
const double*& rPI = pPI;
const double*& rAPI = pPI;
*rAPI = api; // 毛病。指針所指向的值不克不及被直接轉變
rAPI = pAPI; // OK。指針自己可以被轉變
指針援用的用法還有其它的情況,因為少用,故此不談及。