亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C/C++中指針和引用之相關問題深入研究

2020-01-26 15:47:05
字體:
來源:轉載
供稿:網友

一、基本知識
指針和引用的聲明方式:
聲明指針: 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的地址并沒有發生變化,但是其值已經發生了變化。

二、作為參數傳遞
利用引用的這個特性,可以用它作為函數的傳出參數。如程序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;
}

三、指針的指針
前面說到,指針是沒有級數限制的。
程序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

四、函數指針和函數引用
函數指針是C++最大的優點之一。和使用普通指針相比,高級程序員只要有可能都更愿意使用引用,因為引用更容易處理一些。然而,當處理函數時,函數引用對比函數指針就未必有這個優勢了?,F有的代碼很少使用函數引用。下面將向介紹如何函數指針、如何使用函數引用以及分別在什么情況下使用它們。

① 函數指針的例子

復制代碼 代碼如下:

#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);
等等。

五、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修飾引用沒有指針修飾指針那么復雜,只有一種形式。引用本身不能被改變,但所指向的對象是可以被改變的,見上面“一、基本知識”。
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。指針本身可以被改變

指針引用的用法還有其它的情形,由于罕用,故此不談及。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品久久精品亚洲人| 亚洲最大的av网站| 国产91露脸中文字幕在线| 国产精品第100页| 欧美视频一二三| 深夜福利日韩在线看| 亚洲精品理论电影| 久久久精品久久久| 亚洲曰本av电影| 777国产偷窥盗摄精品视频| 亚洲a中文字幕| 欧美大秀在线观看| 91精品国产91久久久久| 国产精品男人的天堂| 国产精品久久久久免费a∨大胸| 精品福利樱桃av导航| 国模吧一区二区三区| 国产亚洲综合久久| 日本一本a高清免费不卡| 亚洲一二在线观看| 亚洲电影免费观看高清完整版| 国产成人精品在线视频| 亚洲成人1234| 国产精品久久久久91| 亚洲视频网站在线观看| 亚洲一级黄色片| 久久久久久久999| 欧美精品日韩三级| 麻豆乱码国产一区二区三区| 91社区国产高清| 欧美在线观看视频| 国产精品久久久久久av下载红粉| 国产xxx69麻豆国语对白| 97香蕉超级碰碰久久免费的优势| 日韩成人xxxx| 欧美日韩高清在线观看| 久久国产一区二区三区| 欧美电影在线免费观看网站| 久久久中精品2020中文| 久久深夜福利免费观看| 2018中文字幕一区二区三区| 97超级碰碰碰| 日韩精品有码在线观看| 亚洲国产精品电影在线观看| 久久伊人精品天天| 欧美视频国产精品| 国产日本欧美一区二区三区在线| 国语自产精品视频在线看抢先版图片| 中文字幕欧美亚洲| 亚洲网站视频福利| 欧美激情18p| 欧美激情视频在线免费观看 欧美视频免费一| 国产欧美一区二区三区久久人妖| 国产精品草莓在线免费观看| 久久琪琪电影院| 亚洲天堂一区二区三区| 欧美最猛性xxxxx(亚洲精品)| 国产高清视频一区三区| 国产精品久久久久7777婷婷| 国产99久久精品一区二区 夜夜躁日日躁| 中文字幕亚洲综合久久筱田步美| zzijzzij亚洲日本成熟少妇| 日韩在线精品一区| 久久久欧美精品| 成人免费视频a| 欧美性猛交视频| 欧美高清视频免费观看| 精品久久久久久| 欧美性受xxxx白人性爽| 高跟丝袜一区二区三区| 92福利视频午夜1000合集在线观看| 日本亚洲精品在线观看| 91国产视频在线播放| 精品亚洲精品福利线在观看| 中文字幕亚洲在线| 日韩国产高清视频在线| 亚洲图片在线综合| 97婷婷大伊香蕉精品视频| 欧美精品激情在线观看| 正在播放国产一区| 国产这里只有精品| 国产精品网站大全| 久久久天堂国产精品女人| 国产精品麻豆va在线播放| 亚洲国产成人久久综合| 亚洲国产黄色片| 午夜精品福利在线观看| 国产精品久久久久久超碰| 色悠悠久久久久| 91久久综合亚洲鲁鲁五月天| 91高潮在线观看| 久久精品91久久久久久再现| 欧美日韩国产va另类| 欧美日韩免费网站| 国产精品久久久久久av福利软件| 成人黄色av网站| 伊人成人开心激情综合网| 国产日韩在线视频| 亚洲电影天堂av| 精品国产一区二区在线| 亚洲 日韩 国产第一| 精品日韩美女的视频高清| 97在线观看免费高清| 国产精品久久久久久久久久久新郎| 日韩精品小视频| 日韩欧美成人区| 超碰91人人草人人干| 亚洲自拍偷拍一区| 2019日本中文字幕| 欧美日韩日本国产| 日本成人精品在线| 欧美性猛交xxxx久久久| 亚洲一区亚洲二区亚洲三区| 欧美精品在线播放| 日韩精品在线观看网站| 欧美日韩美女在线观看| 亚洲激情视频在线观看| 久久久久久尹人网香蕉| 91av视频在线免费观看| 精品综合久久久久久97| 成人黄色在线播放| 亚洲精品视频免费在线观看| 欧美激情视频在线免费观看 欧美视频免费一| 成人伊人精品色xxxx视频| 九九热最新视频//这里只有精品| 亚洲欧美另类中文字幕| 亚洲深夜福利在线| 亚洲电影中文字幕| 97精品国产91久久久久久| 清纯唯美日韩制服另类| 亚洲精品女av网站| 久久久成人av| 在线观看欧美成人| 国产91色在线| 一本色道久久88精品综合| 国产一区二区三区毛片| 国产精品美女久久| 热久久免费视频精品| 国产精品色视频| 欧美综合在线第二页| 亚洲电影中文字幕| 日韩福利伦理影院免费| 日本欧美一级片| 国产精品ⅴa在线观看h| 欧美精品www| 亚洲精品videossex少妇| 久久青草福利网站| 亚洲福利视频二区| 国产成人精品优优av| 亚洲激情视频在线| 在线视频欧美日韩| 亚洲美女视频网| 日韩精品在线视频观看| 国产精品一区二区三区在线播放| 国产一区二区免费| 亚洲精品一区二区三区婷婷月| 久久影视免费观看| 久久青草精品视频免费观看| 欧美日韩爱爱视频| 蜜臀久久99精品久久久无需会员| 日韩电影免费在线观看中文字幕| 亚洲免费一级电影| 超薄丝袜一区二区| 久久这里有精品|