C语言基础知识手册(三)——预处理、运算符

C语言基础知识手册(三)——预处理、运算符,第1张

文章目录
  • 前言
  • 1、预处理器
    •  1.1-预处理指令
    •  1.2-宏定义
      •   1.2.1-带参宏
    •  1.3-条件编译
      •   1.2.1-#if与#endif
      •   1.2.2-defined运算符
  • 2、运算符
    •  2.1-算术运算符
      •   2.1.1-普通算术运算符
      •   2.1.2-复合算术运算符
      •   2.1.3-复合赋值运算符
    •  2.2-关系运算符
    •  2.3-逻辑运算符
    •  2.4-位运算符
      •   2.4.1-进制转换
        •     转换过程:
      •   2.4.2-移位运算符
      •   2.4.3-按位运算符
    •  2.5-sizeof()运算符
    •  2.6-优先级
    •  2.7-三元运算符

前言

  本手册是在学习过C语言的情况下,复习C语言使用的。如果帮助到大家,实属荣幸,如果有不足之处也可以在评论中提出来,互相学习互相增长,谢谢。

1、预处理器

  预处理器其实是一个小软件,它可以在编译前处理C程序。而预处理器的行为都是有预处理指令控制的,我们前面接触到的头文件“#include……”就是一种预处理指令。

 1.1-预处理指令

  指令都是以“#”开头的,在指令中可以随意添加空格,但在“#”的开头不允许有空白字符。常见的预处理指令一共有三种,分别是:宏定义、文件包含、条件编译,对于“#include”文件包含我们就不赘述了,主要介绍后两种。

 1.2-宏定义

  宏定义就是以“#define”开头的预处理指令,整体格式为:#define 宏定义标识符 替换列表
这样,当代码中出现了宏定义标识符,那么它就会被替换列表里的内容替换。比如:#define N 5,在代码编译过程中,所有的”N”都会被替换成5。这样使用的好处是使程序更容易读懂,因为某些数的含义可以被描述出来,还更容易维护,只要修改替换列表,那么大批的宏定义标识符都会被修改。

  1.2.1-带参宏

  上面介绍的宏定义被叫做简单宏,在简单宏的基础上写上参数,这样就可以替换更多的内容。
带参宏的格式通常为:“#define 宏定义标识符(参数列表) 替换列表
比如:#define MAX(x,y) ((x)>(y)?(x):(y)) ,这样,代码中的三目运算符就可以使用MAX(x,y)替换了,就变得通俗易懂,也比较方便。
这里我们还需要注意圆括号的使用,在宏定义中,每个参数和运算符都必须在括号内。如果不写,很难保证编译器会准确识别出我们想要替换的内容。

 1.3-条件编译

  条件编译指的是,编译器会根据预处理器所执行的测试结果来包含或排除程序的片段。

  1.2.1-#if与#endif

  如果了解过shell脚本语言,那么我们可以把这两种条件编译指令与shell脚本中的if语句共同理解,如果出现了#if,就一定会有#endif来结尾,以包含或排除一个片段。如果没有接触过shell脚本,那就把它当成if语句理解,只不过需要加上#endif来确定范围,而不是通过”{ }“。语法格式如下:

#if 条件……
…………
…………
#endif

  满足条件,就会执行中间的语句。比如:

#define X 1
#if X
printf("Hello!\n");
#endif

  在C语言中,表示对错只有两种情况,非0的数表示true,0表示false,要记住。(面试小考点哦!)
  在上例中,X值为1,表示正确,那么就会执行printf,相反,如果为0,就不会执行。

  1.2.2-defined运算符

  defined的作用就是检测宏定义表示符是否被定义,如果被定义则表示正确,相反表示错误。

#if define(X)
printf("no X!\n");
#endif

  其实,我们还可以写成

#ifdef X/#ifndef  标识符
………………
………………
#endif

  #ifdef就是将defined运算符与普通的#if结合起来,效果就是二者功能的结合体。而#ifndef相当于把#if与!defined(标识符)结合起来用于判断该标识符是否没被定义。

2、运算符

  C语言中最基本的结构就是表达式,表达式是表示如何计算值的一个公式,最简单的表达式就是变量和常量。而运算符则是构建表达式的基本工具,其包含了算术运算符、关系运算符、逻辑运算符、位运算符。

 2.1-算术运算符

  除了包含基础数学中的“+”、“-”、“ * ”、“/”,之外还有“%”、“,”、“=”、“+=”、“-=”、“ * =”、“/=”、“%=”、“++”、“–”。

  2.1.1-普通算术运算符

  加减乘除我们就不介绍了,与基础数学中的用法一样。
  “%”——模运算符,也称取余/求余运算符,是一个双元运算符。与除法类似但也有所不同,结果为两元相除所得的余数。

    int a = 5 , b = 3;
    printf("a %% b = %d\n",a%b);

  运行结果为:
  
  “,”——逗号运算符,用于计算多个表达式,总体从左至右依次计算,结果为最后一个表达式的值。

