专题一经典问题解析

const和引用的疑惑

1.对真正意义上的常量 取引用

#include <stdio.h> 

int main()
{
	const int x = 1;
	int& rx = const_cast<int&>(x); //取引用 相当于 取地址 会给x分配内存空间
    							   //rx 是 x内存空间 的别名
	
	rx = 5;
	
	printf("x = %d\n", x); //符号表
	printf("rx = %d\n", rx);
	printf("&x = %p\n", &x);
	printf("&rx = %p\n", &rx); //操作引用就是操作变量
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPxqLoCV-1583635322437)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1581843005751.png)]

2.volatile与const结合

volatie会使编译器不做任何优化,每次都从内存中取值

#include <stdio.h> 

int main()
{	
	volatile const int y = 2; //volatie会使编译器不做任何优化,每次都从内存中取值
							  //y变成了一个只读变量 
	
	int* p = NULL;	
	p = const_cast<int*>(&y);
	
	*p = 6;
	
	printf("y = %d\n", y); 
	printf("y = %d\n", *p);		
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j1csy1HL-1583635322446)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1581853342393.png)]

3.int y = 0; const int z = y; z是一个只读变量

int main()
{
    volatile const int y = 2;
    const int z = y; //z是一个只读变量 ,不是真正意义上的常量 
    p = const_cast<int*>(&z);
    *p = 7;
    printf("z = %d\n", z);
    printf("*p = %d\n", *p);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ERzbnOxa-1583635322450)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583489630919.png)]

4.用char变量给int引用初始化,将得到一个新的只读变量

#include <iostream>
using namespace std;


int main()
{
	char c = 'c';
	char& rc = c;
	const int& trc = c; //不同类型的引用  trc是一个新的只读变量
	
	rc = 'a';
	
	printf("c = %c\n", c);
	printf("rc = %c\n", rc);
	printf("trc = %c\n", trc);
	 	
	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-51IY9m6d-1583635322451)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583546157521.png)]

什么是符号表?

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

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

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

使用其它变量初始化的const常量仍然是只读变量

#include <iostream>
using namespace std;


int main()
{
	
	int a = 2;
	const int b = a; //b为只读变量,不是真正意义上的常量
	
	int* p = const_cast<int*>(&b); 
	*p = 33;
	
	printf("b = %d\n", b);
	 	
	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27DIwEWJ-1583635322452)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583549964783.png)]

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

const引用的类型与初始化变量的类型
相同:使初始化变量成为只读变量
不同:生成一个新的只读变量,其初始值与初始化变量相同

tips:

在编译期间不能直接确定初始值的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}; //sv.x对int& x进行初始化
    
    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); //与&sv.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;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yoQPVDWv-1583635322453)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583554621220.png)]

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

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

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

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

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

重载的疑惑

C++编译器对字面量的处理方式
整数型字面量的默认类型为int,占用4个字节
浮点型字面量的默认类型为double,占用8个字节
字符型字面量的默认类型为char,占用1个字节
字符串型字面量的默认类型为const char*,占用4个字节
当使用字面量对变量进行初始化或赋值时
无溢出产生:编译器对字面量进行默认类型转换
产生溢出:编译器会做截断操作,并产生警告

char c = 2; //不会给警告
char cc = 20000; //会给警告

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8X8QoCY-1583635322455)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583571670819.png)]

C方式编译的疑惑?

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

tips:

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

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

深入理解extern “C”

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

#include <stdio.h>

extern "C"
{
    
    void func(int x) //C方式的编译主要指按照C语言的规则对函数名进行编译
    {
        const int i = 1;
        int& ri = const_cast<int&>(i); //函数体里的代码依然是c++方式编译  const_cast不会报错 
        
        ri = 5;
        
        printf("i = %d\n", i); //c++编译: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);
    
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z2zcMjt5-1583635322456)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583635216706.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值