C语言的两个附加部分

在这些笔记中,我将带你通过C语言的两个重要部分,我们还没有机会详细讨论。这些笔记的第一部分将介绍C预处理器。这些注释的第二部分将介绍位操作符。

除了它们本身很重要之外,我现在讨论这些话题是因为它们将在我下周一的讲座中展示的例子中发挥重要作用。

C预处理器

编译一个C源代码文件是一个两阶段的过程。这个过程的第一步是通过一个叫做C预处理器的特殊程序运行源代码文件。然后,预处理器的输出通过C编译器本身运行。

预处理器是一个文本替换程序。预处理器的工作是查找嵌入在源代码中的特殊预处理器指令。这些指令很容易识别:它们都以#字符开头。

包含、定义和if/else结构

最熟悉的预处理器指令是#include命令。此命令的目的是在源代码文件中包含头文件的文本。

当预处理器遇到#include指令时,例如

# include < stdio . h >

它定位有问题的头文件,并将其全部内容复制到源代码文件中,以取代#include语句。

另一个熟悉的指令是#define命令,它用于定义程序中使用的值。举个例子

#define ARRAY_SIZE 100

预处理器使用#define命令执行文本替换。在遇到上面示例中的#define之后,预处理程序将源代码文件中的文本ARRAY_SIZE替换为值100。

#define有时与#ifdef结合使用。#其他. .# endif构造。这里有一个例子。

#ifdef ARRAY_SIZE int A[ARRAY_SIZE];#else int A[20];# endif

此构造的目的是选择性地在源代码文件中包含代码。在上面的例子中,如果定义了符号ARRAY_SIZE,预处理器将在源代码文件中保留A的第一个声明,并去掉第二个声明。如果没有定义ARRAY_SIZE,预处理器将去掉A的第一个声明,代之以第二个声明。

另一种预处理器指令是宏。宏的目的是设置一些看起来和行为有点像函数声明的东西。

这里有一个例子。下面的#define设置了一个宏,它定义了一个类似于平方函数的东西。

#定义SQ(x) x*x

这定义了宏SQ,它计算其参数的平方。宏通过文本替换机制操作。一旦定义了宏SQ,预处理器将在程序中查找任何看起来像SQ( )的文本,并将其替换为文本 *

因为宏实际上是一种文本替换机制,所以在设置和使用宏时必须谨慎一些。下面是一个可能出错的例子。假设你的程序中有这样的代码:

if(SQ(x+1) > 4)

当预处理器遇到这一行时,它将执行SQ定义中设置的文本替换,生成如下所示的内容:

If (x+1*x+1 > 4)

这个结果可能不是您想要的。因为宏是一种文本替换机制,而不是函数定义,所以它会搞砸像这样的例子。

解决这个问题的方法是在原始宏定义中使用一些防御性括号。如果我们把定义改成

#define SQ(x) (x)*(x)

预处理器将进行翻译

if(SQ(x+1) > 4)

If ((x+1)*(x+1) > 4)

这是正确的。

C中的位操作

我将在下周一展示的示例程序需要做一些位操作。对于那些从未在C中做过位操作的人来说,这里有一个关于完成此操作所需的C语言功能的简短教程。

十六进制数字系统

首先你需要知道的是如何表示比特序列。在C语言中最广泛使用的表示位序列的方法是使用十六进制数。十六进制数字系统是一个以16为基数的数字系统,其中每个数字代表一个4位的序列。这是一个十六进制数字系统中的数字表,以及每个数字的二进制等效值。

数字 二进制
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
一个 1010
b 1011
c 1100
d 1101
e 1110
f 1111

C语言允许使用0x表示法编写十六进制数。例如,0xa表示十六进制数a,其二进制等效值为1010。

您可以通过使用更多的十六进制数字来创建更长的位序列。例如,如果您需要表示位序列

1000010011001110

你首先把它分成四组

1000 0100 1100 1110

然后使用上面的表将每个位序列转换为其等效的十六进制数字。

0 x84ce

C位运算符

一旦您设置了位序列,您将希望能够访问和操作位序列中的单个位。为此,C语言提供了三位操作符&、|、^和~。

~运算符是按位求反运算符,它将序列中的每一位翻转到它的相反位。例如,如果一个无符号字符变量x包含0xb = 1011,那么~x的计算结果为0100,即0x8。

&运算符是位逻辑与运算符,它构成从两个数字中取出的位对的逻辑与。例如,如果我们有两个unsigned char变量x和y,其中包含值0xa = 1010和0x6 = 0110,则x&y的计算结果为(1010)&(0110)= (0010)= 0x2。

类似地,|操作符形成按位或。X b| y的值为(1010)|(0110)= (1110)= 0xe。

^运算符形成位的异或。如果两个位相同,其互斥值为0,如果不相同,则为1。X ^y等于(1010)^(0110)= (1100)= 0xc。

处理单个比特

这些位操作符用于执行位级操作,例如检查单个位的值或操作位序列中的单个位。

要测试数字中特定位的值,可以使用按位运算符&。例如,为了确定一个数字的最后一位是0还是1,我们用一个特殊构造的掩码组成该数字的按位和。在这种情况下,我们将使用的掩码数是除最后一个位置的1外赢博体育数字都为0的数字。例如,如果我们有一个值为0xa8 = 10101000的无符号短变量x,并且我们想要确定最后一位是否为0,我们将使用测试

X&0x01 == 0

我们可以通过改变x的掩码来访问数字中的其他位。例如,要检查第三位是否为1,我们会使用

X&0x20 == 0x20

要将数字中的位设置为特定的值,我们可以使用|或&,这取决于我们想将该位设置为1还是0。例如,要将8位变量y中的第4位设置为1,我们将使用

Y = Y |0x10

要将y的第四位设置为0,我们可以使用&和掩码0xf = 11101111:

Y = Y &0xef

另一种方法是使用~操作符来帮助构造掩码:

Y = Y &(~0x10)