int a = 5 , b = 3 , c = 0;
printf("the res is %d\n",(c = a + b ,a-b,c+b));

  运行结果为:
  
  “=”——赋值运算符,即与将表达式的右 *** 作数赋给左 *** 作数。比如,在上两个例子中的变量初始化就用到了赋值运算符。

int a = 5 , b = 3;
c = a + b;
  2.1.2-复合算术运算符

  “++”——自增运算符,见名知意,若对某个变量使用该运算符,会使变量自身增加1。
  “–”——自减运算符(两个减号连着写),见名知意,若对某个变量使用该运算符,会使变量自身减少1。
  对于自增自减运算符我们需要注意的是,其要影响的目标变量与该运算符之间的位置关系是可能影响结果的。
  当我们单独使用时:

    a++;
    ++a;
    --b;
    b--;

  根据自增自减运算符的语法规则,最终如果输出a和b的值,其变化都是a增加了2,b减少了2。
  但当我们与其他语句共同使用时:

    int a = 5 , b = 3;
    printf("src a is %d b is %d\n",a,b);
    printf("change after ");
    printf("a is %d b is %d\n",a++,++b);
    printf("now ");
    printf("a is %d b is %d\n",a,b);

  其结果为:
  
  不难看出,当变量名在自增/自减运算符前面,调用该表达式会先使用该变量,再自增/自减,当变量名在自增/自减运算符后面,调用该表达式会先自增/自减,再使用该变量。

  2.1.3-复合赋值运算符

  “+=”、“-=”、“ * =”、“/=”、“%=”都属于复合赋值运算符,其最终效果都是赋值,过程不同罢了。其 *** 作为将表达式左 *** 作数与右 *** 作数进行算术运算并将结果赋给左 *** 作数。比如,如果是“a+=b”,就相当于“ a = a + b”,依此类推。

    int a = 5 , b = 3 , c = 4;
    a+=b;
    printf("a = %d\n",a);
    c%=b;
    printf("c = %d\n",c);

  运行结果为:
  

 2.2-关系运算符

  见名知意,确定两个变量之间的关系的运算符,包含了基础数学中的“>”、“>=”、“<”、“<=”,同时还有“= =”、“!=”。
  “>”、“>=”、“<”、“<=”即为大于、大于等于、小于、小于等于,只不过写法上略有不同。
  “==”表示C语言中的判断等于,即判断表达式左右 *** 作数是否相等。
  “!=”表示C语言中的不等于,即判断表达式左右 *** 作数是否不等。

 2.3-逻辑运算符

  逻辑运算符包含了逻辑与、逻辑或、逻辑非。
  “&&”——逻辑与,当运算符的左 *** 作数和右 *** 作数同时表示正确时,整个表达式表示正确,否则,整个表达式表示错误。需要注意的是,当可以判断左 *** 作数表示错误的时候,就不会再去判断右 *** 作数,也就是不会执行右 *** 作数/表达式。

int a = 4 , b = 5;
(a>b) && (a<b);//整个表达式表示错误

  “||”——逻辑或,当运算符的左 *** 作数和右 *** 作数同时表示错误时,整个表达式表示错误,否则,整个表达式表示正确。同上,当可以判断左 *** 作数表示正确的时候,就不会再去判断右 *** 作数,也就是不会执行右 *** 作数/表达式。

int a = 4 , b = 5;
(a>b) || (a<b);//整个表达式表示正确

  “! ”——逻辑非,是一个单元运算符,会将表达式的结果变为与原来相反的结果。

