运算符,表达式和语句

一,循环

#include <stdio.h>
#define ADJUST 7.31                                    //声明常量
int main(void)
{
    const double SCALE = 0.333;                        //声明变量为只读
    double shoe, foot;
    printf("Shoe size (men's)   foot length\n");
    shoe = 3.0;
    while (shoe < 18.5)                                //定义循环,当不满足shoe<18.5时退出
    {                                                  //循环开始
        foot = SCALE * shoe + ADJUST;
        printf("%10.1f %15.2f inches\n",shoe,foot);
        shoe = shoe + 1.0;                             //shoe自增+1,直至退出循环
    }                                                  //循环结束
    printf("If the shoe fits,wear it.\n");
}

/***
output
Shoe size (men's)   foot length
       3.0            8.31 inches
       4.0            8.64 inches
       5.0            8.97 inches
       6.0            9.31 inches
       7.0            9.64 inches
       8.0            9.97 inches
       9.0           10.31 inches
      10.0           10.64 inches
      11.0           10.97 inches
      12.0           11.31 inches
      13.0           11.64 inches
      14.0           11.97 inches
      15.0           12.31 inches
      16.0           12.64 inches
      17.0           12.97 inches
      18.0           13.30 inches
If the shoe fits,wear it.
*/
  • 代码使用花括号**{}来标出while循环的范围。花括号之间的内容就是要被重复执行的内容。花括号以及被花括号括起来的部分被称为块**(block)。

二,基本运算符

2.1 赋值运算符 =

  • C中,**=**并不意味着“相等”,而是一个赋值运算符。如下
    • year = 2022
    • 把值2022赋值给变量year,就是说,=左侧是一个变量名右侧是赋给该变量的值。符号**=被称为赋值运算符**。
    • 上面语句不能读作year等于2022”,而应该读作把值2022赋给变量year”。赋值运算从右往左进行
  • C语言中,类似下面这样的语句是没有意义的(实际上是无效的):
    • 2022 = year
    • 因为在这种情况下,2022被称为右值rvalue),只能是字面常量,不能给常量赋值,常量本身就是它的值。

2.2 数据对象,左值,右值和运算符

  • 赋值表达式语句的目的是把值存储到内存位置上。用于存储值的数据存储区域统称为数据对象data object)。C标准只有在提到这个概念时才会用到对象这个术语。使用变量名是标识对象的一种方式。
  • 左值lvalue)是C语言的术语,用于标识特定数据对象的名称或表达式。因此,对象指的是实际的数据存储,而左值是用于标识或定位存储位置的标签。
  • 可修改的左值modifiable lvalue),用于标识可修改的对象。所以,赋值运算符的左侧应该是可修改的左值。当前标准建议,使用术语对象定位符object locator value)更好。
  • 右值rvalue)指的是能赋值给可修改左值的量且本身不是左值
    • 右值可以是常量变量其他可求值的表达式(如,函数调用)。当前标准在描述这一概念时使用的是表达式的值value of an expression)而不是右值。
  • 名称中被称为“”的就是运算对象operand)。运算对象是运算符操作的对象。如,可以把“打篮球”描述为:“打”(运算符)操作“篮球”(运算对象)。类似地可以说,**=**运算符的左侧运算对象应该是可修改的左值。

2.3 加法运算符:+

  • 加法运算符addition operator)用于加法运算,使其两侧的值相加。

    • 相加的值(运算对象)可以是变量,也可以是常量。如,执行下语句:

      • sum = a + b;
        
      • 计算机查看加法运算符两侧的两个变量,把它们相加,然后赋值给sum

      • sum,a,b都是可修改的左值。因为每个变量都标识了一个可被赋值的数据对象。但是,表达式a+b是一个右值。

2.4 减法运算符:-

  • 减法运算符subtraction operator)用于减法运算,使其左侧的数减去右侧的数。

    • 如,下面的语句把200.0赋给takehome;

      • takehome = 224.00 - 24.00;
        
  • +-运算符都被称为二元运算符binary operator),即这些运算符需要两个运算对象才能完成操作。

