0. 表达式由运算符及其操作数组成
i++ ++ 是后自增运算符, i是它的操作数
i+=1 += 是求和赋值运算符, i和1都是它的操作数, i是左操作数, 1是右操作数
1. 左值和左值表达式
可以放在赋值运算符左侧的操作数是左值
如果一个表达式的计算结果是左值,那么这个表达式是左值表达式
所以左值表达式也可以放在赋值运算符的左侧
2.右值和右值表达式
右值不能放在赋值运算符左侧
如果一个表达式的计算结果是右值,那么这个表达式是右值表达式
例如:
1 数值常量, 字面值
'A' 字符常量, 字面值
"ABC" 字符串常量, 但做为一个表达式, 它的值是这个字串在内存中的首地址
所以它实际是一个只读指针(这个不是右值表达式, 是只读的左值表达式)
1+2 数值求和表达式, 结果是右值, 所以是个右值表达式
int i=1; i++ ; i++的结果是右值,所以是个右值表达式
(未经验证,是当前的猜测:右值不在内存中存放, 是个"瞬间值",只出现在CPU寄存器中,所以没有地址)
3.指针变量的声明和它的"间接访问表达式" (按首地址读写内存)
int a=1;
int *p_int=&a; 指针变量声明,同时对指针变量赋初值
*p_int=3; 对"间接访问表达式"赋值, *p_int是个表达式, 是个左值表达式
第二行和第三行的赋值表达式的形式相似,不易理解
第二行是对p_int这个变量进行赋值 (初始化, 因为这一行是变量声明)
第三行是对 *p_int这个表达式赋值 (这里出现了p_int这个变量,但不是对它赋值, 而是对整个表达式赋值)
3.1 在C语言中指针变量的一个重要用法就是通过*运算符形成一个"左值表达式"
再对整个"左值表达式"的计算结果(左值)进行赋值
3.2 "左值"就是内存中特定的一片字节,这与第一篇中的观点对应
(一个变量就是内存中特定的一段连续的字节,一段可以存入和读出数据的内存字节)
3.3 除了*外, []本质上也是间接访问运算符, 一个数组变量的某个元素, 实质上是个表达式,一个左值表达式
int a[3]={1,3,5};
a[1]+=1; a[1]是个左值表达式(但a是只读指针变量),对它赋值, 就是对特定内存段写入数值
6.关于i++和++i
i++ 这个表达式的计算结果是 "自增前的i"
(注意, i++是个右值表达式, "后++"会对i的值加1,但整个表达式的值是i自增前的值)
++i 这个表达式的计算结果是 "自增前的i"+1 (注意, ++i也是个右值表达式)
int a, i=1;
a=i++; //a=1
a=++i; //a=3
下面这两行说法本身没问题, 但会给我们以严重的误导
i++是在使用i这个变量之后, 再对i进行自增运算
++i是在使用i这个变量之前, 先对i进行自增运算
a=i++; 按上面说法,似乎是用i对a赋值,这是错误的
在对a赋值时,并不使用i的值, 而是使用 i++这个表达式的值,也就是i++这个表达式的计算结果
18-08-06 补充例子: ++作为后缀使用时, 会在所在表达式求值完成后, 再对运算数+=1。注意赋值表达式也是"表达式"
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=3, j=0;
j -= (i++ + j++, (i++*3 + i++*2 +j++) * 2);
printf("i=%d, j=%d\n", i, j);
printf("%d, %d\n", (i++*2, i++), i);
printf("%d\n", i);
getch();
return 0;
}
int i=2; i=i++;
以上两个语句中第二个语句是赋值表达式语句, 其中有两个表达式需要计算求值,
第一个是i++, 表达式计算结果值为2,
所以第二个表式就是i=2, 这时i被赋值为2, 整个赋值表达式的值也是2, 至此计算求值完成
这时发生i+=1, 所以再引用i时, i==3
上例的最终输出为:
啊哈C:
i=6, j=-40
7, 8
8
VC++6.0
i=6, j=-40
7, 7
8
TC7
i=6, j=-40
7, 6
8
从以上三个编译器来看, vc++6.0, 函数参数是从左向右处理, tc7是从右向左处理, 并且都把函数调用做为表达式求值来处理, 也就是在函数调用(表达式求值)完成后, 再执行i+=1
只有啊哈C, 在函数调用完成前, 第二个参数传值完成后, 就执行i+=1 //18-08-06补充
7. 数组变量的概念还有些不清晰
int a[]={1, 3, 5};
数组变量是说a吗? 不是, 数组变量a应该是说存放{1, 3, 5}这三个数的"特定的"的那片内存
那么a是什么?一个可能, 是个只读型的指针变量,存放着这片内存的首地址(必须是只读,因为需要始终对应这个首地址)
a就只是一个指针, 用于不同表达式, 会有不同的效果