5. C++第一節到第四節總結

一、符號表

什麼是符號表?符號表存儲在程序中的哪個地方?

                1. 符號表是編譯器在編譯過程中產生的關於源程序中語法符號的數據結構;
                2. 如常量表、變量名錶、數組名錶、函數名錶等等;
                3. 符號表是編譯器自用的內部數據結構;
                4. 符號表不會進入最終產生的可執行程序中;

 

二、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;
    
    rc = 'a';
    
    printf("c = %c\n", c);
    printf("rc = %c\n", rc);
    printf("trc = %c\n", trc);
    getchar();
    return 0;
}

編譯運行結果如下:

總結:

只有用字面量初始化的const常量纔會進入符號表

              1. 對const常量進行引用會導致編譯器爲其分配空間;
              2. 雖然const常量被分配了空間,但是這個空間中的值不會被使用;
              3. 使用其它變量初始化的const常量仍然是隻讀變量;

被volatile修飾的const常量不會進入符號表
              1. 退化爲只讀變量,每次訪問都從內存中取值;

const引用的類型與初始化變量的類型是否相同
               相同:使初始化變量成爲只讀變量;
               不同:生成一個新的只讀變量,其初始值與初始化變量相同 ;       

注:在編譯期間不能直接確定初始值的const量,都被作爲只讀變量處理。

 

三、引用與指針的疑惑

#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);
    getchar();

    return 0;
}

編譯運行結果如下:

總結:

指針與引用的區別
               1. 指針是一個變量,其值爲一個內存地址,通過指針可以訪問對應內存地址中的值;
               2. 引用只是一個變量的新名字,所有對引用的操作(賦值,取地址等)都會傳遞到其引用的變量上;
               3. 指針可以被const修飾成爲常量或者只讀變量;
               4. const引用使其引用的變量具有隻讀屬性;
               5. 指針就是變量,不需要初始化,也可以指向不同的地址;
               6. 引用天生就必須在定義時初始化,之後無法在引用其它變量;

 

      如何理解“引用的本質就是指針常量”?

            從使用C++語言的角度來看

                1. 引用與指針常量沒有任何的關係

                2. 引用是變量的新名字,操作引用就是操作對應的變量

           從C++編譯器的角度來看

                1. 爲了支持新概念“引用”必須要一個有效的解決方案

                2. 在編譯器內部,使用指針常量來實現“引用”

                3. 因此“引用”在定義時必須初始化

 

         當進行C++編程時,直接站在使用的角度看待引用,與指針毫無關係!
         當對C++程序中的一些涉及引用的bug或者“奇怪行爲”進行分析時,可以考慮站在C++編譯器的角度看待引用!

 

四、重載的疑惑

#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');
    getchar();
    
    return 0;
}

編譯運行結果如下:

#include <stdio.h>

int main()
{
    printf("sizeof(\'1\') = %d\n", sizeof('1'));
    printf("sizeof(2) = %d\n", sizeof(2));
    printf("sizeof(3.0) = %d\n", sizeof(3.0));
    
    char c = '1';
    short s = '1';
    int i = '1';
    long l = '1';
    long long ll = '1';
    
    c = 2;
    s = 2;
    i = 2;
    l = 2;
    ll = 2;
    
    float f = 0;
    double d = 0;
    
    f = 3.0;
    d = 3.0;
    getchar();

    return 0;
}

編譯運行結果如下:

總結:

         C++編譯器對字面量的處理方式:
                1. 整數型字面量的默認類型爲int,佔用4個字節;
                2. 浮點型字面量的默認類型爲double,佔用8個字節;
                3. 字符型字面量的默認類型爲char,佔用1個字節;
                4. 字符串型字面量的默認類型爲const char*,佔用4個字節;

        當使用字面量對變量進行初始化或賦值時:
                無溢出產生:編譯器對字面量進行默認類型轉換
                產生溢出:編譯器會做截斷操作,併產生警告

         深入理解重載規則
                1. 精確匹配實參
                2. 通過默認類型轉換匹配實參
                3. 通過默認參數匹配實參

         三條規則會同時對已存在的重載函數進行挑選
                1. 當實參爲變量並能夠精確匹配形參時,不再進行默認類型轉換的嘗試。
                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);
    
    getchar();

    return 0;
}

編譯運行結果如下所示:

總結:

extern “C”告訴編C++譯器將其中的代碼進行C方式的編譯。C方式的編譯主要指按照C語言的規則對函數名進行編譯。函數名經過編譯後可能與源碼中的名字有所不同。C++編譯器爲了支持重載,函數名經過編譯後會加上參數信息,因而編譯後的函數名與源碼中完全不同。C編譯器不會在編譯後的函數名中加上參數信息。

extern “C”中的重載函數經過C方式編譯後將得到相同的函數名,因此extern “C”中不允許重載函數,但extern “C”中的函數可以與extern “C”之外的函數進行重載。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章