《C编程.开始C》7.操作员和类型铸造

操作员和作业

C拥有广泛的操作符,使简单的数学运算易于处理。按优先级分组的运算符列表如下:

主要表达

标识符(或变量名称)是主表达式,前提是它已被声明为指定一个对象(在这种情况下它是一个左值[一个可以用作赋值表达式左侧的值])或一个函数(在这种情况下,它是一个功能指示符)。

常量是主要表达式。它的类型取决于它的形式和价值。常量的类型是字符常量(例如,' '是空格),整数常量(例如2),浮点常量(例如0.5),以及先前通过定义的枚举常量enum

字符串文字是主要表达式。

带括号的表达式是主表达式。它的类型和值是非括号表达式的类型和值。

在C11中,以_Generic开头的表达式后跟(,初始表达式,表单类型的值列表:表达式,其中type是命名类型或关键字default,和)构成主表达式。该值是表达式,它遵循初始表达式的类型或默认值(如果未找到)。

后缀运算符

首先,主表达式也是后缀表达式。以下表达式也是后缀表达式:

后缀表达式后跟左方括号([),表达式和右方括号(])构成数组下标运算符的调用。其中一个表达式应具有“指向对象类型的指针”,另一个应具有整数类型; 结果类型是类型。连续数组下标运算符指定多维数组的元素。

