c++学习笔记(5.专题一经典问题解析)

本节知识点:

1.const和引用的疑惑:
   a.什么是符号表: 符号表是编译器在编译过程中产生的关于源程序中语法符号的数据结构,如常量表、变量名表、数组名表、函数名表等等。符号表是编译器自用的内部数据结构,不会进入最终产生的可执行程序中。
   b.只有用字面量初始化的const常量才会进入符号表:
      只有const int a = 8; 这样的语句a才能进入符号表。像const int b = c;  或者  const  int* b = (int*)(0x11223344); 这样的语句都使b变量变成了一个只读变量,b不存放在符号表中。还有就是volatile属性的const常量也不会进入符号表中,volatile const int b = 8; 语句中的b变量也一个只读变量,每次访问都从内存中取值。对于这些退化成为只读变量的常量,他们有地址有内存空间,且这个内存空间中存放着变量的值。对于存在符号表中的常量a来说,也可以对它取地址,和取引用,同样会使编译器为a分配一个内存空间,但是这个内存空间不会被使用(分配空间仅仅是为兼容c语言准备的),a常量是存在符号表中的。
   c.const引用的类型与初始化变量的类型相同:
      int a = 9;        const int& b = a;      我们都知道const引用,是个常引用,因为它本质是个引用是个指针,所以它不可能存在符号表中,它是一个只读变量。当与初始化类型相同的时候,b引用仅仅是a的一个别名,与a同属一个内存地址空间,也就是说&a与&b值相同。改变a的时候,b的值也会跟着改变(即b还是a的引用)。
      const引用的类型与初始化变量的类型不相同:
      char a = 9;     const int& b = a;       此时b引用当然还是一个只读变量。但是此时的b引用是编译器新产生的一只读变量了,仅仅是这个只读变量b的初始化是a的值。b已经不再是a的引用了,因为b是编译器新分配内存空间的一个只读变量了,&a与&b的值也不相同了,改变a的时候,b的值也不会跟着变了(a与b完全独立了)。这样我们就很容易理解,const int& b = 3; 的时候,b是一个初始值为3的只读变量(有内存,有地址)。
示例代码:
#include <stdio.h>
int main()
{
	//int b = 9;
	char b = 9;
	const int &a = b;
	
	//b = 12;
	*const_cast<int*>(&a) = 19;
	printf("%d\n",a);
	printf("%d\n",b);
	printf("a %p\n",&a);
	printf("b %p\n",&b);

	return 0;
}
2.引用与指针的疑惑:
a.从使用c++语言的角度来看:
    引用与指针常量没有任何关系,引用是变量的新名字,操作引用就是操作对应的变量。
    从c++编译器的角度来看:
    为了支持新概念“引用”,必须要一个有效的解决方案。 在编译器内部,使用指针常量来实现“引用”,因此“引用”在定义时必须初始化。
b.到底如何理解“引用的本质就是指针常量”:
   当进行c++编译时,直接站在使用的角度看待引用,与指针毫无关系!当对c++程序中的一些涉及引用的bug或者“奇怪行为”进行分析的时候,可以考虑站在c++编译器的角度看待引用!

3.重载的疑惑:
a.c++编译器对字面量的处理方式: 
   整数型字面量的默认类型为int,占用4个字节;浮点型字面量的默认类型为double,占用8个字节;字符型字面量的默认类型为char,占1个字节;字符串型字面量的默认类型为const char*,占用4个字节。 当使用字面量对变量进行初始化或赋值时,无溢出产生,编译器对字面量进行默认类型转换。有溢出产生,编译器会做出截断操作,并产生警告。
b.当重载函数的实参为变量并能够精确匹配参数时,不再进行默认类型转换匹配的尝试。当实参为字面量时,不同编译器处理方式不一样,g++是将字面量进行默认类型转换,然后进行精确匹配参数,成功,就不再进行类型转换的匹配了。有些编译器精确匹配参数成功后,还进行类型转换的匹配,导致int fun(int a, int b)和int fun(int a, char b) 对于fun(1,2)调用的时候,会产生二义性报错(g++不会的)!!!


4.c方式编译的疑惑:
a.extern  "C"告诉c++编译器将其中的代码进行c方式编译,c方式的编译主要指按照c语言的规则对函数名进行编译。对其中的代码其实还是进行了c++方式的编译,也就是说c++特有的属性,关键字都可以在extern  "C"中编译通过的。
b. c++编译器为了支持重载,函数名经过编译后会加上参数信息,因而编译后的函数名与源码中的函数名完全不同。c编译器则不会在编译后的函数名中加上参数信息。
c.正因为c编译器不会在函数名后加上参数信息,所以在extern "C"中,不能以c方式编译多个重载函数,因为他们会被编译成同一个函数名,编译器会报重复定义的错误!
d. 但是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);
        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("Hello");
    func(1, 2);
    
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值