第6节 经典问题解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pt_raspi_fresher/article/details/88319778

-------------------------------------资源来源于网络,仅供自学使用,如有侵权,联系我必删.

第一:

const 和引用的疑惑

#include <stdio.h>

int main()
{
    const int x = 1;//进入符号表的常量
    const int& rx = x;//引用,rx是x的别名
    
    int& nrx  = const_cast<int&>(rx);//nrx强制转换为普通变量
    
    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;//此处的y是只读变量
    int* p = NULL;

    p = const_cast<int*>(&y);//p指向y的地址
    *p = 6;

    printf("y = %d\n", y);
    printf("*p = %d\n", *p);
    
    const int z = y;

    p = const_cast<int*>(&z);//z是只读变量
    *p = 7;

    printf("z = %d\n", z);
    printf("*p = %d\n", *p);

    char c = 'c';//字符型变量c
    char& rc = c;//引用rc
    //const 引用的类型与初始化变量的类型  不同
    const int& trc = c;//char c = 'c'
    
    rc = 'a';
    
    printf("c = %c\n", c);
    printf("rc = %c\n", rc);
    printf("trc = %c\n", trc);
    
    return 0;
}

什么是符号表
符号表存储在程序中的哪个地方?
  符号表是编译器在编译过程中产生的关于源程序中语法符号的数据结构
      如常量表、变量名表、数组名表、函数名表等等
  符号表是编译器自用的内部数据结构
  符号表不会进入最终产生的可执行程序中

  只有用字面量初始化的 const 常量才会进入符号表
     对 const 常量进行引用会导致编译器为其分配空间
     虽然 const 常量被分配了空间,但是这个空间中的值不会被使用
     使用其它变量初始化的 const 常量仍然是只读变量

  被 volatile 修饰的 const 常量不会进入符号表
     退化为只读变量,每次访问都从内存中取值

  const 引用的类型与初始化变量的类型
  相同:使初始化变量成为只读变量
  不同:生成一个新的只读变量,其初始值与初始化变量相同
在编译期间不能直接确定初始值的 const 量,都被作为只读变量处理

扫描二维码关注公众号,回复: 5473686 查看本文章

第二:

引用与指针的疑惑

#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;
}

  指针与引用的区别
  指针是一个变量,其值为一个内存地址,通过指针可以访问对应内存地址中的值
  引用只是一个变量的新名字,所有对引用的操作(赋值,取地址等)都会传递到其引用的变量上
  指针可以被 const 修饰成为常量或者只读变量
  const 引用使其引用的变量具有只读属性
  指针就是变量,不需要初始化,也可以指向不同的地址
  引用天生就必须在定义时初始化,之后无法在引用其它变量

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

  从使用 C++ 语言的角度来看
  引用与指针常量没有任何的关系
  引用是变量的新名字,操作引用就是操作对应的变量
  从 C++ 编译器的角度来看
  为了支持新概念 “ 引用 ” 必须要一个有效的解决方案
  在编译器内部,使用指针常量来实现 “ 引用 ”
  因此 “ 引用 ”在定义时必须初始化

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

答案:
 当进行 C++ 编程时,直接站在使用的角度看待引用,与指针毫无关系!
 当对 C++ 程序中的一些涉及引用的 bug 或者 “ 奇怪行为 ” 进行分析时,可以考虑站在 C++ 编译器的角
度看待引用!

第三:

重载的疑惑

#include <stdio.h>

void func(int a, int b)     //1
{
    printf("void func(int a, int b)\n");
}

void func(int a, char b)    //2
{
    printf("void func(int a, char b)\n");
}

void func(char a, int b)    //3
{
    printf("void func(char a, int b)\n");
}

void func(char a, char b)    //4
{
    printf("void func(char a, char b)\n");
}

int main()
{
    int a = 1;
    char b = '2';
    
    func(a, a);//1
    func(a, b);//2
    func(b, a);//3
    func(b, b);//4
    
    func(1, 2);//实参是字面量,编译器对字面量进行默认类型转换
    func(1 '2');
    func('1', 2);
    func('1', '2');
    
    return 0;
}

  C++ 编译器对字面量的处理方式
     整数型字面量的默认类型为 int ,占用4 个字节
     浮点型字面量的默认类型为 double ,占用8 个字节
     字符型字面量的默认类型为 char ,占用1 个字节
     字符串型字面量的默认类型为 const char* * ,占用4 个字节


  当使用字面量对变量进行初始化或赋值时
     无溢出产生:编译器对字面量进行默认类型转换
     产生溢出:编译器会做截断操作,并产生警告

#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;
    
    return 0;
}

  深入理解重载规则
     精确匹配实参
     通过默认类型转换匹配实参
     通过默认参数匹配实参
  三条规则会同时对已存在的重载函数进行挑选
     当实参为变量并能够精确匹配形参时,不再进行默认类型转换的尝试。
     当实参为字面量时,编译器会同时进行精确匹配和默认类型转换的尝试。

第四:

#include <stdio.h>

//extern “C” 中不允许重载函数!!!!
extern "C"
{
    
    void func(int x)//1
    {
        const int i = 1;
        int& ri = const_cast<int&>(i);//C++语法
        
        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);//调用1函数
    func("Delphi Tang");
    func(1, 2);
    
    return 0;
}

  深入理解extern “C”
     extern “C” 告诉编 C++ 译器将其中的代码进行C 方式的编译
         C  方式的编译主要指按照C  语言的规则对函数名进行编译
           函数名经过编译后可能与源码中的名字有所不同
           C++ 编译器为了支持重载,函数名经过编译后会加上参数信息,因而编译后的函数名与源码中完全不同
           C 编译器不会在编译后的函数名中加上参数信息


extern “C” 中的重载函数经过C 方式编译后将得到相同的函数名,因此extern “C” 中不允许重载函数,但extern “C” 中的函数可以与extern “C” 之外的函数进行重载。

猜你喜欢

转载自blog.csdn.net/pt_raspi_fresher/article/details/88319778