6,102
社区成员
发帖
与我相关
我的任务
分享【高质量C/C++编程】—— 4. 表达式和基本语句
以下运算符的优先级是从高到低排列,结合性除了表中给出的的三个从右到左,其余都是从左到右。对这些运算符优先级熟记是比较困难的,我们只需要有一个大概的认识即可(如一元运算符优先级普遍较高,赋值运算符优先级最低等),毕竟我们的目的只是写出正确且易读的代码
运算符结合性聚合运算符()、[]、->、.一元运算符!、~、++、--、(类型)、sizeof、+(正)、-(负)、*(解引用)、&(取地址)右到左算术运算符*(乘)、/、%``+(加)、-(减)移位运算符<<、>>比较运算符<、<=、>、>=``==、!=位运算符&(按位与)``^(异或)``|(按位或)逻辑运算符&&``||右到左三目运算符? :右到左赋值运算符=、+=、-=、*=、/=、%=、&=、^=、|=、<<=、>>=
规则:
if (a | b && a & c); // 不良的风格
if ((a | b) && (a & c)); // 良好的风格
如a = b = c = 0这样的表达式称为复合表达式。
复合表达式存在的理由:
规则:
i = a >= b && c < d && c + f <= g + h; // 不良的风格,过于复杂的表达式
d = (a = b + c) + r; // 不良的风格,多用途的表达式
if (a < b < c); // 错误的代码,使用了数学表达式
// 问题表达式
/* 结果是不可预测的,当i--运算后可能被加载进寄存器中,后面的--i和++i未必能在运算前修改到i的值 */
ret = i-- - --i * (i = -3) * i++ + ++i;
/* 根据优先级--c先执行,但在--c执行之前左边的c被存储到寄存器中,和--c执行后左边的c还是原值,导致运算结果出现错误 */
ret = c + --c;
if语句是C/C++中最常用的语句,但是有很多程序员都在用隐含错误方式写它,现在让我们来学习它。
true、false或者0、1进行比较,而是以变量或!运算的结果表示真假// 良好的风格
if (flag); // flag表示真
if (!flag); // flag表示假
// 不良的风格
if (flag == true);
if (flag == false);
if (flag == 1);
if (flag == 0);
==或!=与0比较// 良好的风格
if (value == 0);
if (value != 0);
// 不良的风格
if (value); // 容易让人误解为布尔值
if (!value);
==或!=与任何数字比较,它们有精度限制,应该转化为>=或<=形式来消除他们的误差// 良好的风格
if ((x >= -EPSINON) && (x <= EPSINON)); // EPSINON是允许的误差
// 不良的风格
if (x == 0.0); // 由于浮点数底数数位为0时,指数位是任意值都表示0.0,所以可能出现由于指数不同导致的0.0不等于0.0
if (x != 0.0);
==或!=与NULL比较+ 在C++中,指针变量用==或!=与nullptr比较。C++中的NULL被声明为数值0和指针0,优先使用数值0,所以我们尽量不在C++中使用NULL// 在C语言中良好,在C++中不好的风格
if (p == NULL);
if (p != NULL);
// C++中良好的风格
if (p == nullptr);
if (p != nullptr);
// C语言和C++中都不良的风格
if (p);
if (!p);
if (p == 0);
if (p != 0);
规则:
if (p == NULL);
if (p = NULL); // 该语句漏掉一个=,但是编译器不会报错
if (NULL == p); // 将常量前置,与p==NULL没有区别
if (NULL = p); // 漏掉一个=,此时编译器报错,问题可以及时解决
if/else/return的组合要加上大括号或直接使用三目运算表达式// 不良的风格
if (condition)
return x;
return y;
// 良好的风格
if (condition)
{
return x;
}
else
{
return y;
}
// 或者写为更简练的三目表达式
return (condition ? x : y);
在C++/C循环语句中,for循环的使用频率最高,while语句其次,do很少用。提高循环体效率的基本办法是降低循环体复杂性。
规则:
// 效率低,长循环在外层
for (row=0; row<100; row++)
{
for (col=0; col<5; col++)
{
sum += arr[row][col];
}
}
// 效率高,长循环在内层
for (col=0; col<5; col++)
{
for (row=0; row<100; row++)
{
sum += arr[row][col];
}
}
// 效率低,但简洁
for (i=0; i<N; i++)
{
if (condition)
{
DoSomething();
}
else
{
DoOtherSomething();
}
}
// 效率高,但不简洁
if (condition)
{
for (i=0; i<N; i++)
{
DoSomething();
}
}
else
{
for (i=0; i<N; i++)
{
DoOtherSomething();
}
}
// 不能移到循环体外的逻辑判断
for (i=0; i<N; i++)
{
// 逻辑判断的结果受循环影响
if (i % 2 == 0)
{
printf("偶数 : %d\n", i);
}
else
{
printf("奇数 : %d\n", i);
}
}
规则:
for循环体内改变循环变量,防止for循环失去控制。+ 建议for语句的循环控制变量取值采用“半开半闭区间”写法// 不良的风格,在for循环体内改变循环变量,建议以下程序的逻辑使用while循环完成
for (i=0; i<100; i++)
{
if (i % 2 == 0)
{
i += 2;
}
else
{
i += 3;
}
}
for (i = 0; i < N; i++); // 半开半闭区间写法
for (i = 0; i <= N-1; i++); // 闭区间写法
switch语句是多分支选择语句,而if语句只有2个分支可选,虽然可以使用嵌套的if来选择多分支实现,但是程序冗长难读,所以switch语句的出现很有必要
规则:
case语句的结尾都要加上break,否则将导致多个分支重叠(除非有意让分支重叠)+ 不要忘记加上default分支,即使程序根本不需要default处理,也应该保留default: break;语句。防止别人误以为你忘记default处理switch (variable)
{
case value1:
... ...
break;
case value2:
... ...
break;
... ...
default:
break;
}
自从提倡结构化设计以来,goto语句就成了有争议的语句:
goto语句跳转灵活,如果不加限制,会破坏结构化设计风格+ goto语句经常带来错误隐患,可能跳过某些类的构造、变量的初始化、重要的计算等。规则:
goto语句目前的用途是从多重循环中跳出,不用写很多break语句+ 我们对于goto语句的态度应该是慎用,而不是禁用for (i=0; i<N; i++)
{
while (condition1)
{
do
{
if (condition3)
{
goto jump; // 跳出到jump处
}
}
while (condition2);
}
}
jump: // jump处
... ...
文章来源:https://blog.csdn.net/weixin_52811588/article/details/127112165
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA 知识共享协议,转载请附上原文出处链接和本声明。