專題一經典問題解析-7
一。const和引用的疑惑
#include <stdio.h> int main() { const int x = 1; const int& rx = x; int& nrx = const_cast<int&>(rx); nrx = 5; printf("x = %d\n", x); printf("rx = %d\n", rx); printf("nrx = %d\n", nrx); printf("x = %p\n", &x); printf("rx = %p\n", &rx); printf("nrx = %p\n", &nrx); volatile const int y = 2; //不是真正意義上變量,只是只讀常量 int* p = NULL; p = const_cast<int*>(&y); *p = 6; printf("y = %d\n", y); printf("*p = %d\n", *p); const int z = y; p = const_cast<int*>(&z);*p = 7; printf("z = %d\n", z); printf("*p = %d\n", *p); char c = ‘c‘; char& rc = c; const int& trc = c; //類型不同轉化的是只讀常量,如果是char&則結果輸出是a rc = ‘a‘; printf("c = %c\n", c); printf("rc = %c\n", rc); printf("trc = %c\n", trc); return 0; }
輸出結果
2.什麽是符號表?
#符號表是編譯器在編譯過程中產生的關於源程序中語法符號的數據結構。
#如常量表、變量名表、數組名表、函數名表。
#符號表是編譯器自用的內部數據結構
#符號表不會進入最終產生的可執行程序中。
3.上面程序疑惑解答
a。只有用字面量初始化const才會進入符號表。
#對const常量引用會導致編譯器為其分配空間
#雖然const常量被分配了空間,但是這個空間中的值不會被使用
#使用其他變量初始化的const常量仍然是只讀變量(其他變量變了,它就得變,上例的z)。
b。被volatile修飾的const常量不會進入符號表
#退化為只讀變量,每次訪問都從內存中取值(上例的y和*p)
c。const引用的類型與初始化變量的類型
如果相同:使初始化變量成為只讀變量
如果不同:生成一個新的只讀變量,其初始值與初始化變量相同
二。引用與指針的疑惑
1.代碼
#include <stdio.h> struct SV { int x; int y; int z; }; struct SR { int& x; int& y; int& z; }; int main() { SV sv = {1, 2, 3}; SR sr = {sv.x, sv.y, sv.z}; printf("&sv = %p\n", &sv); printf("&sv.x = %p\n", &sv.x); printf("&sv.y = %p\n", &sv.y); printf("&sv.z = %p\n", &sv.z); printf("&sr = %p\n", &sr); printf("&sr.x = %p\n", &sr.x); printf("&sr.y = %p\n", &sr.y); printf("&sr.z = %p\n", &sr.z); SV& rsv = sv; rsv.x = 4; rsv.y = 5; rsv.z = 6; printf("sv.x = %d\n", sv.x); printf("sv.y = %d\n", sv.y); printf("sv.z = %d\n", sv.z); return 0; }
輸出結果
2。疑惑答疑
a。指針是一個變量,其值為一個內存地址,通過指針可以訪問對應內存地址中的值
. b。引用就是變量的一個新名字,所有對引用的操作(賦值,取地址)都會傳遞到引用的變量上面。
c。指針可以被const修飾成為常量或者只讀變量。
d。const引用使其引用的變量具有只讀屬性。
e。指針就是變量,不需要初始化,也可以指向不同的地址。
d。引用天生就是在定義的時候初始化,之後便無法在引用其他變量。
3.如何理解“引用的本質就是指針常量”
a。從使用C++語言的角度來看
#引用和指針常量沒什麽關系。
#引用是變量的新名字,操作引用就是操作對應的變量
b。從C++編譯器的角度來看
#為了支持新概念“引用”必須要一個有效的解決方案
#在編譯器內部,使用指針變量來實現“引用”
#因此“引用”在定義時必須初始化
三。重載的疑惑
1.代碼
#include <stdio.h> void func(int a, int b) { printf("void func(int a, int b)\n"); } void func(int a, char b) { printf("void func(int a, char b)\n"); } void func(char a, int b) { printf("void func(char a, int b)\n"); } void func(char a, char b) { printf("void func(char a, char b)\n"); } int main() { int a = 1; char b = ‘2‘; func(a, a); func(a, b); func(b, a); func(b, b); func(1, 2); func(1 ‘2‘); func(‘1‘, 2); func(‘1‘, ‘2‘); return 0; }
2.深入理解重載規則
# 精確匹配實參
# 通過默認類型轉換匹配實參
#通過默認參數匹配實參
三條規則會同時對已存在的重載函數進行挑選
# 當實參為變量並能夠精確匹配形參時,不再進行默認類型轉換的嘗試
#當實參為字面量時,編譯器會同時進行精確匹配和默認類型轉換的嘗試
四.C方式編譯的疑惑
#include <stdio.h> extern "C" { void func(int x) { const int i = 1; int& ri = const_cast<int&>(i); ri = 5; printf("i = %d\n", i); printf("ri = %d\n", ri); } } void func(const char* s) { printf("%s\n", s); } int func(int a, int b) { return a + b; } int main() { func(1); func("Delphi Tang"); func(1, 2); printf("Press any key to continue..."); getchar(); return 0; }
2.深入理解extern“C”
#extern“C”告訴C++編譯器將其中的代碼進行C方式的編譯
a。C方式的編譯主要按照C語言的剛剛規則對函數名進行編譯。
#函數名經過編譯後可能與源碼中的名字有所不同
#C++編譯器為了支持重載,函數名經過編譯後會加上參數信息,因此編譯後的函數名與源碼中完全不同。
#C編譯器不會在編譯後的函數名中加上參數信息。
b。extern“C”中的重載函數經過C方式編譯後將得到相同的函數名,因此extern“C”中不允許函數重載,但extern“C”中的函數可以與extern“C”之外的函數進行重載。
專題一經典問題解析-7