2.5 符号运算符:-和+

  • 减号可用于标明或改变一个值的代数符号。如,执行下面的语句后,smokey的值为12;

    • rocky = -12;
      smokey = -rocky;
      
    • 这种方式使用的负号称为一元运算符unary operator)。一元运算符只需要一个运算对象

  • C90标准新增了一元+运算符,它不会改变运算对象的值或符号,只能这样使用:

    • dozen = +12;
      
    • 编译器不会报错。但是在以前,这样做是不允许的。

2.6 乘法运算符 *

  • 符号*****表示乘法。下面表示用2.54乘以inch,并将结果赋值给cm。

    • cm = 2.54 * inch;
      
  • C中没有平方函数,计算平方需两数相乘。

2.7 除法运算符:/

  • C使用符号**/来表示除法。/**左侧的值是被除数,右侧的值是除数。如,下面four的值为4.0。

    • four = 12.0 / 3.0;
      
  • 整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法的结果是整数。

  • C中整数除法结果的小数部分被丢弃,这一过程被称为截断truncation)。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/21 18:11
    * @description: 除法演示
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        printf("integer division: 5/4 is %d \n",5 / 4);
        printf("integer division: 6/2 is %d \n",6 / 2);
        printf("integer division: 7/4 is %d \n",7 / 4);
        printf("floating division: 7./4. is %1.2f \n",7. / 4.);
        printf("mixed division: 7./4 is %1.2f \n",7. / 4);          //浮点型除以整型,虽然可以但是要避免使用混合类型
        return 0;
    }
    /***
     * output
     * integer division: 5/4 is 1
     * integer division: 6/2 is 3
     * integer division: 7/4 is 1
     * floating division: 7./4. is 1.75
     * mixed division: 7./4 is 1.75
     */
    
    
  • C99标准之前,C语言给语言的实现者留有一些空间,让它们决定如何进行负数的整数除法。一种方法是,舍入过程采用小于或等于浮点数的最大整数。如:对于3.8而言,处理后的3符合这一描述。但是-3.8会建议四舍五入为-4,因为-4小于-3.8。但是,另一种舍入方法是直接丢弃小数部分。这种方法被称为“趋零截断”,即把-3.8转换成-3。在C99以前,不同的实现采用不同的方法。但是C99规定使用趋零截断。所以,应把-3.8转换成-3。

2.8 运算符优先级

  • 运算符优先级(从高到低)

    • 运算符结合律
      ( )从左往右
      + - (一元)从右往左
      * /从左往右
      + - (二元)从左往右
      =从右往左

三,其他运算符

3.1 sizeof和size_t类型

  • sizeof运算符以字节为单位返回运算对象的大小。运算对象可以是具体的数据对象(如:变量名)或类型。如果运算对象是类型(如:int),则必须用圆括号将其括起来。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/21 19:45
    * @description: sizeof运算符的使用
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        int n = 0;
        size_t intsize;
        
        intsize = sizeof(int);
        printf("n = %d, n has %zd bytes; all ints have %zd bytes.\n",n, sizeof n,intsize);
    
        return 0;
        
    }
    /***
     * output
     * n = 0, n has 4 bytes; all ints have 4 bytes.
     * */
    
    
  • C语言规定,sizeof返回size_t类型的值。这是一个无符号整数类型,但它不是新类型。

在这里插入图片描述

  • C中有一个typedef机制,允许程序员为现有类型创建别名。如:

    • typedef double real;
      
    • 这样,real就是double的别名。现在就可以创建一个real类型的变量:

    • real deal;			//使用typedef
      
  • C99做了进一步调整,新增了**%zd转换说明用于printf()显示size_t类型的值。如果系统不支持%zd**,可使用**%u%lu代替%zd**。

