Statement

2022-03-22
7分钟阅读时长

运算符

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

<< 

左移

变量<<表达式

左到右

双目运算符

>> 

右移

变量>>表达式

6

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

小于

表达式<表达式

<=

小于等于

表达式<=表达式

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

双目运算符

/=

除后赋值

变量/=表达式

双目运算符

*=

乘后赋值

变量*=表达式

双目运算符

%=

取模后赋值

变量%=表达式

双目运算符

+=

加后赋值

变量+=表达式

双目运算符

-=

减后赋值

变量-=表达式

双目运算符

<<=

左移后赋值

变量<<=表达式

双目运算符

>>=

右移后赋值

变量>>=表达式

双目运算符

&=

按位与后赋值

变量&=表达式

双目运算符

^=

按位异或后赋值

变量^=表达式

双目运算符

|=

按位或后赋值

变量|=表达式

双目运算符

15

逗号运算符

表达式,表达式,…

左到右

--

表达式

表达式可以包含有关的运算符,也可以是不包含任何运算符的初等量,如常数是算数表达式的最简单形式。

  1. 算术表达式

    整型表达式:参与运算的是整型量

    实型表达式:参与运算的是实型量,运算自动转为double类型。

  2. 逻辑表达式

    逻辑运算符连接,结果类型为整型int;可认为是整型表达式的一种特殊形式。

  3. 字位表达式

    位运算符连接,结果类型为整型变量;可认为是整型表达式的一种特殊形式。

  4. 强制类型转换

    用“(类型)”运算符进行强制类型转换;

  5. 逗号表达式

    形式为表达式1,表达式2,…,表达式n;计算方法为依次求出各个表达式的值,取最后一个值作为结果

  6. 赋值表达式

  7. 条件表达式

  8. 指针表达式

  9. sizeof与strlen()的区别

    1. sizeof是运算符,strlen()是函数;

    2. sizeof计算实际占用存储空间,不受初始化影响;

      strlen计算有效字符串的长度,不包括’\0’,受初始化影响;

    3. sizeof可以用类型做参数,计算类型占内存的大小;

      strlen只能以char*作参数,且必须以’\0’结尾,用来计算字符串的长度。

      特别地,数组作sizeof的参数不变化,而传递给strlen时就变化成指针了。

    4. sizeof在编译时计算,strlen在运行时才计算。

  10. 左值与右值

    左值指的是允许出现在赋值表达式左侧的值,它是可修改的值,如变量;

    右值指的是允许出现在赋值表达式右侧的值,它包括常量、表达式等;

    左值可作右值,右值不一定能作左值。

  11. 常量表达式

    即参与运算的均为常量,不允许有变量,也不允许函数调用;

    个人推测常量表达式的值在编译时就已经确定。

预处理语句

#号开头的命令称为预处理命令,均由预处理器处理。

包含指令

包含指令#include原理:将该头文件包括到用户源文件中。

#include <stdio.h> //从C语言编译系统的目录寻找
#include "stdio.h" //从用户的当前目录寻找,未找到则再去编译系统目录查找

条件编译指令

根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。

条件编译主要用于在不同环境或条件下启用不同的语句。

#if

//不同的平台下引入不同的头文件
#if _WIN32  //识别windows平台
#include <windows.h>
#elif __linux__  //识别linux平台
#include <unistd.h>
#else //识别其他平台
#endif

#ifdef

该条件编译指令通常用于Debug/Release程序发布。

#ifdef _DEBUG
	printf("正在使用 Debug 模式编译程序...\n");
#else
	printf("正在使用 Release 模式编译程序...\n");
#endif
	system("pause");

#ifndef

该条件编译指令通常用于多文件结构的单次编译。

#ifndef Function1_H
#define Function1_H
//some declaration codes
#endif

特别注意:

#if 后面跟的是“整型常量表达式”,而 #ifdef 和 #ifndef 后面跟的只能是一个宏名,不能是其他的。

#undef

用于取消已定义的宏。

宏定义指令

宏定义指令概念

宏定义指令#define,用一个标识符来表示一个字符串,如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串。例如:

#define M (n*n+3*n)
...
#undef M //结束该宏的作用域

