2.1 C++中的const
2.1.1 C与C++中const的比较
-
(1)C语言中的const
C语言中 const修饰的变量是一个 常变量,本质还是变量,有自己的地址空间。 -
(2)C++中的const
1、C++中 const 变量声明的是一个真正的常量,不是变量,所以编译器不会为该常量分配空间;
2、const 修饰的常量会被放到“符号表” 中;
3、对const常量取地址,这一步操作会让编译器为该变量分配空间,分配的空间并不会被a使用;
4、通过指针改变指向的空间的值,这个空间是编译器为常量分配的空间,但是常量的值并不在这个空间内 所以即使通过指针修改了这个空间的值,也不会影响到 a 本身。 -
(3)C++编译器对const常量的处理
当碰见常量声明时,在符号表中放入常量 ==> 问题:那又如何解释取地址?
编译过程中若发现对const使用了extern或者&操作符,则给对应的常量分配存储空间(兼容C),但是编译过程中若发现使用常量则直接以符号表中的值替换,即使给该常量分配了空间,也并不使用里面的值。
2.1.2 const与#define
-
相同之处
C++中的const修饰的和#define定义的,是一个真正的常量,而不是C中变量(只读)。在const修饰的常量编译期间,就已经确定下来了 -
不同之处
(1)const常量是由编译器处理的,提供类型检查和作用域检查
(2)宏定义由预处理器处理,单纯的文本替换
#include <iostream>
using namespace std;
void f()
{
#define b 100
const int c = 200;
}
int main()
{
const int a = 1; //C++中,const修饰的是常量,存放在符号表中
//a++;
int *p = (int *)&a; //对const修饰的常量取地址,编译器分配了一个整形的长度,并且把数字1填写到对应的内存
*p = 2;
cout << a << endl; //因为a是常量,所以用1替换a
cout << b << endl; //作用域不同
//cout << c << endl; // 会报错
return 0;
}
2.2 C++中的引用
2.2.1 普通引用
(1)变量名回顾
- 变量名实质上是一段连续存储空间的别名,是一个标号(门牌号)
- 程序中通过变量来申请并命名内存空间,通过变量的名字可以使用存储空间
(2) C++引用概念
- 引用可以看作一个已定义变量的别名
- 引用的语法:Type& name = var;
- 引用在定义的时候必须初始化。
#include <iostream>
int main()
{
// 定义一个int型变量a,在内存中占4个字节,
// 变量名 a 就代表了这一块内存,或者说 a 是这块内存的别名
int a = 10;
// 定义了一个引用变量 b ,它是 a 的别名,和 a 代表同一块内存空间
// 对 b 的操作 和 对 a 的操作是一样的
int& b = a;
// 改变 b 等于改变了 a
b = 20;
std::cout << " a = " << a << " b = " << b << std::endl;
// a 与 b 代表同一块内存空间
std::cout << &a << " " << &b << std::endl;
return 0;
}
(3) 引用的意义
- 1、引用作为其它变量的别名而存在,因此在一些场合可以代替指针;
- 2、引用相对于指针来说具有更好的可读性和实用性。
#include <iostream>
using namespace std;
void swap(int &x, int &y) //int &x = m;
{
y = x + y;
x = y - x;
y = y - x;
}
int main()
{
int a = 100;
int &b = a; //定义引用b,初始化为a b是a的别名
b = 200; //a = 200
cout << a << endl;
int m = 1, n = 2;
swap(m, n);
cout << "m = " << m << " n = " << n << endl;
return 0;
}
(4)引用的本质
-
1. 引用类型是否占内存空间?
struct Student
{
char &a;
char &b;
};
sizeof(struct Student) ====》 应该是输出 16 个字节(当作指针)
#include <iostream>
using namespace std;
struct Test
{
int &a;
int &b;
char &c; //引用的本质是常指针
};
int main()
{
int a = 1;
char ch = 'x';
int &pa = a; //引用定义的时候必须要初始化
char &pc = ch; //等价于 char *const pc = &ch;
//int &f;
cout << sizeof(pa) << endl; //引用的长度就是其引用的对象的长度
cout << sizeof(pc) << endl;
//分析正常语法现象的时候,当做别名来分析,
//但是分析奇怪的语法现象,需要考虑引用的实现
cout << sizeof(struct Test) << endl;
return 0;
}
-
2. 引用的本质
-
引用在C++中的内部实现是一个常指针
Type& name <====> Type* const name -
1、C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
-
2、从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏
当我们使用引用语法的时,我们不去关心编译器引用是怎么做的
当我们分析奇怪的语法现象的时,我们才去考虑c++编译器是怎么做的
(5)函数返回值为引用
- 返回栈变量(不要返回局部变量的引用)
- 返回全局变量或静态变量
#include <iostream>
using namespace std;
int g = 100;
int &f1()
{
int a = 1;
//return a; //不要返回局部变量的引用
return g;
}
int f2()
{
return g;
}
int main()
{
int &b = f1();
f1() = 1;
//f2() = 1; //等价于100 = 1;
return 0;
}
(6)指针引用
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
void Init(char *&s) //引用指针
{
s = (char *)malloc(sizeof(char) * 100);
}
int main()
{
char *s = NULL;
Init(s);
strcpy(s, "hello");
cout << s << endl;
return 0;
}
2.2.2 常引用
- const & int e 相当于 const int * const e
- 普通引用 相当于 int *const e1
- 当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
- 使用字面量对const引用初始化后,将生成一个只读变量
#include <iostream>
int main()
{
int a = 10;
int &b = a; // 普通引用
int x = 20;
const int& y = x; // 常引用 让变量y拥有只读属性,不能通过y改变x的值
// 常引用 初始化 分为 2中情况
// 1、用变量初始化 常引用
{
int a1 = 20;
const int& b1 = a1; // a1 变量去初始化常引用
}
// 2、用常量去初始化常引用
{
const int a = 40; // C++编译器把a放在符号表中
// int& b = 41; // 普通引用,引用一个常量,常量是没有地址空间的,这样的做法是不合法的
// 使用常量去初始化常引用是合法的,C++编译器会为该引用分配空间,常量的值存储到分配的空间中去
// 使用常量对 const引用 初始化后,将生成一个只读变量
const int& b = 42;
int *p = (int *)&b;
*p = 50;
std::cout << "b = " << b << std::endl;
}
return 0;
}