3.2 求模运算符:%

  • 求模运算符modulus operator)用于整数运算。求模运算符给出其左侧整数除以右侧整数的余数

  • 求模运算符只能用于整数,不能用于浮点数。

  • 求模运算符常用于控制程序流。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/21 21:40
    * @description: 把秒数转换成分钟和秒
    ********************************************************************************/
    #include <stdio.h>
    #define SEC_PER_MIN 60          //1分钟60秒
    int main(void)
    {
        int min , sec, left;          //定义分钟和秒
        printf("请输入需要转换的秒数:\n");
        scanf("%d",&sec);       //读取秒数
        while (sec > 0) 
        {
            min = sec / SEC_PER_MIN;       //截取分钟数
            left = sec % SEC_PER_MIN;      //截取剩余秒数
            printf("您输入的时间一共等于%d分钟%d秒\n",min,left);
            printf("如需退出请输入小于或等于0的数字或继续:\n");
            scanf("%d",&sec);
            printf("再见!!!!!\n");
        }
        return 0;
        
    }
    /***
     * output
     *请输入需要转换的秒数:
     *90
     *您输入的时间一共等于1分钟30秒
     *如需退出请输入小于或等于0的数字或继续:
     *0
     *再见!!!!!
     */
    
    
  • 负数如何进行求模运算。C99规定,第一个运算对象是负数,那么求模的结果为负数;如果第一个运算对象是正数,那么求模的结果也是正数

    • 即:11 % 5 = 1,11 % -2 = 1,-11 % -2 = -1,-11 % 5 = -1
    • 如果系统不支持C99标准,会显示不同结果,实际上,*标准规定,无论何种情况,只要a和b都是整数值,便可以通过a-(a/b)b来计算a%b。

3.3 递增运算符:++

  • 递增运算符increment operator)执行简单的任务,将其运算对象递增1。该运算以两种方式出现:

    • ++出现在其作用的变量前面,这是前缀模式
    • ++出现在其作用的变量后面,这是后缀模式
  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/22 15:34
    * @description: 前缀和后缀
    ********************************************************************************/
    
    #include <stdio.h>
    int main(void)
    {
        int ultra = 0, super = 0;
        while (super < 5)
        {
            super++;
            ++ultra;
            printf("super = %d , ultra = %d\n",super,ultra);
        }
        return 0;
    }
    /***
     *output
     * super = 1 , ultra = 1
     * super = 2 , ultra = 2
     * super = 3 , ultra = 3
     * super = 4 , ultra = 4
     * super = 5 , ultra = 5
     * */
    
  • 使用前缀和后缀的区别如下:

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/22 15:43
    * @description: 使用前++和后++的区别
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        int a = 1, b = 1;
        int a_post,pre_b;
        a_post = a++;       //后++先将a赋值给a_post,再+1
        pre_b = ++b;        //前++先将+1赋值给b,再将b赋值给pre_b
        printf("a   a_post  b   pre_b   \n");
        printf("%d %5d %5d %5d\n",a,a_post,b,pre_b);
        return 0;
    }
    
    /***
     * output
     * a   a_post  b   pre_b
     * 2     1     2     2
     */
    
  • 注:单独使用递增运算符时(如:ego++),使用那种形式都没有问题。

  • 如果x++是表达式的一部分,可将其视为“先使用x,在递增”;而++x则表示“先递增x,再使用”。

3.4 递减运算符:–

  • 递减运算符decrement operator)与递增运算符相对,用**–替换++**即可。

3.5 递增与递减注意事项

  • 如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增和递减运算符。
  • 如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。

四,表达式和语句

  • C的基本程序步骤由语句组成,而大多数语句statement)都由表达式(expression)构成。

4.1 表达式

  • 表达式由运算符运算对象(运算符操作的对象)组成,下面是一些表达式:
    • 4
    • -6
    • 4+21
    • a*(b+c/d)/20
    • q=5*2
    • x=++q % 3
    • q > 3
  • 运算对象可以是常量,变量或二者的组合。一些表达式由子表达式subexpression)组成(子表达式即较小的表达式)。如c/d是上面例子中a*(b + c/d)/20的子表达式。