注意:

  1. 宏定义中表达式两边的括号不能少,否则在宏展开以后可能会由于运算符的优先级产生歧义。
  2. 宏定义是简单替换,预处理程序不会检查,如有错误,只可能在编译时(展开宏)发现;
  3. 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换;
  4. 引号括起来的视为字符串常量,不会产生宏替换;
  5. 宏定义允许嵌套使用,即可以在宏定义中使用已定义的宏名;
  6. 宏定义可用于表示数据类型

宏定义与typedef

特别注意宏定义与typedef定义的区别

  1. 宏定义只是简单的字符串替换以实参代换形参。由预处理器来处理;
  2. typedef 是在编译阶段由编译器处理的,它并不是简单的字符串替换,而给原有的数据类型起一个新的名字,将它作为一种新的数据类型。
#define PIN1 int *
typedef int *PIN2;  //也可以写作typedef int (*PIN2);
PIN1 a, b;//宏替换后成为int *a,b;表示a是指针,b是整型;
printf("%d,%d",sizeof(a),sizeof(b));//输出8,4
PIN2 a, b;//typedef定义则a和b均为int*类型
printf("%d,%d",sizeof(a),sizeof(b));//输出8.8

带参数的宏定义指令

C语言允许宏带有参数。

在宏定义中的参数称为“形式参数”,在宏调用中的参数称为“实际参数”,这点和函数有些类似。

例1:

#define M(y) y*y+3*y  //宏定义
// TODO:
k=M(5);  //宏调用

例2:

#define MAX(a,b) (a>b) ? a : b
// TODO:
max = MAX(x, y);

注意:宏调用在预处理时采用简单替换,这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再传递给形参,而宏展开中对实参表达式不作计算,直接按照原样替换。

例3:

#define SQ(y) (y)*(y)
// TODO:
sq = SQ(a+1);//预处理后得到 sq=(a+1)*(a+1);

注意:对于带参宏定义不仅要在参数两侧加括号,还应该在整个字符串外加括号

例4:

#define SQ(y) (y)*(y)
// TODO:
sq = 100/SQ(a+1);
//预处理后得到 sq=100/(a+1)*(a+1);按运算符次序得到的与预期不相符。
#define SQ(y) ((y)*(y))
// TODO:
sq = 100/SQ(a+1);
//预处理后得到 sq=100/((a+1)*(a+1));现在能得到正确答案。

进一步体会宏定义与函数调用的区别:

※ 例5:

#include <stdio.h>
int SQ(int y){
  return ((y)*(y));
}
int main(){
    int i=1;
    while(i<=5){
        printf("%d^2 = %d\n", (i-1), SQ(i++));
    }
    return 0;
}

分析:

在printf函数处,调用函数SQ(i++),此时传入SQ函数的参数为i,而printf函数内i=i+1。

输出为$1^2 = 1,2^2 = 4,3^2 = 9,4^2 = 16,5^2 = 25$

※ 例6:

#include <stdio.h>
#define SQ(y) ((y)*(y))
int main(){
    int i=1;
    while(i<=5){
        printf("%d^2 = %d\n", i, SQ(i++));
    }
    return 0;
}

分析:

在printf函数处SQ(i++)会被简单替换为(i++)*(i++);此时依次计算两次自增,i从3开始,依次为3,5,7。

输出为$3^2=2,5^2 = 12,7^2 = 30$。

带参数的宏也可以定义多个语句。始终记住宏是简单替换即可。

总结

宏和typedef的区别

宏定义与typedef 内容。

带参数的宏调用和函数的区别

  1. 函数调用时,若实参为表达式,先求表达式的值,再带入形参;而宏调用则是简单字符替换,如果未加入足够的括号声明优先级,可能由于优先级顺序不当使得运算结果不正确;
  2. 函数调用是在运行时处理,而宏则是在编译前进行预处理的时候完成;
  3. 函数的实参和形参有严格的类型限制,宏是字符替换,无类型限制;
  4. 宏会使得实际程序变长,函数调用则无此情况;
  5. 宏不占用运行时间,只消耗预处理时间和编译时间,函数调用则消耗运行时间;
  6. 在不利用指针作值传递的情况下,函数调用最多修改一个值,宏则可以修改多个值;
Avatar

坐忘道琼峰 Sitting Oblivion Tao EndlessPeak

瞽者无以与乎文章之观,聋者无以与乎钟鼓之声。岂唯形骸有聋盲哉?
上一页 StoreType
下一页 Structure