专题一经典问题解析-7
一。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; //类型不同转化的是只读常量,如果是char&则结果输出是a rc = 'a'; printf("c = %c\n", c); printf("rc = %c\n", rc); printf("trc = %c\n", trc); return 0; }
输出结果
2.什么是符号表?
#符号表是编译器在编译过程中产生的关于源程序中语法符号的数据结构。
#如常量表、变量名表、数组名表、函数名表。
#符号表是编译器自用的内部数据结构
#符号表不会进入最终产生的可执行程序中。
3.上面程序疑惑解答
a。只有用字面量初始化const才会进入符号表。
#对const常量引用会导致编译器为其分配空间
#虽然const常量被分配了空间,但是这个空间中的值不会被使用
#使用其他变量初始化的const常量仍然是只读变量(其他变量变了,它就得变,上例的z)。
b。被volatile修饰的const常量不会进入符号表
#退化为只读变量,每次访问都从内存中取值(上例的y和*p)
c。const引用的类型与初始化变量的类型
如果相同:使初始化变量成为只读变量
如果不同:生成一个新的只读变量,其初始值与初始化变量相同
二。引用与指针的疑惑
1.代码
#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; }
输出结果
2。疑惑答疑
a。指针是一个变量,其值为一个内存地址,通过指针可以访问对应内存地址中的值
. b。引用就是变量的一个新名字,所有对引用的操作(赋值,取地址)都会传递到引用的变量上面。
c。指针可以被const修饰成为常量或者只读变量。
d。const引用使其引用的变量具有只读属性。
e。指针就是变量,不需要初始化,也可以指向不同的地址。
d。引用天生就是在定义的时候初始化,之后便无法在引用其他变量。
3.如何理解“引用的本质就是指针常量”
a。从使用C++语言的角度来看
#引用和指针常量没什么关系。
#引用是变量的新名字,操作引用就是操作对应的变量
b。从C++编译器的角度来看
#为了支持新概念“引用”必须要一个有效的解决方案
#在编译器内部,使用指针变量来实现“引用”
#因此“引用”在定义时必须初始化
三。重载的疑惑
1.代码
#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; }
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); printf("Press any key to continue..."); getchar(); return 0; }
2.深入理解extern“C”
#extern“C”告诉C++编译器将其中的代码进行C方式的编译
a。C方式的编译主要按照C语言的刚刚规则对函数名进行编译。
#函数名经过编译后可能与源码中的名字有所不同
#C++编译器为了支持重载,函数名经过编译后会加上参数信息,因此编译后的函数名与源码中完全不同。
#C编译器不会在编译后的函数名中加上参数信息。
b。extern“C”中的重载函数经过C方式编译后将得到相同的函数名,因此extern“C”中不允许函数重载,但extern“C”中的函数可以与extern“C”之外的函数进行重载。