表达式的值

  • C表达式的一个最重要的特征是,每个表达式都有一个值。要获得这个值,必须根据运算符优先级规定的顺序来执行操作。

  • 对于存在大小判断的表达式,**如果条件为真,表达式的值为1;如果条件为假,表达式的值为0。**下面是一些示例。

    • 表达式
      -4+62
      c = 3 + 811
      5 > 31
      6 + (c = 3 + 8)17
  • 最后一个表达式虽然奇怪,但在C中完全合法(但不建议使用)。

4.2 语句

  • 语句是C程序的基本构建块。在C中大部分语句都以分号结尾。

  • 最简单的语句是空语句:

    • ;				//空语句
      
  • C把末尾加上分号的表达式都看作是一条语句。因此下面这样也是可以的。

    • 8;
      3 + 4;
      
  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/22 17:27
    * @description: 几种常见的声明语句
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        int count, sum;                             //声明
        count = 0;                                  //表达式语句
        sum = 0;                                    //表达式语句
        while (count ++ < 20)                       //迭代语句
            sum = sum + count;
            printf("sum = %d\n",sum);               //表达式语句
        return 0;                                   //跳转语句
    }
    
    
    
    • 声明不是语句。声明创建了名称和类型,并为其分配内存位置。也就是说,如果删除了后面的分号,剩下的部分不是一个表达式,也没有值。
    • while语句是一种迭代语句,有时也被称为结构化语句,因为它的结构比简单的赋值表达式语句复杂。

    副作用和序列点

    • C语言的术语副作用side effect)。副作用是对数据对象或文件的修改。如:

      • states = 50;
        
      • 它的副作用是将变量的值设置为50。这似乎更像是主要目的,但是从C语言的角度看,主要目的是对表达式求值。给出表达式4 + 6,C会对其求值得10;给出表达式states = 50,C会对其求得值50.对该表达式求值得副作用是把变量states的值改为50.跟赋值运算符一样,递增和递减运算符也有副作用,使用它们的主要目的就是使用其副作用。

    • 序列点sequence point)是程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。在C语言中,语句中的分号标记了一个序列点。意思是,在一个语句中,赋值运算符,递增运算符和递减运算符对运算对象做的改变必须在程序执行下一条语句之前完成。

    • 完整表达式full expression),就是指这个表达式不是另一个更大表达式的子表达式。

