事实上在C++语言中,初始化和赋值是两个完全不同的操作。
变量能且只能被定义一次,但是可以被多次声明。

代码实例:
#include
int main()
{
int i = 42;
std::cout << "i为:" <<i << std::endl;
int &r = i;
std::cout << "r为:" << r <<std::endl;
int *p;//p为所指地址,*p为该地址所存得值
p = &i;
std::cout << "p为:" << p << std::endl;
std::cout << "*p为:" << *p << std::endl;
*p = i;
std::cout << "p为:" << p << std::endl;
std::cout << "*p为:" << *p << std::endl;
int &r2 = *p;
std::cout << "r2为:" << r2 << std::endl;
}
运行结果:

在新标准型下,现在的C++程序最好使用 nullptr 或 0 进行初始化,同时尽量避免使用NULL。
int zero = 0;
pi = zero; //错误:不能把int变量直接赋给指针
void*是一种特殊的指针类型,可用于存放任意对象的地址。一个 void * 指针存放着一个地址,这一点和其他指针类似。不同的是,我们对该地址中到底是个什么类型的对象并不了解:
利用void* 指针能做的事儿比较有限:拿它和别的指针比较、作为函数的输入或输出,或者赋给另外一个void* 指针。不能直接操作 void* 指针所指的对象,因为我们并不知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。
指针是内存中的对象,像其他对象一样也有自己的地址,因此允许把指针的地址再存放到另一个指针当中。
int ival =1024;
int *pi = &ival; //pi指向一个int型的数
int **ppi = π // ppi指向一个int型的指针
此处pi是指向int型数的指针,而ppi是指向int型指针的指针,下图描述了它们之间的关系。

解引用int型指针会得到一个int型的数,同样,解引用指向指针的指针会得到一个指针。此时为了访问最原始的那个对象,需要对指针的指针做两次解引用:
int i = 42;
int *p ; //p是一个int型指针
int *&r = p; // r是一个对指针p的引用
r = &i; //r引用了一个指针,因此给r赋值&i就是令p指向i
*r = 0; //解引用r得到i,也就是p指向的对象,将i的值改为0
面对一条比较复杂的指针或引用的声明语句时,从右向左阅读有助于弄清楚它的真实含义。
(1)、const的引用:
int i = 42;
int &rl = i; //引用ri绑定对象i
const int &r2 = i; //r2也绑定对象i,但是不允许通过r2修改i的值
rl = 0; //r1并非常量,i的值修改为0
r2=0; //错误:r2是一个常量引用
(2)、指针和const:
const指针:
把*放在const关键字之前用以说明指针是一个常量,这样的书写形式隐含着一层意味,即不变的是指针本身的值而非指向的那个值:
int errNumb = 0;
int *const curErr = &errNumb; //curErr将一直指向errNumb
const double pi = 3.14159;
const double *const pip = π //pip是一个指向常量对象的常量指针
指针本身是一个常量并不意味着不能通过指针修改其所指对象的值:
*pip = 2.72; //错误:pip是一个指向常量的指针
//如果curErr所指的对象(也就是errNumb)的值不为0
if (*curErr) {
errorHandler();
*curErr = 0; //正确:把 curErr所指的对象的值重置
}
int i = 0;
int *const pl = &i; //不能改变pl的值,这是一个顶层const
const int ci = 42; //不能改变ci的值,这是一个顶层const
const int *p2 =&ci; //允许改变p2的值,这是一个底层const
const int *const p3 = p2; //靠右的const是顶层const,靠左的是底层const
const int &r = ci; //用于声明引用的const都是底层const
const int max_files = 20 ; //max_files是常量表达式
const int limit = max_files + l; //limit是常量表达式
int staff_size = 27; //staff_size不是常量表达式
const int sz = get_size(); // sz不是常量表达式,其具体值直到运行时才能获取到
constexpr int mf = 20; //20是常量表达式
constexpr int limit = mf + l; // mf +1是常量表达式
constexpr int sz = size (); //只有当size是一个constexpr函数时才是一条正确的声明语句
指针和constexpr
const int *p = nullptr; //p是一个指向整型常量的指针
constexpr int *q = nullptr; // q是一个指向整数的常量指针
constexpr int *np = nullptr; //np是一个指向整数的常量指针,其值为空
int j = 0;
constexpr int i = 42; // i的类型是整型常量
// i和j都必须定义在函数体之外
constexpr const int *p = &i; //p 是常量指针,指向整型常量
constexpr int *p1 =&j; //p1是常量指针,指向整数j
typedef double wages; // wages是double的同义词
新标准规定了一种新的方法,使用别名声明(alias declaration)using来定义类型的别名:
using sI = Sales_item; // SI是sales_item的同义词
遇到一条使用了类型别名的声明语句时,人们往往会错误地尝试把类型别名替换成它本来的样子,以理解该语句的含义:
typedef char *pstring;
const pstring cstr = 0; // cstr是指向char的常量指针
const char *cstr = 0; //是对const pstring cstr的错误理解
前者声明了一个指向char的常量指针:
改写后的形式则声明了一个指向const char的指针。
int i =0, &r = i;
auto a = r; //a是一个整数(r是i的别名,而i是一个整数)
const int ci = i, &cr = ci;
auto b = ci; // b是一个整数( ci的顶层const 特性被忽略掉了)
auto c = cr; // c是一个整数(cr是ci的别名,ci本身是一个顶层const)
auto d = &i; // d是一个整型指针(整数的地址就是指向整数的指针)
auto e = &ci; //e是一个指向整数常量的指针(对常量对象取地址是一种底层const)
const auto f = ci; //ci的推演类型是int,f是const int
auto &g = ci; //g是一个整型常量引用,绑定到ci
auto &h = 42; //错误:不能为非常量引用绑定字面值
const auto &j= 42; //正确:可以为常量引用绑定字面值
要在一条语句中定义多个变量,切记,符号&和*只从属于某个声明符,而非基本数据类型的一部分,因此初始值必须是同一种类型:
auto k = ci, &l = i; // k是整数,1是整型引用
auto &m = ci, *p = &ci; // m是对整型常量的引用,p是指向整型常量的指针1
//错误:i的类型是int而&ci的类型是const int
auto &n = i, *p2= &ci;
decltype类型指示符:
希望从表达式的类型推断出要定义的变量的类型,decltype的作用是选择并返回操作数的数据类型.
decltype(f()) sum = x; // sum的类型就是函数f的返回类型
struct Sales_data {
std:: string bookNo;
unsigned units_sold = 0;
double revenue = 0.0 ;
};
C++程序还会用到的一项预处理功能是头文件保护符(header guard),头文件保护符依赖于预处理变量;预处理变量有两种状态:已定义和未定义。
使用这些功能就能有效地防止重复包含的发生:
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
struct sales_data {
std:: string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
#endif
整个程序中的预处理变量包括头文件保护符必须唯一,通常的做法是基于头文件中类的名字来构建保护符的名字,以确保其唯一性。为了避免与程序中的其他实体发生名字冲突,一般把预处理变量的名字全部大写。
头文件即使(目前还)没有被包含在任何其他头文件中,也应该设置保护符。头文件保护符很简单,程序员只要习惯性地加上就可以了,没必要太在乎你的程序到底需不需要。