1.const和引用
#include <stdio.h>
int main()
{
const int x = 1;//定义常量
const int& rx = x;
int& crx = const_cast<int&>(x);//取地址就会为常量x分配空间,crx成为这个空间的别名,crx为变量
int& nrx = const_cast<int&>(rx);
crx = 6;
nrx = 5;
printf("x = %d\n", x);//1,在符号表中取值,而不是在内存中
printf("rx = %d\n", rx);//5
printf("crx = %d\n", crx);//6
printf("nrx = %d\n", nrx);//5
printf("x = %p\n", &x);
printf("rx = %p\n", &rx);
printf("crx = %p\n", &crx);
printf("nrx = %p\n", &nrx);
volatile const int y = 2;//优化选项,y在内存中取值,而不在符号表中
int* p = NULL;
p = const_cast<int*>(&y);
*p = 6;
printf("y = %d\n", y);//6
printf("*p = %d\n", *p);//6
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;//类型转换产生另外的常量
rc = 'a';
printf("c = %c\n", c);
printf("rc = %c\n", rc);
printf("trc = %c\n", trc);
getchar();
return 0;
}
符号表
1.符号表是编译器在编译过程中产生的关于源程序中语法符号的数据结构,如常量表,变量名表,数组名表,函数名表等等
2.符号表是编译器自用的内部数据结构
3.符号表不会进入最终产生的可执行程序中
只有用字面量初始化的const常量才会进入符号表,对const常量引用会导致编译器为其分配空间,但是这个空间中的值不会被使用,使用其他变量初始化的const常量仍然是只读变量
被volatile修饰的const常量不会进入符号表,会退化为只读变量,每次访问都从内存中取值
const引用的类型与初始化变量的类型:(1)相同,使初始化变量成为只读变量(2)不同,生成一个新的只读变量,其初始值与初始化变量相同
在编译期间不能直接确定初始值的const量,都被作为只读变量处理
2.引用和指针
#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;
}
指针与引用的区别
1.指针是一个变量,其值 为一个内存地址,通过指针可以访问对应内存地址中的值。
引用只是一个变量的新名字,所有对引用的操作(赋值,取地址)都会传递到其引用的变量上
2.指针可以被const修饰成常量或者只读变量
const引用使其引用的变量具有只读属性
3.指针就是变量,不需要初始化,也可以指向不同的地址
引用天生就必须在定义时初始化,之后无法再引用其他的变量
引用的本质就是指针常量
从使用C++语言的角度开看,引用与指针常量没有任何的关系,引用是变量的新名字,操作引用就是操作对应的变量
从C++编译器的角度看,在编译器内部,使用指针常量来实现“引用”,因此“引用”在定义时必须初始化(因为是常量)
所以,在进行编程的时候,站在使用的角度看,与指针毫无关系,但C++程序涉及一些bug或者“奇怪行为”时可以站在C++编译器角度来看待引用
3.重载
#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;
}
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;
}
无警告
深入理解重载规则:精确匹配实参---》通过默认类型转换匹配实参-----》通过默认参数匹配实参
三条规则会同时对已存在的重载函数进行挑选:
当实参为变量并能精确匹配形参时,不再进行默认类型转换的尝试;
当实参为字面量时,编译器会同时进行精确匹配和默认类型转换的尝试,和编译器的实现有关
4.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);
return 0;
}
深入理解extern “C”
extern “C”告诉编译器将其中的代码进行c方式的编译,主要是指按照C语言的规则对函数名进行编译
函数名经过编译后可能与源码中的名字有所不同
C++编译器为了支持重载,函数名经过编译后会加上参数信息,因而编译后的函数名与源码中完全不同
C编译器不会再编译后的函数名加上参数信息
extern “C”中的重载函数经过C方式编译后将得到相同的函数名,因此extern “C”中不允许重载函数,但extern “C”中的函数可以与extern “C”之外的函数进行重载