IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 《C陷阱与缺陷》第二章【语法陷阱】下 -> 正文阅读

[C++知识库]《C陷阱与缺陷》第二章【语法陷阱】下

?目录

??? 一、switch语句

??? 二、函数调用

?? 三、“悬挂” else 引发的问题

?? 四、练习题?

?练习1?

?练习2


?

?? 一、switch语句

先看下面的例子,两端程序代码分别使用C语言和Pascal语言编写

switch(color)
{
   case 1:
        printf("red");
        break;
   case 2:
        printf("yellow");
        break;
   case 3:
        printf("blue");
        break;
}
case color of
1:      write('red');
2:      write('yellow');
3:      write('blue');
end

? ? ? ? 两段程序代码要完成的是同样的任务:根据变量 color 的值(1、2或3),分别打印出 red yellowblue 。两段程序代码非常相似,只有一种例外情形:那就是用 Pascal 语言编写的程序段中每个 case 部分并没有与C语言的 break 语句对应的部分。之所以会这样,是因为C语言中把 case 标号当作真正意义上的标号,因此程序的控制流程会径直通过 case 标号,而不会受到任何影响。而在 Pascal 语言中,每个 case 标号都隐含地结束了前一个 case 部分。

??注意:虽然?Pascal 语言没有break语句,但也具有相当于C语言break的作用。

如果我们使用C语言编写程序的时候省略了break,就会带来一下结果。代码如下:

switch(color)
{
   case 1:
        printf("red");
   case 2:
        printf("yellow");
   case 3:
        printf("blue");
}

假设 color 的值是2,最后,程序将会打印出:yellowblue

? ? ? ? 因为程序的控制流程在执行了第二个 printf 函数的调用之后,会自然而然的顺序执行下去所以第三个 printf 函数调用也会被执行。

这是C语言 switch 语句的一大特性,它既有优势也有弱点

弱点:程序员很容易遗漏各个 case 部分的 break 语句,造成一些难以理解的程序行为。

优势:如果程序员有意省略一个 break 语句,则可以表达出一些采用其他方式很难方便地加以实现的程序控制结构。

? ? ? ?特别是对于一些较大的 switch 语句,我们常常会发现各个分支的处理大同小异:对于某个分支情况的处理只要稍作改动,剩余部分就完全等同于另一个分支情况下的处理。

例如:

? ? ? ?考虑这样一个程序,它是某种假想的计算机的解释器(相当于虚拟机)。这个程序中有一个 switch 语句,用来处理每个不同的操作码。在这种假想的计算机上,只要将第二个操作数的正负号反号后,减法运算和加法运算的处理本质上就是一样的。因此,如果我们可以像下面这样写代码,无疑会大大方便程序的处理:

case SUBTRACT:
         opnd2 = -opnd2;
         /*次处没有 break 语句*/
case ADD:
         ...

? ? ? ?当然,像上面的例子那样添加适当的程序注释是一种不错的做法。如果其他人阅读到这段代码,就能够了解到此处是有意省去了一个 break 语句。

? ? ? ? 再看另一个例子。考虑这样一段代码,它的作用是一个编译器在查找符号时跳过程序中的空白字符。这里,空格键、制表符和换行符的处理都是相同的,不过在遇到换行符时,程序的代码行计数器需要进行递增:

case '\n':
        linecount++;
        /*次出没有 break 语句*/
case '\t':
case ' ':
       ...

代码示例:

#include<stdio.h>

int main()
{
	int a = 1;
	int b = a++;
	switch (b)
	{
	case 1:
		printf("a = %d\n", a);
		break;
	case 2:
		printf("b = %d\n", b);
		break;
	}
	return 0;
}

当前代码的含义是根据 b?的值来进入对应的 case 部分,代码最终会打印:a = 2

如果我们把 break 语句省略掉之后,代码最终会打印:?

a = 2

b = 1


?

??? 二、函数调用

这里涉及到函数调用操作符(),如果 f 是一个函数,那么

f();

是一个函数调用语句,而

f;

却是一个什么都不做的语句。更精确的说,这个语句计算的函数 f 的地址,却并不调用该函数。

代码示例:

#include<stdio.h>

int f(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	int a = 10;
	int b = 20;
	int ret = f(a, b);
	printf("%d\n", ret);
}

当前代码输出的结果是函数的返回值,也就是a、b其中的最大值

如果将函数调用操作符去掉:

#include<stdio.h>

int f(int a, int b)
{
	return a > b ? a : b;
}

int main()
{
	int a = 10;
	int b = 20;
	int ret = f;
	printf("%d\n", ret);
}

代码并没有调用f()函数,也就不会得到最大值。

?? 三、“悬挂” else 引发的问题

考虑下面的程序片断:

if (x == 0)
         if (y == 0) error();
else
{
         z = x + y;
         f(&z);
}

当前代码的本意应该主要有两种情况:即?x 等于?0?以及 x?不等于?0?。

x 等于 0?:除非 y?也等于?0 (此时调用函数 error?),否则程序不作任何处理。

x 不等于 0:程序首先将?x y?之和赋值给?z?,然后以?z?的地址为参数来调用函数 f?。

然而当前代码的实际含义却是:

if (x == 0)
{
    if (y == 0)
    {
        error();
    }
    else 
    {
        z = x + y;
        f(&z);
    }
}

先判断 x 是不是等于 0 ,再判断 y 是不是等于 0 ;若 y 等于 0 调用 error 函数;若 y 不等于 0 ,先将 x y 的和赋给 z ,再调用 f 函数。

??注意:发生这样的情况原因在于C语言中规定,else 始终与同一对括号内最近的未匹配的 if 结合

上面代码的本意应该这样写:

if (x == 0)
{
   if (y == 0)
   {
      error();
   }
}
else
{
    z = x + y;
    f(&z);
}

??注意:最好使用圆括号括起来,因为这样显示的更加明显。

代码示例:

#include<stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	if (a > 5)
		if (b < 10)
			printf("hello\n");
	else
		printf("haha\n");
	return 0;
}

看似当前代码含义是先判断 a 是不是大于 5 ,若不大于 5 ,输出 haha ;若大于 5 再判断 b 是不是小于 10 ,若小于 10 ,输出 hello ;若不小于。就什么都不做。

然而实际含义却是:

#include<stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	if (a > 5)
	{
		if (b < 10)
		{
			printf("hello\n");
		}
		else
		{
			printf("haha\n");
		}
	}
	return 0;
}

先判断 a 是不是大于 5 ,若不大于 5 ,就什么都不做?;若大于 5 ,再判断 b 是不是小于 10 ,若小于 10 ,输出 hello ;若不小于。就输出 haha

?? 四、练习题?

?练习1?

C语言允许初始化列表中出现多余的逗号,例如:

int?days[]?=?(?31,?28,?31,30,31,30,
31,31,30,31,30,31,);

为什么这种特性是有用的?

?练习2

? ? ? ?前面指出了在C语言中以分号作为语句结束的标志所带来的一些问题。虽然我们现在考虑改变C语言的这个规定已经太迟,但是设想一下是否这有其他办法来分隔语句却是一件饶有趣味的事情。其他语言中是如何分隔语句自呢?这些方法是否也存在它们固有的缺陷呢?

??

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-18 17:26:01  更:2022-05-18 17:26:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 15:49:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码