后缀表达式后跟括号或可选的带括号的参数列表表示调用函数调用操作符。函数调用运算符的值是使用提供的参数调用的函数的返回值。函数的参数按值复制到堆栈上(或者至少编译器就像发生的那样;如果程序员希望通过引用复制参数,那么更容易将区域的地址传递给通过值修改,然后被调用的函数可以通过相应的指针访问该区域。编译器的趋势是将参数从右向左传递到堆栈。

后缀表达式后跟一个点(.)后跟一个标识符,从结构或联合中选择一个成员; 后缀表达式后跟一个箭头(->)后跟一个标识符,从结构或联合中选择一个成员,该成员由表达式左侧的指针指向。

后缀表达式后跟增量或减量运算符(++--)表示变量将作为副作用递增或递减。表达式的值是递增或递减之前的后缀表达式的值。这些运算符仅适用于整数和指针。

一元表达

首先,后缀表达式是一元表达式。以下表达式都是一元表达式:

递增或递减运算符后跟一元表达式是一元表达式。表达式的值是增量或减量的一元表达式的值。这些运算符仅适用于整数和指针。

以下运算符后跟一个强制转换表达式是一元表达式:

运算符含义
======== =======
   和地址; value是操作数的位置
   *内容 - ; value是存储在该位置的值
   - 否定
   +运算符的值
   !逻辑否定((!E)相当于(0 == E))
   〜按位补充

sizeof后跟一元表达式的关键字是一元表达式。该值是表达式类型的大小(以字节为单位)。表达式未评估。

sizeof后跟带括号的类型名称的关键字是一元表达式。该值是以字节为单位的类型的大小。

演员

强制转换表达式是一元表达式。

带括号的类型名称后跟一个强制转换表达式是一个强制转换表达式。带括号的类型名称具有强制转换表达式为括号中类型名称指定的类型的效果。对于算术类型,这不会更改表达式的值,或者如果表达式是整数并且新类型小于先前类型,则截断表达式的值。

将float转换为int的示例:

float  pi  =  3.141592 ; 
int  truncated_pi  =  (int )pi ;  // truncated_pi == 3

将char转换为int的示例:

char  my_char  =  'A' ; 
int  my_int  =  (int )my_char ;  //在使用ASCII作为字符集的计算机上,my_int == 65

乘法和加法运算符

在C语言中,简单的数学运算非常容易处理。存在以下运算符:+(加法),(减法),*(乘法),/(除法)和(模数); 你可能从你的数学课上知道所有这些 – 除了模数。它返回除法的余数(例如5%2 = 1)。(模数没有为浮点数定义,但math.h库有一个fmod函数。)

必须注意模数,因为它不等于数学模数:( – 5)%2不是1,而是-1。整数除法将返回一个整数,并且负整数除以正整数将向零舍入而不是向下舍入(例如(-5)/ 3 = -1而不是-2)。但是,对于所有整数a和非零整数b,总是如此,((a / b)* b)+(a%b)== a。

没有内联运算符进行取幂(例如5 ^ 2 不是 25 [它是7; ^是异或运算符],5 ** 2是错误),但是有 幂函数。

操作的数学顺序确实适用。例如(2 + 3)* 2 = 10而2 + 3 * 2 = 8.乘法运算符优先于加法运算符。

#include  <stdio.h>

int  main (void )
{ 
int  i  =  0 , j  =  0 ;

    / *当i小于5且j小于5时,循环* / 
    while ( (i  <  5 ) &&  (j  <  5 ) )
    { 
        / *后缀增量,i ++ 
         *读取i的值然后递增
         * / 
        printf (“i:%d \ t ” , i ++ );

        / * 
         *前缀增量,++ j 
         * 
j 的值递增,然后读取         * / 
        printf (“j:%d \ n ” , ++ j ); 
    }

    printf (“最后它们都有相同的值:\ n i:%d \ t j:%d \ n ” , i , j );

    getchar ();  / *暂停* / 
    返回 0 ; 
}

将显示以下内容:

i:0 j:1
我:1:2:2
我:2:3:3
我:3:4
我:4:5
最后,他们都有相同的价值观:
我:5:5

移位运算符(可用于旋转位)

移位功能通常用于低级I / O硬件接口。Shift和rotate函数在密码学和软件浮点仿真中被大量使用。除此之外,可以使用移位代替除法或乘以2的幂。许多处理器都有专用功能块来快速完成这些操作 – 请参阅微处理器设计/移位和旋转块。在具有此类块的处理器上,大多数C编译器将移位和旋转运算符编译为单个汇编语言指令 – 请参阅X86汇编/移位和旋转。

左移

<<运算符将二进制表示的左侧,下降最显著位和零位追加了。结果相当于将整数乘以2的幂。

无符号右移

无符号右移运算符,有时也称为逻辑右移运算符。它将二进制表示向右移动,删除最低有效位并将其前置为零。>>对于无符号整数,运算符相当于除以2的幂。

签名右移

带符号的右移运算符,有时也称为算术右移运算符。它将二进制表示向右移动,删除最低有效位,但在前面加上原始符号位的副本。该>>运营商不等同于分裂符号整数。

在C中,>>运算符的行为取决于它所作用的数据类型。因此,有符号和无符号右移看起来完全相同,但在某些情况下会产生不同的结果。

向右旋转

与流行的看法相反,可以编写编译为“旋转”汇编语言指令的C代码(在具有这种指令的CPU上)。

大多数编译器都认识到这个成语:

  unsigned  int  x ; 
  unsigned  int  y ; 
  / * ... * / 
  y  =  (x  >>  shift ) |  (x  <<  (32  -  shift ));

并将其编译为单个32位旋转指令。 [

在某些系统上,这可能是“#define”编辑为宏或定义为内联函数,在“bitops.h”等标准头文件中称为“rightrotate32”或“rotr32”或“ror32”。 [

向左旋转

大多数编译器都认识到这个成语:

  unsigned  int  x ; 
  unsigned  int  y ; 
  / * ... * / 
  y  =  (x  <<  shift ) |  (X  >>  (32  -  移位));

并将其编译为单个32位旋转指令。

在某些系统上,这可能是“#define”编辑为宏,或者定义为在“bitops.h”之类的头文件中称为“leftrotate32”或“rotl32”之类的内联函数。

关系和相等运算符

如果操作的结果为真,则关系二元运算符<(小于),>(大于),<=(小于或等于)和>=(大于或等于)运算符返回值1,如果为假,则返回0。

等于二元运算符==(等于)和!=(非等于)运算符与关系运算符类似,只是它们的优先级较低。

按位运算符

按位运算符是&(和),^(异或)和|(包含或)。该&经营者具有更高的优先级比^,它的优先级高于|

所运作的价值必须是一体的; 结果是不可或缺的。

按位运算符的一个用途是模拟位标志。这些标志可以用OR设置,用AND测试,用XOR翻转,用AND NOT清除。例如:

/ *此代码是按位运算的示例。* / 
#define BITFLAG1(1)
#define BITFLAG2(2)
#define BITFLAG3(4)/ *它们的强度为2 * /

unsigned  bitbucket  =  0U ;     / *清除所有* / 
bitbucket  | =  BITFLAG1 ;       / *设置位标志1 * / 
bitbucket  &=  ~ BITFLAG2 ;      / *清除位标志2 * / 
bitbucket  ^ =  BITFLAG3 ;       / *将位标志3的状态从关闭翻转为打开,
                               反之亦然* / 
if  (bitbucket  & BITFLAG3 ) { 
  / *位标志3设置为* / 
}  否则 { 
  / *位标志3未设置* / 
}

逻辑运算符

逻辑运算符是&&(和),和||(或)。如果关系为真,则这两个运算符都生成1,假为0则生成0。这两个运算符都短路; 如果可以从第一个操作数确定表达式的结果,则忽略第二个操作数。该&&经营者具有比更高的优先级||运营商。

&&用于从左到右计算表达式,如果两个语句都为真,则返回1 ,如果其中任何一个为假,则返回0。如果第一个表达式为false,则不计算第二个表达式。

  
  int  x  =  7 ; 
  int  y  =  5 ; 
  if (x  ==  7  &&  y  ==  5 ) { 
      ... 
  }

在这里,&&操作员检查最左边的表达式,然后检查右边的表达式。如果链接的表达式超过两个(例如x && y && z),则运算符首先检查x,然后检查y(如果x非零),如果x或y都不为零则继续向右运行z。由于两个语句都返回true,因此&&运算符返回true,并执行代码块。

  
    if (x  ==  5  &&  y  ==  5 ) { 
        ... 
    }

&&运算符以与以前相同的方式进行检查,并发现第一个表达式为false。一旦发现语句为false,&&运算符就会停止计算,并返回false。


||用于从左到右计算表达式,如果一表达式为真,则返回1 ;如果两者都为假,则返回0。如果第一个表达式为true,则不计算第二个表达式。

      
    / *使用与以前相同的变量。* / 
    if (x  ==  2  ||  y  ==  5 ) {  // || 语句检查两个表达式,发现后者为真,并返回true 
        ... 
    }

||这里的运算符检查最左边的表达式,发现它为false,但继续评估下一个表达式。它发现下一个表达式返回true,停止并返回1. &&当运算符找到返回false的表达式时,运算符停止的次数很多,||运算符在找到返回true的表达式时停止。

值得注意的是,C没有其他语言中常见的布尔值(true和false)。它将0解释为false,将任何非零值解释为true。

条件运算符

三元?:运算符是条件运算符。表达式(x ? y : z)的值为yif x非零,z否则为。

例:

  
int  x  =  0 ; 
int  y ; 
y  =  (x  ? 10  : 6 );  / *括号在技术上不是必需的,因为赋值
                     的优先级低于条件运算符,但
                     它是为了清晰起见。* /

表达式的x计算结果为0.三元运算符然后查找“if-false”值,在这种情况下,它是6.它返回它,因此y等于6。如果x一个非零,则表达式将返回10。

作业运算符

赋值运算符为=*=/=%=+=-=<<=>>=&=^=,和|=。的=操作者存储右操作数的到由左操作数所确定的位置的值,它必须是一个左值(即具有一个地址的值,并且因此可以被分配给)。

对于其他人来说,x op= y是简写x = x op (y)。因此,以下表达式是相同的:

    1. x + = y  -  x = x + y
    2. x  -  = y  -  x = xy
    3. x * = y  -  x = x * y
    4. x / = y  -  x = x / y
    5. x%= y  -  x = x%y

赋值表达式的值是赋值后左操作数的值。因此,任务可以链接; 例如,表达式a = b = c = 0;会将值零赋给所有三个变量。

逗号运算符

优先级最低的运算符是逗号运算符。表达式的值x, y将同时评估xy,但提供值y

此运算符可用于在一个语句中包含多个操作(例如,在for循环条件内)。

以下是逗号运算符的一个小示例:

int  i , x ;       / *在一个声明中声明两个整数,即i和x。
                  从技术上讲,这不是逗号运算符。* /

/ *这个循环将x和i初始化为0,然后运行循环* / 
for  (x  =  0 , i  =  0 ;  i  <=  6 ;  i ++ ) { 
    printf (“x =%d,i =%d)\ n “ , x , i ); 
}

猜你想读:《C编程.开始C》8.数组和字符串

文章作者: 6z | Intz  

THE END
分享