运算符、表达式、语句
算术运算符
包括:=,+,-,*,/,%,++,--
赋值运算符
= 为赋值运算符。
赋值行为从右往左进行。
可以连续赋值:
#include <stdio.h> int main(void) { int a, b, c; a = b = c = 0; // 连续赋值 printf("%d\t%d\t%d\n", a, b, c); return 0; }
执行结果:
0 0 0
不支持连续初始化, 下面语句是错误的:
int a = b = c = 10;
要区分初始化和赋值:
#include <stdio.h> int main(void) { const int a = 1; // 初始化 int b = 2; // 初始化 b = 3; // 赋值 return 0; }
运算过程中的过程值是一个临时值:
#include <stdio.h> int main(void) { const int a = 1; // 初始化 int b = 2; // 初始化 b = 3; // 赋值 int c; c = 3 * (a + b); // 表达式(a+b) 是右值,是一个临时值,计算完毕后便会被丢弃 return 0; }
派生赋值运算符
包括:+= , -= , *= , /= , %=
这些赋值运算符都用于更新变量, 左侧是一个变量名, 右侧是一个表达式.
赋给变量的新值是根据右侧表达式的值调整之后的值.
确切的调整方案取决于具体的运算符.
例如:
scores += 20 is the same as scores = scores + 20 .
dimes -= 2 is the same as dimes = dimes - 2 .
bunnies *= 2 is the same as bunnies = bunnies * 2 .
time /= 2.73 is the same as time = time / 2.73 .
reduce %= 3 is the same as reduce = reduce % 3 .
这几个派生的赋值运算符的优先级和 = 相同.
加法运算符
+
减法运算符
-
加法运算符和减法运算符是二元运算符,还可以作为一元运算符,即符号运算符,加法运算符不改变值,减法运算符使值变为相反数。
#include <stdio.h> int main(void) { int a = 1, b = 2; int c = -a, d = +b; printf("%d\t%d\n", c, d); return 0; }
执行结果:
-1 2
乘法运算符
*
除法运算符
/
整数除法的结果为整数,进行截断,丢弃小数部分,不会进行四舍五入。C99 规定趋零截断。
浮点数除法的结果是浮点数。即便可以整除,结果也是浮点数。
求模运算符
%
求模运算符只能作用于整数。
在 C99 规定整数除法是趋零截断之前,求模运算也有很多种处理方法。自从 C99 规定趋零截断之后,求模运算也就只有一种计算方式了。如果第 1 个运算对象是负数,那么求模的结果为负数,如果第 1 个运算对象是正数,那么求模的结果也是正数。
公式:
程序示例:
#include<stdio.h> int main(void) { int a = 8; int b = 3; printf("8 %% 3 = %d\n", a % b); return 0; }
结果:
8 % 3 = 2
求模运算符常用于控制程序流。程序示例:
// 输入秒数,将其转换为分钟数和秒数 #include<stdio.h> #define SEC_PER_HOUR 60 int main(void) { printf("Enter the seconds (Enter q to quit): "); int seconds = 0; int min = 0; while (scanf("%d", &seconds) == 1) { min = seconds / SEC_PER_HOUR; seconds = seconds % SEC_PER_HOUR; printf("minutes = %d, seconds = %d.\n", min, seconds); printf("Enter the seconds (Enter q to quit): "); } return 0; }
结果:
Enter the seconds (Enter q to quit): 121 minutes = 2, seconds = 1. Enter the seconds (Enter q to quit): 23 minutes = 0, seconds = 23. Enter the seconds (Enter q to quit): q
负整数求模的结果的正负和第一个整数相同。程序示例:
#include<stdio.h> int main(void) { printf("-11 %% 5 = %d\n", (- 11) % 5); printf("-11 %% -5 = %d\n", (-11) % -5); printf("11 %% -5 = %d\n", 11 % -5); printf("11 %% 5 = %d\n", 11 % 5); return 0; }
结果:
-11 % 5 = -1 -11 % -5 = -1 11 % -5 = 1 11 % 5 = 1
递增运算符
++
递减运算符
--
如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增或递减运算符。
如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。
代码示例:
#include <stdio.h> int main(void) { int x = 3; int y = (x++) * 3 + x++; // 无法保证两个x++哪个先执行 return 0; }
条件运算符
?=
是唯一一个三元运算符。
格式: 表达式 1 ? 表达式 2 : 表达式 3
以上构成一个条件表达式.
当表达式 1 为真时, 条件运算表达式的值为表达式 2 的值, 否则条件运算表达式的值为表达式 3 的值.
若表达式 1 为真,则只算表达式 2,表达式 3 不计算。若表达式 1 为假,则只算表达式 3,表达式 2 不计算。
求两个数的较大值:
#include <stdio.h> int max(int a, int b) { return a > b ? a : b; } int main(void) { int a, b; scanf("%d %d", &a, &b); printf("%d\n", max(a, b)); return 0; }
条件运算符的结合方向是自右至左.
条件表达式可以嵌套, 即其中的表达式又是一个条件表达式.
int a, b, c, d; a > b ? a : c > d ? c : d;
相当于:
a > b ? a : (c > d ? c : d);
条件运算符可以替代 if else, 代码更加简洁, 编译器生成的机器码也更简洁.
程序示例:
#include<stdio.h> #define COVERAGE 200 // 每一罐油漆可以粉刷的面积 int main(void) { int sq; // 需要粉刷的面积 int cans; // 需要的罐数 printf("Enter the square to be painted (Enter q to quit): "); while (scanf("%d", &sq) == 1) { cans = sq / COVERAGE; cans = sq % COVERAGE == 0 ? cans : cans + 1; printf("You need %d %s.\n", cans, cans == 1 ? "can" : "cans"); printf("Enter the square to be painted (Enter q to quit): "); } return 0; }
结果:
Enter the square to be painted (Enter q to quit): 199 You need 1 can. Enter the square to be painted (Enter q to quit): 200 You need 1 can. Enter the square to be painted (Enter q to quit): 201 You need 2 cans. Enter the square to be painted (Enter q to quit): q
关系运算符
6 个:>,>=,<,<=,=,!=
由关系运算符构成的表达式称为关系表达式。
关系运算符的结果即关系表达式的值为真 (true) 或假 (false)。真等于 1,假等于 0。0 为假,非零为真。
程序示例:
#include<stdio.h> int main(void) { int a, b; a = (10 > 2); b = (10 < 2); printf("a = %d, b = %d\n", a, b); return 0; }
结果:
a = 1, b = 0
程序示例:
#include<stdio.h> int main(void) { int a = 3; while (--a) { printf("%d is true.\n", a); } printf("%d is false.\n", a); return 0; }
结果:
2 is true. 1 is true. 0 is false.
== 是相等运算符
当关系运算符比较两个字符时, 参与比较的是两个字符的机器码, 如 ASCII 码.
不能用关系运算符比较两个字符串.
比较两个浮点数时, 尽量只是用 > 和 <, 因为浮点数在内存中的存储存在舍入误差, 这可能导致理论上相等的两个数在计算机中不相等. 此时可以使用 fabs() 函数比较两个浮点数是否相等. 该函数返回一个浮点数的绝对值.
关系运算符的优先级高于赋值运算符。
关系运算符内部有两种优先级:
高优先级组:<, <=, >, >=
低优先级组:==, !=
逻辑运算符
C 语言的逻辑运算符包括三种:
逻辑运算符可以将两个关系表达式连接起来.
Suppose exp1 and exp2 are two simple relational expressions, such as cat > rat and debt == 1000 . Then you can state the following:
■ exp1 && exp2 is true only if both exp1 and exp2 are true.
■ exp1 || exp2 is true if either exp1 or exp2 is true or if both are true.
■ !exp1 is true if exp1 is false, and it’s false if exp1 is true.
&& 和 || 都是序列点.
因此, 一旦发现某个元素使得整个表达式无效, 将会立刻停止求值而不会继续向后计算.
例如, 语句:
while ((c = getchar()) != ' ' && c != '\n')
读取字符直到遇到第一个空格或者换行符, 第一个子表达式将读取到的字符赋值给 c. 后面的子表达式还会用到 c. 如果没有求值顺序的保证, 编译器可能会在给 c 赋值之前先对后面的表达式求值.
再例如, 语句:
if (num != 0 && 12 / num)
如果 num 为 0, 则第一个子表达式为假, 则整个表达式为假, 则不再继续对第二个子表达式求值, 这样可以避免把 0 作为除数.
逻辑表达式从左向右求值, 一旦算到了某个子表达式时, 发现整个表达式的值为真或假的结果能得到了, 则停止计算, 因此 && 和 || 都是短求值.
&& 可以用于范围测试, 例如, 语句:
if (num > 90 && num < 100) // 测试 num 是否大于 90 且小于 100
再例如, 语句:
if (c >= 'a' && c <= 'z') // 判断 c 是否是一个小写字母 { printf("%c is a lowercase character.\n", c); }
上述代码只适用于相邻字母与相邻数字一一对应的字符编码, 例如 ASCII.
一种可移植的方法为:
if (islower(c)) // 判断 c 是否是一个小写字母 { printf("%c is a lowercase character.\n", c); }
无论使用哪种字符编码, islower() 函数都能正常执行.
逗号运算符
逗号运算符最常用在 for 循环的循环头中.
程序示例:
#include<stdio.h> #define FIRST_OZ 46 #define NEXT_OZ 20 int main(void) { int ounces; float cost; printf("ounces cost\n"); for (ounces = 1, cost = FIRST_OZ; ounces <= 16; ounces++, cost += NEXT_OZ) { printf("%3d $%5.2f\n", ounces, cost); } return 0; }
结果:
ounces cost 1 $46.00 2 $66.00 3 $86.00 4 $106.00 5 $126.00 6 $146.00 7 $166.00 8 $186.00 9 $206.00 10 $226.00 11 $246.00 12 $266.00 13 $286.00 14 $306.00 15 $326.00 16 $346.00
逗号运算符是一个序列点。
整个逗号表达式的值是右侧项的值.
例如下面这条语句:
x = (y = 3, (z = ++y + 2) + 5);
先将 3 赋值给 y, 再递增 y 得到 y 等于 4, 再加 2, 得到 6, 赋值给 z, 再加 5 得到 11, 最终将 11 赋值给 x.
下面这条语句:
x = 7, 10;
被视为一个逗号表达式语句, 逗号左侧是一个赋值表达式, x 的值为 7. 逗号右侧是 10.
这和下面这两条语句等价:
x = 7; 10;
任何表达式后面加上分号就得到了一个语句, 因此 10; 也是一个语句, 但是什么也不做.
下面的语句:
x = (100, 200);
是将 200 赋值给 x.
逗号不一定都是逗号运算符,也可能是分隔符。
下面的语句中,逗号都是分隔符。
int a = 10, b = 9; printf("a = %d b = %d\n", a, b);
sizeof 运算符
sizeof 运算符是 C 的内置运算符, 作用于一个具体的标识符或一个数据类型关键字, 返回以字节表示的该运算对象占用的内存空间.
作用于具体的标识符时可以不加括号, 作用于数据类型关键字时必须加括号.
C 标准只规定 sizeof 返回一个无符号整型, 在不同的实现中可能是 unsigned int 或 unsigned long 类型, 因此用 printf() 输出时对应的转换说明可能是 %ud 或 %lu, 要根据具体实现来定, 这严重影响了编程效率和代码的可移植性.
C 提供了可移植性更好的 size_t 类型作为 sizeof 的返回值类型. size_t 不是一个新的类型, 而是使用 typedef 机制定义的类型. 在头文件 stddef.h (在包含 stdio.h 头文件时已将其包含在其中)中, 把 size_t 定义为 sizeof 的返回值类型, 这被称为底层类型 (underlying type), 同时规定 printf() 使用 z 修饰符表示打印 size_t 类型.
头文件中使用 typedef 将 size_t 作为 unsigned int 或 unsigned long 的别名. 在使用 size_t 时编译器会自动根据不同系统进行替换.
C99 新增 %zd 作为 printf() 显示 size_t 类型的值, 如果不支持可以用 %u 或 %lu. 具体是换成哪一种应根据具体的实现来确定.
C99 以前没有 %zd,但是有 size_t。
整个 sizeof 表达式被视为整型常量.
sizeof 和 strlen 的比较:
// sizeof和strlen的比较 // strlen()函数的返回值和sizeof的结果都用%zd作为转换说明 #include<stdio.h> #include<string.h> #define GREET "hello" int main(void) { char name[10]; printf("Enter your name: "); scanf("%s", name); // 给字符数组赋值 printf("Your name is %s.\n", name); // 输出字符数组 // 字符数组 printf("name array occupies %zd bytes.\n", sizeof name); // 打印字符数组占用的字节数 printf("name string has %zd characters.\n", strlen(name)); // 打印字符串所含有的字符个数, 不包括最后的空字符 // define定义的符号常量 printf("GREET string occupies %zd bytes.\n", sizeof(GREET)); // sizeof计算所有的字节数,所以包括字符串最后的空字符 printf("GREET string has %zd characters.\n", strlen(GREET)); // strlen()不包括最后的空字符 // 字符串常量 printf("GREET string occupies %zd bytes.\n", sizeof("Hello")); // sizeof计算所有的字节数,所以包括字符串最后的空字符 printf("GREET string has %zd characters.\n", strlen("Hello")); // strlen()不包括最后的空字符 return 0; }
结果:
Enter your name: xiao Your name is xiao. name array occupies 10 bytes. name string has 4 characters. GREET string occupies 6 bytes. GREET string has 5 characters. GREET string occupies 6 bytes. GREET string has 5 characters.
程序示例:
// sizeof的一个示例 size_t int_size; // 定义一个size_t类型的变量 int_size = sizeof(int); printf("int has %zd bytes.\n" , int_size); // 打印一个size_t类型的变量
结果:
int has 4 bytes.
程序示例;
// 打印一些数据类型的长度 #include<stdio.h> #include<complex.h> #include<limits.h> #include<float.h> int main(void) { printf("以字节为单位,打印一些数据类型的长度:\n"); putchar('\n'); printf("_Bool: %zd\n" , sizeof(_Bool)); putchar('\n'); int * p; printf("int * : %zd\n" , sizeof(p)); printf("char * : %zd\n" , sizeof(char *)); printf("指针变量本身占用%zd个字节\n" , sizeof(p)); putchar('\n'); printf("char: %zd\n" , sizeof(char)); printf("int: %zd\n" , sizeof(int)); printf("short: %zd\n" , sizeof(short)); printf("long: %zd\n" , sizeof(long)); printf("long long: %zd\n" , sizeof(long long)); putchar('\n'); printf("unsigned char: %zd\n" , sizeof(unsigned char)); printf("unsigned int: %zd\n" , sizeof(unsigned int)); printf("unsigned short: %zd\n" , sizeof(unsigned short)); printf("unsigned long: %zd\n" , sizeof(unsigned long)); printf("unsigned long long: %zd\n" , sizeof(unsigned long long)); printf("有符号和无符号占用的字节数是不变的.\n"); putchar('\n'); printf("float: %zd\n" , sizeof(float)); printf("double: %zd\n" , sizeof(double)); printf("long double: %zd\n" , sizeof(long double)); putchar('\n'); printf("float complex: %zd\n" , sizeof(float complex)); printf("double complex: %zd\n" , sizeof(double complex)); printf("long double complex: %zd\n" , sizeof(long double complex)); putchar('\n'); // printf("float imaginary: %zd\n" , sizeof(float imaginary)); // printf("double imaginary: %zd\n" , sizeof(double imaginary)); // printf("long double imaginary: %zd\n" , sizeof(long double imaginary)); putchar('\n'); printf("int max = %d\n" , INT_MAX); printf("int min = %d\n" , INT_MIN); putchar('\n'); printf("float max = %f\n" , FLT_MAX); putchar('\n'); return 0; }
结果:
以字节为单位,打印一些数据类型的长度: _Bool: 1 int * : 8 char * : 8 指针变量本身占用8个字节 char: 1 int: 4 short: 2 long: 4 long long: 8 unsigned char: 1 unsigned int: 4 unsigned short: 2 unsigned long: 4 unsigned long long: 8 有符号和无符号占用的字节数是不变的. float: 4 double: 8 long double: 16 float complex: 8 double complex: 16 long double complex: 32 int max = 2147483647 int min = -2147483648 float max = 340282346638528860000000000000000000000.000000
sizeof 内的表达式不进行计算。代码示例:
#include<stdio.h> int main(void) { short a = 0; int b = 10; printf("%d\n", sizeof(a = a + b)); printf("%d\n", sizeof(short)); printf("%d\n", sizeof(int)); printf("%d\n", a); return 0; }
结果:
2 2 4 0
一个表达式有两个属性, 即类型属性和值属性, 类型属性可以推算出来, 不一定非要求值.
运算符的优先级
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
---|---|---|---|---|---|
1 | [] |
数组下标 | 数组名[常量表达式] | ||
() |
圆括号 | (表达式) 函数名(形参表) | |||
. | 成员选择(对象) | 对象.成员名 | |||
-> | 成员选择(指针) | 对象指针->成员名 | |||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
(类型) | 强制类型转换 | (数据类型)表达式 | |||
++ | 自增运算符 | ++变量名 变量名++ | 单目运算符 | ||
-- | 自减运算符 | --变量名 变量名-- | 单目运算符 | ||
* | 取值运算符 | *指针变量 | 单目运算符 | ||
& | 取地址运算符 | &变量名 | 单目运算符 | ||
! | 逻辑非运算符 | !表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~表达式 | 单目运算符 | ||
sizeof | 长度运算符 | sizeof(表达式) | |||
3 | / | 除 | 表达式 / 表达式 | 双目运算符 | |
* | 乘 | 表达式*表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式%整型表达式 | 双目运算符 | ||
4 | + | 加 | 表达式+表达式 | 双目运算符 | |
- | 减 | 表达式-表达式 | 双目运算符 | ||
5 | << | 左移 | 变量<<表达式 | 双目运算符 | |
>> | 右移 | 变量>>表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式>表达式 | 双目运算符 | |
>= | 大于等于 | 表达式>=表达式 | 双目运算符 | ||
< | 小于 | 表达式<表达式 | 双目运算符 | ||
<= | 小于等于 | 表达式<=表达式 | 双目运算符 | ||
7 | == | 等于 | 表达式==表达式 | 双目运算符 | |
!= | 不等于 | 表达式!= 表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式&表达式 | 双目运算符 | |
9 | ^ | 按位异或 | 表达式^表达式 | 双目运算符 | |
10 | | | 按位或 | 表达式|表达式 | 双目运算符 | |
11 | && | 逻辑与 | 表达式&&表达式 | 双目运算符 | |
12 | || | 逻辑或 | 表达式||表达式 | 双目运算符 | |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 基本赋值运算符 | 变量=表达式 | 右到左 | |
/= | 除后赋值 | 变量/=表达式 | |||
*= | 乘后赋值 | 变量*=表达式 | |||
%= | 取模后赋值 | 变量%=表达式 | |||
+= | 加后赋值 | 变量+=表达式 | |||
-= | 减后赋值 | 变量-=表达式 | |||
<<= | 左移后赋值 | 变量<<=表达式 | |||
>>= | 右移后赋值 | 变量>>=表达式 | |||
&= | 按位与后赋值 | 变量&=表达式 | |||
^= | 按位异或后赋值 | 变量^=表达式 | |||
|= | 按位或后赋值 | 变量|=表达式 | |||
15 | , | 逗号运算符 | 表达式,表达式,… |
规律:
-
结合方向只有三个是从右往左, 其余都是从左往右. 其中扩展的赋值运算符的结合方向和基本赋值运算符的结合方向相同.
-
所有双目运算符中只有赋值运算符的结合方向是从右往左.
-
另外两个从右往左结合的运算符也很好记:一个是单目运算符中的负号运算符, 一个是三目运算符即条件运算符.
-
C 语言中有且只有一个三目运算符, 即条件运算符.
-
逗号运算符的优先级最低.
-
逻辑非
!
> 算术运算符 > 关系运算符 > 逻辑与&&
和逻辑或||
> 赋值运算符
运算符的优先级为表达式中的求值顺序提供了重要的依据, 但没有规定所有的顺序.
例如,未规定的情况:
#include <stdio.h> int main(void) { int a = 3 * 8 + 9 * 7; int b = (2 + 5) * 6 + (-3 + 5); return 0; }
上述代码的3 * 8 + 9 * 7
中,先算3*8
还是先算9*7
没有规定。同样,(2 + 5) * 6 + (-3 + 5)
的两个括号谁先谁后算也没有规定。语言的实现者可以根据具体的硬件选择效率最高的计算方式.
a = 6 * 5 + 4 * 3;
再例如:
while (num < 21) { printf("%10d %10d\n", num, num * num++); }
在执行 printf()
语句时, 因为 num
和 num * num++
都是表达式, 会先对两个表达式求值, 再将表达式的值替换格式字符串中的转换说明. 但是具体是先求 num
的值还是先求 num * num++
的值并没有统一的规定, 不同操作系统或不同编译器或不同硬件可能先后顺序不同.
再例如:
n = 3; y = n++ + n++;
对 y 求值时, 编译器可能使用两次 3, 然后将 n 递增两次. 也可能先使用 n 的原值, 再递增一次 n, 再使用 n 的新值, 再递增一次 n.
虽然最后都会递增两次 n, 但是 y 的值会不一样. 此时的结果是未定义的, 即 C 标准没有规定结果一定是什么.
表达式
表达式由运算符和运算对象组成。
最简单的表达式是一个单独的运算对象,以此为基础可以建立复杂的表达式。
一些表达式由子表达式组成。子表达式即较小的表达式。
这些都是一些表达式:
-4 a = b + c
每个表达式都有一个值。
语句
语句是 C 程序的基本构建块,一条语句相当于一条完整的计算机指令。
语句要以分号结束, 分号可以告诉编译器一条语句在哪里结束, 下一条语句在哪里开始.
C 将所有加了分号的表达式都看成一条语句, 即表达式语句.
a = 5 // 表达式 a = 5; // 语句 6; // 语句 3+6; // 语句
后两条也是语句, 但是什么也不做, 不算是真正有用的语句.
最简单的 C 语句是空语句, 即一个单独的分号.
; // 空语句
空语句什么也不做, 常作为占位符.
一条语句是一个完整的指令, 但指令不一定是语句.
int a , b; a = b = 1; x = 1 + a + (y = b);
子表达式 y = b
是一条完整的指令, 但不是语句, 它只是语句的一部分.
C 语言中声明不是语句, 而 C++ 中声明是语句.
函数调用和赋值都是表达式, 即函数调用表达式和赋值表达式. 加上分号就变成了函数调用表达式语句和赋值表达式语句.
语句分类:根据 C 标准, C 语言有 6 种语句.
-
表达式语句:在各种表达式后面加上分号就构成表达式语句.
-
选择语句:if、switch
-
迭代语句:while、for、do while
-
复合语句:用
{
和}
包含起来的一条或多条语句. 复合语句也称为块. 复合语句被视为一条语句.不是这种复合语句的语句则为简单语句, 简单语句用一个分号结尾. -
标号语句
-
跳转语句:return、break、continue、goto
副作用
副作用是指对数据对象或文件的修改.
对于表达式来说, 主要作用是求表达式的值, 求值之外产生的其他所有效果均为副作用.
如函数调用表达式 printf("hello\n");
求出显示的字符的个数是主要作用, 显示字符是副作用;赋值表达式语句 a = 9;
主要作用是求表达式的值, 副作用是改变 a
的值, 即给 a
赋值;再如递减运算符 a--;
副作用是将 a
的值减 1.
程序示例:
#include<stdio.h> int main(void) { int a = 10; printf("a++ = %d\n", a++); printf("a = %d\n", a); a = 10; printf("++a = %d\n", ++a); printf("a = %d\n", a); return 0; }
程序结果:
a++ = 10 a = 11 ++a = 11 a = 11
a++
和 ++a
的副作用都是将a的值加一,但 a++
这个表达式本身的值是 a 递增前的值,++a
这个表达式本身的值是 a 递增后的值。
序列点
序列点是执行程序的点, 在该点上, 所有的副作用都在进入下一步之前发生.
序列点有: 完整表达式, 表示语句结束的分号, 逻辑运算符 && 和 ||, 逗号运算符.
程序执行到序列点时, 序列点之前的所有副作用全部发生, 然后接着执行序列点之后的程序.
完整表达式指: 该表达式不是另一个更大的表达式的子表达式.
完整表达式有: 表达式语句中的表达式, while 循环中作为测试条件的表达式, for 循环的圆括号内的每一个表达式.
下面的表达式是错误的:
y = (4 + x++) + (6 + x++);
这里 (4 + x++)
和 (6 + x++)
是子表达式. 无法确定是先计算哪个子表达式, 也无法确定何时递增 x. 因此是错误的.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术