4.3 复合语句(块)

  • 复合语句compound statement)是用花括号括起来的一条或多条语句,复合语句也称为block)。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/22 20:34
    * @description: 复合语句
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        int sam;
        int index = 0;
        while (index++ < 10)
            sam = 10 * index +2;
        printf("sam = %d\n",sam);
    
        return 0;
    }
    /***
     * output
     * sam = 102
     */
    
    
    • 在上面这个while循环中只有一条赋值表达式语句。没有花括号,while语句从while这行运行至下一个分号。循环结束后,**printf()**函数只会被调用一次。
  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/22 20:34
    * @description: 复合语句
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        int sam;
        int index = 0;
    
        while (index++ < 10)
        {
            sam = 10 * index + 2;
            printf("sam = %d\n",sam);
        }
        return 0;
    }
     /***
     * output
      * sam = 12
      * sam = 22
      * sam = 32
      * sam = 42
      * sam = 52
      * sam = 62
      * sam = 72
      * sam = 82
      * sam = 92
      * sam = 102
      * /
    
    • 花括号确保两条语句都是while循环的一部分,每执行一次循环就调用一次**printf()**函数。

五,类型转换

  • 在语句和表达式中应使用类型相同的变量和常量。但是,如果使用混合类型,C语言会采用一套规则进行自动类型转换。虽然好用,但不建议。

  • 当类型转换出现在表达式时,无论是unsigned还是signedcharshort都会被自动转换成int,如有必要会被转换成unsigned int(如果shortint的大小相同,unsigned short就比int大。这种情况下,unsigned short会被转换成unsigned int)。由于都是从较小类型转换为较大类型,所以这些转换被称为升级promotion)。

  • 涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。

  • 类型的级别从高至低依次是long doubledoublefloatunsigned long longlong longunsigned longlongunsigned intint。例外的情况是,当longint的大小相同时,unsigned intlong的级别高。之所以shortchar类型没有列出,是因为它们已经被升级到intunsigned int

  • 在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能导致类型升级降级demotion)。降级,是指把一种类型转换成更低级别的类型

  • 当作为函数参数传递时,charshort被转换成intfloat被转换成double。函数原型会覆盖自动升级。

  • 当类型降级时,较低类型可能放不下整个数字,怎么办?

    1. 目标类型是无符号整型,且待赋的值是整数时,额外的位将被忽略。如,如果目标类型是8位unsigned char,待赋的值是原始值求模256。
    2. 如果目标类型是一个有符号整型,且待赋的值是整数,结果因实现而异。
    3. 如果目标类型是一个整型,且待赋的值是浮点数,该行为是未定义的。
  • 当浮点类型被降级为整数类型时,原来的浮点值会被截断。即舍弃小数部分。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/22 21:30
    * @description: 自动类型转换
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        char ch;
        int i;
        float fl;
        
        fl = i = ch = 'C';
        printf("ch = %c,i = %d,fl = %2.2f\n",ch,i,fl);
        ch = ch + 1;
        i = fl + 2 * ch;
        fl = 2.0 * ch +i;
        printf("ch = %c,i = %d,fl = %2.2f\n",ch,i,fl);
        ch = 1107;
        printf("Now ch = %c\n",ch);
        ch = 80.89;
        printf("Now ch = %c\n",ch);
        return 0;
    }
    /***
     * ch = C,i = 67,fl = 67.00
     * ch = D,i = 203,fl = 339.00
     * Now ch = S
     * Now ch = P
     */
    
    
    

5.1 强制类型转换运算符

  • 有时需要进行精确的类型转换,或者在程序中表明类型转换的意图。这种情况下要用到强制类型转换cast),即在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。圆括号和它括起来的类型名构成了强制类型转换运算符cast operator)。
  • 一般而言,不应该混合使用类型(有些语言直接不允许这样做)。

六,带参数的函数

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/1/22 22:00
    * @description: 定义一个带一个参数的函数
    ********************************************************************************/
    #include <stdio.h>
    void pound(int n);		//ANSI函数原型声明
    int main(void)
    {
        int times = 5;
        char ch = '!';
        float f = 6.0;
    
        pound(times);
        pound(ch);          //ch被自动转换为int
        pound(f);           //float被自动转换为int
    
        return 0;
    }
    void pound(int n)
    {
        while (n-- > 0)
            printf("#");
            printf("\n");
    
    }
    /*** 
     * #####
     * #################################
     * ######
     */
    
    • 如果函数不接受任何函数,函数头的圆括号中应该写上关键字void。由于该函数接收一个int类型的参数,所以圆括号中包含一个int类型变量n的声明。参数名应遵循C语言的命名规则。

    • 声明参数创建了被称为形式参数formal argumentformal parameter,简称形参)的变量,上面例子中,形式参数是int类型的变量n

    • 函数调用传递的值为实际参数actual argumentactual parameter),简称实参

    • 形参是变量实参是函数调用提供的值实参被赋给相应的形参

    • 原型prototype)即是函数的声明,描述了函数的返回值参数

    • ANSI C之前,C使用的是函数声明,而不是函数原型。函数声明只指明了函数名和返回类型,没有指明参数类型。为了向下兼容,C现在仍然允许这样的形式。

      • void pound();			//ANSI C之前的函数声明
        
      • 在上面例子中使用此函数声明会出现异常,在第三次函数调用,pound(f)会失败,因为缺少函数原型,float会被自动升级为double,虽然程序仍然能运行,但是输出的内容不正确。在函数调用中显式使用强制类型转换,可以修复这个问题:但是如果f的值太大,超过了int类型表示的范围也是不行的。

        • pound((int)f);				//把f强制类型转换为正确的类型
          

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值