目錄
1. C++關鍵字
2. 命名空間
2.1 命名空間定義
2.2 命名空間使用
3. C++輸入&輸出
4. 缺省參數
4.1 缺省參數概念
4.2 缺省參數分類
5. 函數重載
5.1 函數重載概念
5.2 C++支持函數重載的原理--名字修飾(name Mangling)
6. 引用
6.1 引用概念
6.2 引用特性
6.3 常引用
6.4 使用場景
6.5 傳值、傳引用效率比較
6.5.1 值和引用作為函數參數的性能比較
6.5.2 值和引用作為返回值類型的性能比較
6.6 引用和指針的區(qū)別
7. 內聯函數
7.1 概念
7.2 特性
8. auto關鍵字(C++11)
8.1 類型別名思考
8.2 auto簡介
8.3 auto的使用細則
8.4?auto不能推導的場景
9. 基于范圍的for循環(huán)(C++11)
9.1 范圍for的語法
9.2 范圍for的使用條件
10. 指針空值nullptr(C++11)
10.1 C++98中的指針空值
1. C++關鍵字
C++總計63個關鍵字,C語言32個關鍵字
ps:下面我們只是看一下C++有多少關鍵字,不對關鍵字進行具體的講解。
asm | do | if | return | try | continue |
auto | double | inline | short | typedef | for |
bool | dynamic_cast | int | signed | typeid | public |
break | else | long | sizeof | typename | throw |
case | enum | mutable | static | union | wchar_t |
catch | explicit | namespace | static_cast | unsigned | default |
char | export | new | struct | using | friend |
class | extern | operator | switch | virtual | register |
const | false | private | template | void | true |
const_cast | float | protected | this | volatile | while |
delete | goto | reinterpret_cast |
2. 命名空間
在C/C++中,變量、函數和后面要學到的類都是大量存在的,這些變量、函數和類的名稱將都存在于全局作用域中,可能會導致很多沖突。使用命名空間的目的是對標識符的名稱進行本地化,以避免命名沖突或名字污染,namespace關鍵字的出現就是針對這種問題的。
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
// C語言沒辦法解決類似這樣的命名沖突問題,所以C++提出了namespace來解決
int main()
{
printf("%d\n", rand);
return 0;
}
// 編譯后后報錯:error C2365: “rand”: 重定義;以前的定義是“函數”
2.1 命名空間定義
定義命名空間,需要使用到namespace關鍵字,后面跟命名空間的名字,然后接一對{}即可,{}中即為命名空間的成員。
// tjq是命名空間的名字,一般開發(fā)中是用項目名字做命名空間名。
// 1. 正常的命名空間定義
namespace tjq
{
// 命名空間中可以定義變量/函數/類型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
//2. 命名空間可以嵌套
// test.cpp
namespace N1
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N2
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
//3. 同一個工程中允許存在多個相同名稱的命名空間,編譯器最后會合成同一個命名空間中。
// ps:一個工程中的test.h和上面test.cpp中兩個N1會被合并成一個
// test.h
namespace N1
{
int Mul(int left, int right)
{
return left * right;
}
}
注意:一個命名空間就定義了一個新的作用域,命名空間中的所有內容都局限于該命名空間中。
2.2 命名空間使用
命名空間中成員該如何使用呢?比如:
namespace N
{
// 命名空間中可以定義變量/函數/類型
int a = 0;
int b = 1;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
// 編譯報錯:error C2065: “a”: 未聲明的標識符
printf("%d\n", a);
return 0;
}
命名空間的使用有三種方式:
- 加命名空間名稱及作用域限定符
int main()
{
printf("%d\n", N::a);
return 0;
}
- 使用using將命名空間中某個成員引入
using N::b;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
return 0;
}
- 使用using namespace命名空間名稱引入
using namespce N;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
3. C++輸入&輸出
新生嬰兒會以自己獨特的方式向這個嶄新的世界打招呼,C++剛出來后,也算是一個新事物,那C++是否也應該向這個美好的世界來聲問候呢?我們來看下C++是如何來實現問候的。
#include <iostream>
// std是C++標準庫的命名空間名,C++將標準庫的定義實現都放到這個命名空間中
using namespace std;
int main()
{
cout << "Hello world!!!" << endl;
return 0;
}
說明:
- 使用cout標準輸出對象(控制臺)和cin標準輸入對象(鍵盤)時,必須包含<iostream>頭文件以及按命名空間使用方法使用std。
- cout和cin是全局的流對象,endl是特殊的C++符號,表示換行輸出,他們都包含在<iostream>頭文件中。
- <<是流插入運算符,>>是流提取運算符。
- 使用C++輸入輸出更方便,不需要像printf/scanf輸入輸出時那樣,需要手動控制格式。C++的輸入輸出可以自動識別變量類型。
- 實際上cout和cin分別是ostream和istream類型的對象,>>和<<也涉及運算符重載等知識,這些知識我們我們后續(xù)才會學習,所以我們這里只是簡單學習他們的使用。后面我們還有一個章節(jié)更深入的學習IO流用法及原理。
注意:早期標準庫將所有功能在全局域中實現,聲明在.h后綴的頭文件中,使用時只需包含對應頭文件即可,后來將其實現在std命名空間下,為了和C頭文件區(qū)分,也為了正確使用命名空間,規(guī)定C++頭文件不帶.h;舊編譯器(vc 6.0)中還支持<iostream.h>格式,后續(xù)編譯器已不支持,因此推薦使用<iostream>+std的方式。
#include <iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
// 可以自動識別變量的類型
cin >> a;
cin >> b >> c;
cout << a << endl;
cout << b << " " << c << endl;
return 0;
}
// ps:關于cout和cin還有很多更復雜的用法,比如控制浮點數輸出精度,控制整形輸出進制格式等
// 等。因為C++兼容C語言的用法,這些又用得不是很多,我們這里就不展開學習了。
std命名空間的使用慣例:
std是C++標準庫的命名空間,如何展開std使用更合理呢?
- 在日常練習中,建議直接using namespace std即可,這樣就很方便。
- using namespace std展開,標準庫就全部暴露出來了,如果我們定義跟庫重名的類型/對象/函數,就存在沖突問題。該問題在日常練習中很少出現,但是項目開發(fā)中代碼較多、規(guī)模大,就很容易出現。所以建議在項目開發(fā)中使用,像std::cout這樣使用時指定命名空間 + using std::cout展開常用的庫對象/類型等方式。
4. 缺省參數
4.1 缺省參數概念
缺省參數是聲明或定義函數時為函數的參數指定一個缺省值。在調用該函數時,如果沒有指定實參則采用該形參的缺省值,否則使用指定的實參。
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func(); // 沒有傳參時,使用參數的默認值
Func(10); // 傳參時,使用指定的實參
return 0;
}
4.2 缺省參數分類
- 全缺省參數
void Func(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
- 半缺省參數
void Func(int a, int b = 10, int c = 20)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
注意:文章來源:http://www.zghlxwxcb.cn/news/detail-739741.html
????????1.?半缺省參數必須從右往左依次來給出,不能間隔著給
? ? ? ? 2.?缺省參數不能在函數聲明和定義中同時出現
// test.h
void Func(int a = 10);
// test.cpp
void Func(int a = 20)
{}
// 注意:如果生命與定義位置同時出現,恰巧兩個位置提供的值不同,那編譯器就無法確定到底該
// 用那個缺省值。
? ? ? ? 3. 缺省值必須是常量或者全局變量
? ? ? ? 4. C語言不支持(編譯器不支持)
5. 函數重載
自然語言中,一個詞可以有多重含義,人們可以通過上下文來判斷該詞真實的含義,即該詞被重載了。
比如:以前有一個笑話,國有兩個體育項目大家根本不用看,也不用擔心。一個是乒乓球,一個
是男足。前者是“誰也贏不了!”,后者是“誰也贏不了!”
5.1 函數重載概念
函數重載:是函數的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數,這些同名函數的形參列表(參數個數 或 類型 或 類型順序)不同,常用來處理實現功能類似數據類型不同的問題。
#include<iostream>
using namespace std;
// 1、參數類型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
// 2、參數個數不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
// 3、參數類型順序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
return 0;
}
5.2 C++支持函數重載的原理--名字修飾(name Mangling)
為什么C++支持函數重載,而C語言不支持函數重載呢?
在C/C++中,一個程序要運行起來,需要經歷以下幾個階段:預處理、編譯、匯編、鏈接。
????????1. 實際項目通常是由多個頭文件和多個源文件構成,而通過C語言階段學習的編譯鏈接,我們可以知道,【當前a.cpp中調用了b.cpp中定義的Add函數時】,編譯后鏈接前,a.o的目標文件中沒有Add的函數地址,因為Add是在b.cpp中定義的,所以Add的地址在b.o中。那么怎么辦呢?
????????2.?所以鏈接階段就是專門處理這種問題,鏈接器看到a.o調用Add,但是沒有Add的地址,就會到b.o的符號表中找Add的地址,然后鏈接到一起。
????????3.?那么鏈接時,面對Add函數,鏈接接器會使用哪個名字去找呢?這里每個編譯器都有自己的函數名修飾規(guī)則。
????????4.?由于Windows下vs的修飾規(guī)則過于復雜,而Linux下g++的修飾規(guī)則簡單易懂,下面我們使用了g++演示了這個修飾后的名字。
????????5.?通過下面我們可以看出gcc的函數修飾后名字不變。而g++的函數修飾后變成【_Z+函數長度+函數名+類型首字母】。
- 采用C語言編譯器編譯后結果
結論:在linux下,采用gcc編譯完成后,函數名字的修飾沒有發(fā)生改變。
- 采用C++編譯器編譯后結果
結論:在linux下,采用g++編譯完成后,函數名字的修飾發(fā)生改變,編譯器將函數參數類型信息添加到修改后的名字中。
- Windows下名字修飾規(guī)則
對比Linux會發(fā)現,windows下vs編譯器對函數名字修飾規(guī)則相對復雜難懂,但道理都是類似的,這里就不深究了。
????????6.?通過這里就理解了C語言沒辦法支持重載,因為同名函數沒辦法區(qū)分。而C++是通過函數修飾規(guī)則來區(qū)分,只要參數不同,修飾出來的名字就不一樣,就支持了重載。
????????7.?如果兩個函數函數名和參數是一樣的,返回值不同是不構成重載的,因為調用時編譯器沒辦法區(qū)分。
6. 引用
6.1 引用概念
引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會為引用變量開辟內存空間,它和它引用的變量共用同一塊內存空間。
比如:李逵,在家稱為"鐵牛",江湖上人稱"黑旋風"。
類型& 引用變量名(對象名) = 引用實體;
void TestRef()
{
int a = 10;
int& ra = a;// <==== 定義引用類型
printf("%p\n", &a);
printf("%p\n", &ra);
// a 和 ra 的地址是一樣的
}
注意:引用類型必須和引用實體是同種類型的
6.2 引用特性
- 引用在定義時必須初始化
- 一個變量可以有多個引用
- 引用一旦引用一個實體,再不能引用其他實體
void TestRef()
{
int a = 10;
// int& ra; // 該條語句編譯時會出錯
int& ra = a;
int& rra = a;
printf("%p %p %p\n", &a, &ra, &rra);
}
6.3 常引用
void TestConstRef()
{
const int a = 10;
// int& ra = a; // 該語句編譯時會出錯,a為常量
const int& ra = a;
// int& b = 10; // 該語句編譯時會出錯,b為常量
const int& b = 10;
double d = 12.34;
// const int& rd = d; // 該語句編譯時會出錯,類型不同
const double& rd = d;
}
6.4 使用場景
1. 做參數
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
2. 做返回值
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
下面代碼輸出什么結果?為什么?
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :" << ret << endl;
return 0;
}
注意:如果函數返回時,出了函數作用域,如果返回對象還在(還沒還給系統(tǒng)),則可以使用引用返回,如果已經還給系統(tǒng)了,則必須使用傳值返回。
6.5 傳值、傳引用效率比較
????????以值作為參數或者返回值類型,在傳參和返回期間,函數不會直接傳遞實參或者將變量本身直接返回,而是傳遞實參或者返回變量的一份臨時的拷貝,因此用值作為參數或者返回值類型,效率是非常低下的,尤其是當參數或者返回值類型非常大時,效率就更低。
6.5.1 值和引用作為函數參數的性能比較
#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
A a;
// 以值作為函數參數
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作為函數參數
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分別計算兩個函數運行結束后的時間
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
6.5.2 值和引用作為返回值類型的性能比較
#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{
// 以值作為函數的返回值類型
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作為函數的返回值類型
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2();
size_t end2 = clock();
// 計算兩個函數運算完成之后的時間
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
通過上述代碼的比較,發(fā)現傳值和指針在作為傳參以及返回值類型上效率相差很大。
6.6 引用和指針的區(qū)別
在語法概念上引用就是一個別名,沒有獨立空間,和其引用實體共用同一塊空間。
int main()
{
int a = 10;
int& ra = a;
cout << "&a = " << &a << endl;
cout << "&ra = " << &ra << endl;
return 0;
}
在底層實現上實際是有空間的,因為引用是按照指針方式來實現的。
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}
我們來看下引用和指針的匯編代碼對比
引用和指針的不同點:
- 引用概念上定義一個變量的別名,指針存儲一個變量地址。
- 引用在定義時必須初始化,指針沒有要求
- 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實體
- 沒有NULL引用,但有NULL指針
- 在sizeof中含義不同:引用結果為引用類型的大小,但指針始終是地址空間所占字節(jié)個數(32位平臺下占4個字節(jié))
- 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小
- 有多級指針,但是沒有多級引用
- 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理
- 引用比指針使用起來相對更安全
7. 內聯函數
7.1 概念
以inline修飾的函數叫做內聯函數,編譯時C++編譯器會在調用內聯函數的地方展開,沒有函數調用建立棧幀的開銷,內聯函數提升程序運行的效率。
如果在上述函數前增加inline關鍵字將其改成內聯函數,在編譯期間編譯器會用函數體替換函數的調用。
查看方式:
- 在release模式下,查看編譯器生成的匯編代碼中是否存在call Add
- 在debug模式下,需要對編譯器進行設置,否則不會展開(因為debug模式下,編譯器默認不會對代碼進行優(yōu)化,以下給出vs2013的設置方式)
7.2 特性
????????1. inline是一種以空間換時間的做法,如果編譯器將函數當成內聯函數處理,在編譯階段,會用函數體替換函數調用,缺陷:可能會使目標文件變大,優(yōu)勢:少了調用開銷,提高程序運行效率。
????????2.?inline對于編譯器而言只是一個建議,不同編譯器關于inline實現機制可能不同,一般建議:將函數規(guī)模較小(即函數不是很長,具體沒有準確的說法,取決于編譯器內部實現)、不是遞歸、且頻繁調用的函數采用inline修飾,否則編譯器會忽略inline特性。下圖為《C++prime》第五版關于inline的建議:
????????3. inline不建議聲明和定義分離,分離會導致鏈接錯誤。因為inline被展開,就沒有函數地址
了,鏈接就會找不到。
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
// 鏈接錯誤:main.obj : error LNK2019: 無法解析的外部符號 "void __cdecl
f(int)" (?f@@YAXH@Z),該符號在函數 _main 中被引用
8. auto關鍵字(C++11)
8.1 類型別名思考
隨著程序越來越復雜,程序中用到的類型也越來越復雜,經常體現在:
- 類型難于拼寫
- 含義不明確導致容易出錯
#include <string>
#include <map>
int main()
{
std::map<std::string, std::string> m{ { "apple", "蘋果" }, { "orange",
"橙子" }, { "pear","梨" } };
std::map<std::string, std::string>::iterator it = m.begin();
while (it != m.end())
{
//....
}
return 0;
}
std::map<std::string, std::string>::iterator 是一個類型,但是該類型太長了,特別容易寫錯。聰明的同學可能已經想到:可以通過typedef給類型取別名,比如:
#include <string>
#include <map>
typedef std::map<std::string, std::string> Map;
int main()
{
Map m{ { "apple", "蘋果" },{ "orange", "橙子" }, {"pear","梨"} };
Map::iterator it = m.begin();
while (it != m.end())
{
//....
}
return 0;
}
使用typedef給類型取別名確實可以簡化代碼,但是typedef有會遇到新的難題:
typedef char* pstring;
int main()
{
const pstring p1; // 編譯成功還是失?。? const pstring* p2; // 編譯成功還是失???
return 0;
}
在編程時,常常需要把表達式的值賦值給變量,這就要求在聲明變量的時候清楚地知道表達式的類型。然而有時候要做到這點并非那么容易,因此C++11給auto賦予了新的含義。
8.2 auto簡介
在早期C/C++中auto的含義是:使用auto修飾的變量,是具有自動存儲器的局部變量,但遺憾的是一直沒有人去使用它,大家可思考下為什么?
C++11中,標準委員會賦予了auto全新的含義即:auto不再是一個存儲類型指示符,而是作為一個新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時期推導而得。
int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
//auto e; 無法通過編譯,使用auto定義變量時必須對其進行初始化
return 0;
}
【注意】
使用auto定義變量時必須對其進行初始化,在編譯階段編譯器需要根據初始化表達式來推導auto的實際類型。因此auto并非是一種“類型”的聲明,而是一個類型聲明時的“占位符”,編譯器在編譯期會將auto替換為變量實際的類型。
8.3 auto的使用細則
????????1. auto與指針和引用結合起來使用
用auto聲明指針類型時,用auto和auto*沒有任何區(qū)別,但用auto聲明引用類型時則必須加&
int main()
{
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
*a = 20;
*b = 30;
c = 40;
return 0;
}
????????2. 在同一行定義多個變量
當在同一行聲明多個變量時,這些變量必須是相同的類型,否則編譯器將會報錯,因為編譯器實際只對第一個類型進行推導,然后用推導出來的類型定義其他變量。
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 該行代碼會編譯失敗,因為c和d的初始化表達式類型不同
}
8.4?auto不能推導的場景
? ? ? ? 1. auto不能作為函數的參數
// 此處代碼編譯失敗,auto不能作為形參類型,因為編譯器無法對a的實際類型進行推導
void TestAuto(auto a)
{}
? ? ? ? 2. auto不能直接用來聲明數組
void TestAuto()
{
int a[] = { 1,2,3 };
auto b[] = { 4,5,6 };
}
????????3.?為了避免與C++98中的auto發(fā)生混淆,C++11只保留了auto作為類型指示符的用法
????????4. auto在實際中最常見的優(yōu)勢用法就是跟下面會講到的C++11提供的新式for循環(huán),還有l(wèi)ambda表達式等進行配合使用。
9. 基于范圍的for循環(huán)(C++11)
9.1 范圍for的語法
在C++98中如果要遍歷一個數組,可以按照以下方式進行:
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
array[i] *= 2;
for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)
cout << *p << endl;
}
對于一個有范圍的集合而言,由程序員來說明循環(huán)的范圍是多余的,有時候還會容易犯錯誤。因此C++11中引入了基于范圍的for循環(huán)。for循環(huán)后的括號由冒號“ :”分為兩部分:第一部分是范圍內用于迭代的變量,第二部分則表示被迭代的范圍。
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for (auto& e : array)
e *= 2;
for (auto e : array)
cout << e << " ";
}
注意:與普通循環(huán)類似,可以用continue來結束本次循環(huán),也可以用break來跳出整個循環(huán)。
9.2 范圍for的使用條件
????????1.?for循環(huán)迭代的范圍必須是確定的
對于數組而言,就是數組中第一個元素和最后一個元素的范圍;對于類而言,應該提供begin和end的方法,begin和end就是for循環(huán)迭代的范圍。
注意:以下代碼就有問題,因為for的范圍不確定
void TestFor(int array[])
{
for (auto& e : array)
cout << e << endl;
}
? ? ? ? 2. 迭代的對象要實現++和==的操作。(關于迭代器這個問題,以后會講,現在提一下,沒辦法講清楚,現在大家了解一下就可以了)
10. 指針空值nullptr(C++11)
10.1 C++98中的指針空值
在良好的C/C++編程習慣中,聲明一個變量時最好給該變量一個合適的初始值,否則可能會出現不可預料的錯誤,比如未初始化的指針。如果一個指針沒有合法的指向,我們基本都是按照如下方式對其進行初始化:
void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}
NULL實際是一個宏,在傳統(tǒng)的C頭文件(stddef.h)中,可以看到如下代碼:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看到,NULL可能被定義為字面常量0,或者被定義為無類型指針(void*)的常量。不論采取何種定義,在使用空值的指針時,都不可避免的會遇到一些麻煩,比如:
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
程序本意是想通過f(NULL)調用指針版本的f(int*)函數,但是由于NULL被定義成0,因此與程序的初衷相悖。
在C++98中,字面常量0既可以是一個整形數字,也可以是無類型的指針(void*)常量,但是編譯器默認情況下將其看成是一個整形常量,如果要將其按照指針方式來使用,必須對其進行強轉(void*)0。
注意:
- 在使用nullptr表示指針空值時,不需要包含頭文件,因為nullptr是C++11作為新關鍵字引入的。
- 在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所占的字節(jié)數相同。
- 為了提高代碼的健壯性,在后續(xù)表示指針空值時建議最好使用nullptr。
本文完文章來源地址http://www.zghlxwxcb.cn/news/detail-739741.html
到了這里,關于【C++】main開始的地方的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!