1. 变量 vs 常量
-
常量: 在编译时会被存储在内存表中的一个实体。
-
变量: 编译器为其分配内存,地址。
-
全局常量波通常会被分配在只读代码/数据区。(从地址来看,发现s1的地址与main的地址非常相近,说明全局常量被分配在代码段里)
#include<stdio.h>
int main(void)
{
char *s1="Hello world"; //“Hello world” 全局常量,分配在代码段里,
//这里的“=”只是简单的将地址赋值给s1
//s1[0]='A'; error
char s2[]="Hello world"; //“=”拷贝,将代码段里的“Hello world” 拷贝到堆栈中
//然后将址赋值给s2
//s2[0]=‘A’; correct
printf("main: %p\n",main); //main: 0401550
printf("s1: %p\n", s1); //s0: 0404000
printf("s2: %p\n",s2); //s1: 061FE0c
}
- const 在编译时刻必须被知道,因此必须被初始化。编译器保证const 值不会被修改。计算机访问内存地址时,mmu负责翻译为物理地址并进行内存保护,从而保证const值不被修改。
2. const 修饰对象vs 指针
- const 修饰指的是不能通过变量去修改,而不是指的是那块内存本身是不是const 。
- *const A p: 对象是const. 即 *p 不能做左值。A * const p : 指针是const.即p 不能做左值。const 在 * 前,对象是const.const 在 * 后 ,指针是const.
- 【1】 int A::f() const{} 即是对象里的成员变量不可修改 <====> int A::f(const A* this)
【2】 int A:: f() {};
A::f()const与A::f() 构成重载(函数名相同,但形参个数或类型不同)
class A
{
public:
A(/* args */){};
~A(){};
void f(){ cout<<"A::f()"<<endl;};
void f() const{cout<<"A::f()const"<<endl;}
};
int main(){
A a;
const A b;
a.f(); // 调用的A::f() ,因此打印 “A::f()”;
b.f(); // 调用的A::f() const 因此打印 “A::f()const”
return 0;
}
const int c=3;
int *p= &c; //错误了,因为通过*p可以修改c,打破了ci是const的限制
3. const 修饰类成员变量
- 成员变量时const,因为无法修改const,所以必须被初始化,不能做数组的size.这是因为在C++中,类的成员变量必须在编译时就确定大小。在这个例子中,数组s的大小是由const成员变量size来确定的,而const成员变量size的值是在运行时才确定的。这样的定义是错误的,因为数组的大小必须在编译时就确定。
class A{
const int size;
int s[size]; // ERROR
};
- 类的内存映像
class A{
public:
const int size;
int s[10];
public:
A(int s);
};
A::A(int s):size(5){
cout<<"A::A()"<<endl;
}
int main(){
A a(11);
printf("&a: %p\n",&a); //输出&a: 061fdf0
printf("&a.size: %p",&a.size); //输出&a.size: 061fdf0
printf("a.s: %p",&a.s); //输出a.s: 061fdf4
return 0;
}
3. const 修饰形参
- void f(const int * x) 形参是指针,编译器会保证不能通过 指针修改x所指的变量的值。
- 传值会在堆栈分配空间,尤其是传对象时会耗费很多的内存空间。传地址时,在对象前加const,可以办证函数不会修改对象的成员变量。
4. 引用
- 引用实际上就是变量的别名
char &r=c; //r即c的别名
- 初始化:定义时绑定变量,绑定后,不能再绑定其它对象
- 赋值: 将值赋值给其所绑定的变量
int x = 3;
int y = 5;
int &a = x;
int &b = y;
a = b; // 将b所绑定的变量的值的赋值给a 所绑定变量的值
cout << "a=" << a << endl; //a=5
cout << "b=" << b << endl; //b=5
- 规则: 【1】本地变量定义时一定要初始化,在运行时绑定的对象不变【2】 函数调用,成员变量,绑定在函数调用和成员遍量初始化时进行。
- g(a); 不能相信在函数调用后a的值不会发生变化(函数原型为 void g(int &a),有可能在函数内部堆a进行修改)
int& h(){
int q=0;
return q;//错误,因为函数调用之后局部变量会被销毁,导致引用悬空
}
- 实质上就是const 指针(A *const p),让代码少些 *、
- const int &z=x; z是x的别名,但通过z不能修改x的值,,即z不能做左值。
4. c++ vs java 内存模型
- c++内存模型
- java内存模型
java中引用的其实等同于c++的指针,因为引用绑定的对象在运行时不可更改。外形没有* ,不能做指针的计算 .而c的指针可以进行计算。
A a=new a();
B b=new b();
a=b; //将a所指的指针指向b所指的对象
5.指针 vs 引用
void f(int &b);
int main(){
f(6);//ERROR,因为临时变量没有名字。绑定智能绑定已存在的变量。
}
6. 引用限制
//int & *p; *p的类型是refernece,如果 *p是reference,则指可以取到reference的地址,而reference 的地址是不可取到的。
//int * &p; p是reference ,但捆着的变量是int 指针。
- 不能创建引用数组,因为引用不是对象,不能被声明为一个数组。
int x = 10;
int& refs[3] = {x, x, x}; // 错误:不能创建引用数组