《重学C++》5. 彻底学会 C++ 指针,引用(一)指针和左值右值

1. 左值和右值

概念:

一般说法,编译器为左值单独分配了一块存储空间,可以取其地址。左值可以放在赋值运算符左边也可以在右边;
右值指的是数据本身;不能取到其自身地址,右值只能赋值运算右边;

左值代表地址单元,右值代表数据本身。

左值最常见的情况如函数和数据成员的名字;
右值是没有标识符、不可以取地址的表达式,一般也称之为“临时对象”。

比如:

 a = b + c;

&a是允许的操作,而&(b+c)不能通过编译,因此a是一个左值,而(b+c)是一个右值。

1
2
3
4
char strHelloWorld[] = {"helloworld"};
char* pStrHelloWorld = "helloworld";
pStrHelloWrold = strHelloWorld; //指针变量的值允许改变
strHelloWorld = pStrHelloWrold; //strHelloWorld是右值,代表字符串的值,不可以被赋值等

理解C和C++中的左值和右值
https://blog.csdn.net/xuwqiang1994/article/details/79924310


2. 指针和数组

指针的数组(array of pointers)与数组的指针(a pointer to an array):
指针的数组T* t[]
数组的指针 T(*t) []

1
2
int* a[4];  //注意:[]优先级比较高,没有小括号时,a优先和[]结合,表明是数组;
int (*b)[4]; //优先和()结合,*表明b是指针;

3. 指针和const

除了指针和数组容易混淆外,指针也容易和const混淆。

const和指针

4. 原始指针

  • 野指针:
    1
    2
    int *a;
    *a=12; //出错,
    一般有三种情况:
  1. 指针变量没有初始化;
  2. 已经释放不用的指针没有置NULL,如delete和free之后的指针;
  3. 指针操作超越了变量的作用范围;
  • 空指针:
    1
    int *a = NULL;
    空指针表明了该指针目前没有指向任何位置。

指针要么被手动初始化指向一个地址,要么就应该被置为NULL。(尽量避免野指针,使用空指针)
同样,在使用一个指针前,要先判断其是否是NULL。


5. 指针和左值右值

一元取地址符 ‘**&**’ 拿一个左值作为参数并且生成一个右值:

1
2
3
4
5
char ch = 'a';char* cP = &ch;

char* cp = &ch; //&ch作为右值正确
//&cp = 97; //&cp左值不合法
char** cpp = &cp; //&cp右值

上面的例子中,&ch和&cp都是只能作为右值而不是左值。
在取地址时,编译器记录变量的地址,并将其数值保存在临时变量中。而所有临时变量(它们均由编译器管理)只能取到数值,是右值的一种
这里要想让&ch和&cp作为左值,只能手动将其保存为一个程序员管理的变量(而不是编译器内部管理的临时变量)。

一元运算符‘*****’(解引用)拿一个右值作为参数而产生一个左值作为结果:

*p会取到指针p的空间作为左值。由于左值可以作为右值使用,*p作为右值时取p指向的空间里的数值。

1
2
3
4
5
6
7
8
9
10
11
12
*cp = 'a';			//*cp左值取变量ch位置

char ch2 = *cp; //*cp此时当右值,取变量ch存储的值

//*cp + 1 = 'a';
//*cp+1左值,不合法的位置。
//为了和1相加,*cp转换为了右值(变量ch存储的值),两个右值相加结果也是右值

ch2 = *cp + 1; //*cp+1右值取到的字符做ASCII码+1操作

*(cp + 1) = 'a'; //*(cp+1)左值语法上合法,取ch后面位置的值。但可能会非法访问。
ch2 = *(cp + 1); //*(cp+1)右值语法上合法,取ch后面位置的值。但可能会非法访问。

++和–运算符

1
2
3
4
5
//++,--操作符
char* cp2 = ++cp; //++指针cp,只能做右值,取cp存储的地址+1的地址作为数值
char* cp3 = cp++; //类似的,返回cp存储的地址作为数值,仅作为右值
char* cp4 = --cp;
char* cp5 = cp--;

*和++组合运算符

注意:注意:++操作符的优先级高于取间接*操作符的优先级。

1
2
3
4
5
//*++,++*
*++cp2 = 98; //取cp2指向地址的后面空间,是左值
char ch3 = *++cp2; //类似上面,但是左值充当了右值,取cp2指向地址的后面空间存储的数值
*cp2++ = 98; //取cp2指向地址,是左值
char ch4 = *cp2++; ////类似上面,但是左值充当了右值,取cp2指向地址中存储的数值

6.读取运算符“贪心法”

编译器程序分解成符号的方法是:一个字符一个字符的读入,如果该字符可能组成一个符号,那么读入下一个字符,一直到读入的字符不再能组成一个有意义的符号。这个处理过程称为”贪心法”。

1
2
3
int a = 1,b = 2,c;
c = a+++b; //相当于a++ +b;
d = a++++b; //相当于a++ ++b,error

小练习:
表达式 ++*++cp 是什么含义?(提示:结合贪心法和堆栈)