int a = 4 , b = 5;(a<b);//整个表达式表示错误

  特别提醒:使用关系运算符和逻辑运算符的表达式称为逻辑表达式,其结果只有true和false两种情况。

 2.4-位运算符

  在计算机中,任何 *** 作数都是以二进制表示的,这就诞生了一种通过 *** 作其某位上的数字的运算符,位运算符。位运算符包含了移位运算符、按位运算符。

  2.4.1-进制转换

  由于牵扯到二进制数,我们这里介绍一下进制转换。实际上,我们常接触的进制数有二进制、八进制、十进制、十六进制。
  二进制:全部由0和1组成,位数依据实际情况而定。
  八进制:由0~7之间的数组成,首位为数字0,其余位任意。
  十进制:我们正常使用数都是十进制数。
  十六进制:由0~15之间的数组成,10至15分别用A-F表示。

    转换过程:

  二进制→十进制:从最右端往左,第一位表示的值为2的当前位数-1次幂乘上该位的值,依此类推,最后求和。如:
  

  二进制→八进制:从最右端往左,每三位一组,不够三位在每组的左端补0凑满三位。每组按照十进制转换并依次排列即可。如:
  
  二进制→十六进制:从最右端往左,每四位一组,不够四位在每组的左端补0凑满四位。每组按照十进制转换并依次排列即可。如:
  
  八进制→二进制:其实懂得了二进制转八进制,那么八进制转二进制就很容易了。比如033,抛去首位0,3的二进制数为0011,那么最总结果就是11011。
  八进制→十进制:方法与二进制转十进制相同,只不过从2的次幂变成了8的次幂,这里就不过多介绍了。
  八进制→十六进制:先将八进制转换成二进制,再将二进制转换成十六进制即可。

  十进制→二进制:十进制转换成二进制用到了一种列式除法,如下:
  方法就是对一个十进制数除2,将整数商写道下面,余数写到侧面,依此类推,不断除2直至商为0,然后从0向上把每次的余数按顺序写下来就得到了这个十进制的二进制形式,若没位数要求,第一个0可省。
  十进制→八进制:方法与十进制转二进制相同,只不过将除数换成了8,其余步骤一样,就不再介绍了。
  十进制→十六进制:先将十进制转换成二进制,再将二进制转换成十六进制即可。

  十六进制→二进制:根据二进制转换成十六进制的方法,我们可以得出,十六进制数的每一位都由四位二进制数表示,那么反过来就是写出每一位数的二进制形式,然后按顺序写出来就是结果,这里就不再过多介绍了。
  十六进制→八进制:先将十六进制转换成二进制,再将二进制转换成八进制即可。
  十六进制→十进制:先将十六进制转换成二进制,再将二进制转换成十进制即可。

  2.4.2-移位运算符

  “<<”——左移位运算符,为二元运算符,即得到 / 将左操作数对应的二进制数 / 向左移动 / 右操作数个 / 位,并在左操作数对应的二进制的最右端用“0”补齐最左端溢出的位 / 后的结果。语言表达,理解起来比较匮乏,直接看例子。(这里的“/”只表示短句,无其他含义)
  为简化理解,我们采用16位无符号数进行测试。

    int a = 5 , b = 3 , c = 2;
    b = a<<c;
    printf("b is %d\n",b);

  运行结果为:
  
  根据运算符的含义,我们先将a转换成二进制数,5的二进制(16位,无符号)表示为“0000000000000101”,左移位运算符的右 *** 作数为c,表示2,所以向左移两位,变成“00000000000101”,再在最右端用0,补上溢出的两位,结果为“0000000000010100”,再转换成十进制就是20。

  “>>”——右移位运算符,如果理解了左移,那么右移就很容易了。对于无符号或非负的数,跟左移类似,只不过变成了右移,然后在最左端补0。如果是负数,根据实际情况,在左端补0或为保留符号位补1。

    int a = 5 , b = 3 , c = 2;
    b = a>>c;
    printf("b is %d\n",b);

  运行结果为:
  

  2.4.3-按位运算符

  “~”——按位取反运算符,是一元运算符,即将 *** 作数对应的二进制数的每一位取反(0变1,1变0)即可。
  “&”——按位与运算符,是二元运算符,将左右 *** 作数的二进制数每位对应,如果对应位上的两数全为1则结果为1,其余情况结果为0。
  “^”——按位异或运算符,是二元运算符,将左右 *** 作数的二进制数每位对应,如果对应位上的两数相同则结果为0,不同结果为1。
  “|”——按位或运算符,是二元运算符,将左右 *** 作数的二进制数每位对应,如果对应位上的两数有一位是1则结果为1,否则结果为0。
  “&”、“^”、“|”都可以与赋值运算符结合,效果同上,最终再加上赋值功能。

 2.5-sizeof()运算符

  表面上长得像一个函数,实际上是一种 *** 作符,与“++”、“–”同等。功能就像他的名字一样,得到变量或数据类型所占内存空间的大小,以字节为单位。若为字符串测量,包含了‘\0’。比如

    int a = 5;
    char x[] = "mo";
    printf("int size is %d\na size is %d\nx size is %d\n",sizeof(int),sizeof(a),sizeof(x));

  运行结果为:
  

 2.6-优先级

  就像加减乘除先算乘除一样,每个运算符都有自己的优先级。在C语言中的大致顺序为:算术运算符>移位运算符>条件运算符>按位运算符>赋值。具体如下:(由于*会影响整体格式,所以这里用图片)
  

 2.7-三元运算符

  是一种特殊的运算符,本质是一种条件表达式,由于是唯一一个需要三个 *** 作数的运算符,所以得此名。格式如下:

//表达式1?表达式2:表达式3
    int a = 5 , b = 3 , c = 0;
    printf("res is %d\n",a>b?a:b);

  
  那么它的作用可以概括为,当表达式1的结果为真,执行表达式2,否则执行表达式3。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/3002891.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存