46. 41-45经典问题解析

const和引用的疑惑

什么是符号表?符号表存储在程序中的哪个地方?

符号表是编译器在编译过程中产生的关于源程序中语法符号的数据结构

    • 如常量表、变量名表、数组名表、函数名表等等

符号表是 编译器 自用的 内部数据结构

符号表不会进入 最终产生的 可执行程序 中

(只与编译器相关,与最后的程序无关)

只有 用字面量 初始化的 const常量 才会进入符号表

const int x = 1;//字面量(1) 初始化的const常量 //进入符号表真正意义上的常量

回顾:

  • 对const常量进行引用会导致编译器为其分配空间

  • 虽然const常量被分配了空间,但是这个空间中的值不会被使用

  • 使用其它变量(y)初始化的const常量(z)仍然是只读变量

被volatile修饰的const常量不会进入符号表

  • 退化为只读变量,每次访问都从内存中取值

const引用的类型与初始化变量的类型

  • (引用类型 == 初始化变量类型)相同:使初始化变量成为只读变量

  • (引用类型 != 初始化变量类型)不同:(用不同类型的变量去给常引用初始化)生成一个新的只读变量,其初始值与初始化变量相同

小结

//用字面量初始化的const常量才会 进入符号表
const int x = 1;//进入符号表

//被volatile修饰的const常量 不会进入符号表
volatile const int y = 2; //tui退化为只读变量

//使用其它变量(类型相同)初始化的const常量仍然是只读变量
const int z = y;//只读变量

//const引用的类型与初始化变量的类型不同
char c = 'c';
const int& trc = c;//生成新的只读变量

在编译期间不能直接确定初始值的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);//结构体sv的地址
    printf("&sv.x = %p\n", &sv.x);//&sv与&sv.x一样
    printf("&sv.y = %p\n", &sv.y);//&sv.x+4
    printf("&sv.z = %p\n", &sv.z);//&sv.x+8
    
    printf("&sr = %p\n", &sr);//结构体sr的地址

    printf("&sr.x = %p\n", &sr.x);//引用,与&sv.x一致
    printf("&sr.y = %p\n", &sr.y);//引用,与&sv.y一致
    printf("&sr.z = %p\n", &sr.z);//引用,与&sv.z一致
    
    SV& rsv = sv;//给sv取别名rsv
    
    rsv.x = 4;
    rsv.y = 5;
    rsv.z = 6;
    
    printf("sv.x = %d\n", sv.x);//4
    printf("sv.y = %d\n", sv.y);//5
    printf("sv.z = %d\n", sv.z);//6
    
    return 0;
}
result:
&sv = 000000000061FE0C
&sv.x = 000000000061FE0C
&sv.y = 000000000061FE10
&sv.z = 000000000061FE14
&sr = 000000000061FDF0
&sr.x = 000000000061FE0C
&sr.y = 000000000061FE10
&sr.z = 000000000061FE14
sv.x = 4
sv.y = 5
sv.z = 6

指针与引用的区别

  • 指针是一个变量,其值为一个内存地址,通过指针可以访问对应内存地址中的值

  • 引用只是一个变量的新名字,所有对引用的操作(赋值,取地址等)都会传递到其引用的变量上

  • 指针可以被const修饰成为常量或者只读变量

  • const引用使其引用的变量具有只读属性

  • 指针就是变量,不需要初始化,也可以指向不同的地址

  • 引用天生就必须在定义时初始化,之后无法再引用其它变量

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

  • 从使用C++语言的角度来看

    • 引用与指针常量没有任何的关系

    • 引用是变量的新名字,操作引用就是操作对应的变量

  • 从C++编译器的角度来看

    • 为了支持新概念“引用”必须要一个有效的解决方案

    • 在编译器内部,使用指针常量来实现“引用”

    • 因此“引用”在定义时必须初始化(定义一个常量必须初始化)

当进行C++编程时,直接站在使用的角度看待引用,与指针毫无关系!

当对C++程序中的一些涉及引用的bug或者“奇怪行为”进行分析时,可以考虑站在C++编译器的角度看待引用!

重载的疑惑

C++编译器对字面量的处理方式

  • 整数型字面量的默认类型为int,占用4个字节

  • 浮点型字面量的默认类型为double,占用8个字节

  • 字符型字面量的默认类型为char,占用1个字节

  • 字符串型字面量的默认类型为const char*,占用4个字节

    //字面量‘1’的默认类型是char
printf("sizeof(\'1\') = %d\n", sizeof('1'));
    //字面量 2 的默认类型是int
printf("sizeof(2) = %d\n", sizeof(2));
    //字面量 3.0 的默认类型是double
printf("sizeof(3.0) = %d\n", sizeof(3.0));

result:
sizeof('1') = 1
sizeof(2) = 4
sizeof(3.0) = 8

当使用字面量对变量进行初始化或赋值时

  • 无溢出产生:编译器对字面量进行默认类型转换

  • 产生溢出:编译器会做截断操作,并产生警告

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;//double(8bit)向float(4bit)赋值,截断??
    d = 3.0;

深入理解重载规则

  • 精确匹配实参

  • 通过默认类型转换匹配实参

  • 通过默认参数匹配实参

上述三条规则会同时对已存在的重载函数进行挑选

  • 当实参为变量并能够精确匹配形参时,不再进行默认类型转换的尝试。

  • 当实参为字面量时,编译器会同时进行精确匹配和默认类型转换的尝试。

C方式编译的疑惑

深入理解extern “C”

extern “C”告诉编C++译器将其中的代码进行C方式的编译

  • C方式的编译主要指按照C语言的规则对函数名进行编译

    • 函数名经过编译后可能与源码中的名字有所不同

    • C++编译器为了支持重载,函数名经过编译后会加上参数信息,因而编译后的函数名与源码中完全不同

    • C编译器不会在编译后的函数名中加上参数信息

extern “C”中的重载函数经过C方式编译后将得到相同的函数名,因此extern “C”中不允许重载函数,但extern “C”中的函数可以与extern “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);//1
        printf("ri = %d\n", ri);//5
    }
    //还是c++的编译方式?不是声明了C方式编译?
    //函数名是C的方式编译了,并不影响函数体是C++的编译方式
}

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");
    printf("func(1,2) = %d\n",func(1,2));
    
    return 0;
}
result:
i = 1
ri = 5
Delphi Tang
func(1,2) = 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值