变量定义的格式为
type name; // 定义type类型变量name,默认初始化
或者:
type name = value; // 定义type类型变量name,并设置初始值为value
类型说明符 变量名
例如:
// 下面的定义没有给初始值,所以变量会被默认初始化
int a;
bool b;
char c;
double d;
long l;
short s;
// 定义整型变量并设置初始值为1
int a = 1;
注意:定义在函数体外的内置类型变量会被初始化为0;
定义在函数体内部的内置类型变量不会被初始化,所以值是未定义的;使用该值可能会发生错误。
初始化是创建变量的时候赋予其一个初值;
赋值是把对象的当前值擦除,赋予其一个新值;
声明是指把名字告知程序,使得程序知道该名字。
定义是负责创建与名字关联的实体。
声明和定义都规定了变量的类型和名字,但是声明不会为变量申请存储空间,定义会为变量申请内存空间,也可能会为变量赋初值。
包含了显示初始化的声明自动变为定义。
变量只能被定义依次,但是可以多次声明。
声明变量的方法:
extern int i; //声明一个整型变量i
int j; //声明并定义j
extern int k = 0; //声明并定义k
C++标识符右字母、下画线、数组组成;
必须以字母或者下画线开头,不能数字开头,长度没有限制,大小写敏感。
变量的作用域起始于名字的声明语句,以声明语句所在的作用域末端结束
int i; //i是一个全局变量,在整个程序中有效
void test()
{
int i; //i是一个局部变量,在函数test的函数体内有效,同时这个局部的变量i覆盖了上面的全局变量i,如果在函数体内想要使用全局的i可以加上全局作用域符::i来使用
}
引用就是给变量起一个别名,用法变量原来的名字是完全相同的。
格式:
int i = 0;
int& r1 = i; //r是i的一个引用,也就是一个别名
int& r2 = r1; //正确,r2是i的另一个引用,r2,r1,i代表相同的变量
定义引用时必须初始化,否则无法知道引用的是哪个变量,编译出错。
引用只能绑定在对象上,不能绑定字面值或者表达式的计算结果。
指针是指向某种类型的变量,其值代表其指向变量的地址。
指针本身也是一个对象,允许赋值和拷贝,可以执行不同的对象。
指针在定义时可以不给初始值;
格式:
int* p; // p是一个int型指针,指向一个int型的对象
int i = 0;
int* pi = &i; //p是一个int型指针,指向i
如果想要通过指针来访问(获取值,修改值等)对象,对指针使用*(解引用)操作符就可以
int i = 0;
int* p = &i;
*p = 10; // 把p指向的对象的值赋为10,此时i的值变为10;
cout << *p << endl; // 把p指向对象的值打印到标准输出流,屏幕输出10;
不使用*(解引用)运算符时,给指针赋值,是改变了指针所指向的地址
int i = 0;
int* p = &i; // 让p指向i;
int a = 10;
p = &a; // 让p重新指向a;
因为引用本身不是对象,所以指针不能指向引用,但是指针可以指向指针
int i = 0;
int* p = &i; // p是指针,指向int类型的i
int** pp = &p; // pp是指针的指针,指向int*类型的p
const 是常量修饰符,有const修饰的变量,其值不能被修改;实现只读效果
const修饰的变量,其值不能被修改,所以必须要在定义的时候进行初始化。
const int i = 42;// 正确,i是一个常变量,其值只读,不可修改
const int j; // 错误,常变量必须在定义时初始化
默认状态下,const对象只在本文件内有效,如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
const的引用,可以绑定任意表达式作为初始值(非const的引用只能绑定到对象上),,const引用可以绑定非常量对象,字面值,甚至是一般表达式。
int i = 10;
const int& r1 = i; //正确
const int& r2 = r1 * 10; //正确
const int& r3 = 42; //正确
int& r4 = r1 * 10; //错误
指针可以指向常量,指向常量的指针,不能用于改变其所指对象的值,如果想存放常量对象的地址,只能使用指向常量的指针。
const int i = 10;
int* pi = &i; // 错误,pi不是一个指向常量的指针,但是其指向的变量是一个常变量
const int* ptr = &i; //正确,ptr是一个指向常整型变量的指针
*ptr = 20; // 错误,不能对指向常量的指针执行的对象进行赋值
const修饰的指针,可以指向非常量,但是此时不能通过指针修改指向对象的值
double d = 3.14;
const double* p = &d; //正确,p是指向d的一个指针,但是通过这个指针只能对d进行只读操作,不能进行修改操作
d = 2.718; // 此时,可以通过其他方法修改d的值
指针本身是一个变量,const也可以作用与指针本身,此时指针为常量指针,也就是指针的值(指针指向的地址)不能变化。
int i = 10;
int a = 20;
int* const p = &i; // 正确,p指向i,同时p的值不能改变,也就是p不能重新指向别的对象
*p = 20; // 正确,可以通过指针修改指向对象的值
p = &a; //错误,p是const类型的指针,其值(指向的地址)不能被修改
对于指针来说,const可以用来说明指针本身是常量(顶层const)或者指针指向常量(底层const)
对于引用来说,const全部都是底层const(也就是说是对常量的引用)
int i = 10;
const int* p1 = &i; // 底层const,用来说明指针指向常量(只读,不能通过指针修改指向对象的值)
int* const p2 = &i; // 顶层const,用来说明指针是一个常量(不能指向别的地址)
const int& r1 = i; // 底层const,用来说明是对常量的引用(只读,不能通过引用来修改对象的值)
一般地,在赋值时,顶层const不对赋值产生影响,但是底层const的变量不能赋值给非底层const的变量
int i = 10;
const int* p1 = &i;
int* const p2 = &i;
int* p3 = p1; // 错误,p1具有底层const,其指向的是常量(只读,不对其指向的对象进行修改),而p3不具有底层const,其指向的不是常量(非只读,可以通过其对指向的对象进行修改)
int* p4 = p2; // 正确,p2具有顶层const,代表p2是常指针,可以把p2指向的地址赋给p4
常量表达式是指值不会改变,并且在编译过程就能得到计算结果的表达式。
C++11允许将变量声明为constexpr类型以便由编译器验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,并且必须用常量表达式初始化
constexpr int i = 20;
constexpr int limit = i + 1;
constexpr int sz = size(); //当size是一个constexpr函数时正确
constexpr作用于指针时,仅对指针有效,与指针所属的对象无关
const int* p = nullptr; //p是一个指向常量整型的指针
constexpr int* q = nullptr; // q是一个指向整型的常量指针,constexpr把它定义的对象限定为顶层const
typedef unsigned int size_t; //size_t是unsigned int 的别名
using ui = unsigned int; //ui是unsigned int的别名
size_t sz = 10; // sz是unsigned int类型变量
ui i = 10; // i是unsigned int类型变量
指针类型的别名和const一起使用时,const会被认为是顶层const而不是底层const
typedef char* pstchar;
const pstchar pc; // 此时,pc是常量指针,而不是指向常量的指针,const是顶层const
当要将一个复杂表达式的结果赋值给某个变量,必须要清楚的知道表达式结果的类型并在声明是写出该类型,对于比较复杂的表达式,并不是一件容易的事。
C++11新标准引入了auto类型说明符,可以让编译器帮助判断表达式结果类型
auto a = 10; // a是整型
auto sz = 0, pi = 3.14; // 这种声明是错误的,因为编译器判断0是整型,3.14是double型,无法确定auto实际应该代表的类型
auto一般会忽略顶层的const,同时底层的const会保留
int i = 0;
const int ci = 10, &cr = i;
auto a = ci; // ci具有顶层const,auto会忽略,a是一个整型变量
auto b = cr; // cr是ci的引用,相当于把ci赋值给b,b是一个整型变量
auto c = &i; // c是一个整型指针,指向整型变量
auto d = &ci; // d是一个指向常量的指针,因为ci具有底层const,
如果需要auto推断出的类型具有顶层const,需要明确指出:
const auto e = ci; // e是一个常整型(const int)
decltype 可以根据表达式返回类型,不用计算表达式的值
decltype(size()) sz = 10; // sz的类型是size函数的返回值的类型
decltype在得出变量类型时会保留其顶层const和引用
const int ci = 0, &cr = ci;
decltype(ci) x = 0; // x是const int类型
decltype(cr) y = x; // y是const int&类型,绑定到x
decltype(cr) z; // 错误,z是引用,必须初始化
decltype的表达式如果是对指针解引用操作,那么得到的将是一个引用类型
int i = 10;
int* p = &i;
decltype(*P) a = i; //a是一个整型的引用,如果不初始化,会报错
decltype的结果类型和表达式密切相关,如果对变量名加上括号,那么得到的将是该变量类型的引用
int i = 10;
decltype(i) a; //a是一个int型变量
decltype((i)) b; // 错误,b是一个整型引用,必须初始化