C_基础_第二章数据类型、运算符、表达式

第二章数据类型、运算符、表达式

  一.数据类型

    基本类型:是C语言数据类型的基本型,其值不可再分解为其他类型。
    构造类型:一种由单种或多种数据类型构造而成的数据类型。 数组  结构  共用体  枚举类型
    指针类型:一种特殊的数据类型,其值为某个量的内存地址。
       空类型:一种无返回值函数的数据类型。 void

      1、数值类型   

      计算短整型的数值范围是多少?
              最大值为 :                0111 1111 1111 1111 
                                             先加1                       0111 1111 1111 1111  +  1  =1000 0000 0000 0000=pow(2,15)=32768
                            在减1                         最大值=32768-1=32767
     最小的值是多少呢?
             最小值                                      1000 0000 0000 0000
             先求                                           1000 0000 0000 0001
                                          先减去1                         (减1)    1000 0000 0000 0000
                                          然后取反 ,得到源码    (取反)    1111  1111  1111  1111              读出:-32767
                                          最小值         =      - 32768     =        1000 0000 0000 0000
     那么无符号的短整型的数据范围是啥呢?
        无符号的意思是所有二进制位都应该看成是数值位。

        最大值应该是:        1111 1111  1111  1111 
        人为加1得到          1  0000 0000 0000 0000    =  pow(2,16)=65536
             然后减去1,               65535
                            最小值应该是:        0000 0000 0000 0000 =0.

                     浮点类型: float           4个字节            数值范围  10^38~10^-38之间       %f

                                       double         8个字节                            10^308~10^-308之间       %lf

                    字符类型   char               1个字节          c标准中字符一共有128         %c  /  %d

          char 1个字节 -128 ~127之间。
          最大值 = 0111 1111  = pow(2,7)= 127.
                                    最小值  = 1000 0000  = -128

   2.构造类型

     在实际应用中,对于复杂问题的解决,C语言还提供了新的数据类型,这些数据类型的元素或成员的数据类型仍然是基本数据类型。

      C语言中构造类型共有四种:数组、结构体、共用体(联合体)、枚举。

     3.指针类型

  C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里,数据对象是指存储在内存中的一个指定数据类型的数值或字符串,它们都有一个自己的地址,而指针便是保存这个地址的变量。也就是说:指针是一种保存变量地址的变量。

    4.空类型

    一类函数调用后并不需要向调用者返回函数值, 这种函数可以定义为“空类型”。其类型说明符为void,不需要向调用者返回函数值 。

    void的语义有多种: 1、作为参数表示无参数 2、作为返回值代表无返回值 3、修饰指针变量代表未知类型的指针 

  二 常量、变量

    -常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。(如果修改常量值(1)程序编译时会发现 报错(2程序崩溃))

      1.整形常量

    整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

    常量加前缀:代表不同的进制表现

    常量加后缀:代表常量的类型

      不加后缀 默认为int类型的常量  L 表示常量为long类型  U 表示常量为无符号的int     UL 无符号的long类型(无符号  unsigned   代表没有负数 只有正)

                后缀 不区分大小写

      %u  以有符号类型输出;

     2 浮点型常量

    浮点常量由整数部分、小数点、小数部分和指数部分组成(十进制),可以使用小数形式或者指数形式(科学计数法)来表示浮点常量。 当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时,字母E/e前必须有数字, 而E后面的阶码必须为整数。

    两种表现形式

      (1)指数(科学计数法)

      (2)十进制小数形式

        如果要写一个合法的科学计数法表示的浮点数,必须满足两条

          (1)、e前面必须有数字。
          (2)、e后面必须有整型数。

        例如:1234.56     数学中可以写成 :1.23456*10^3,C语言中:1.3456e+003

         输出时 默认保留六位小数 并且 四舍五入

        输出小数时  % .n f  就代表保留n个小数输出   

  3,字符常量:

    语法:char  ' x ' ;   字符常量是括在单引号中,存储在 char 类型的简单变量中。

       注意:(1)字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0’)。

              (2)在内存中,字符常量以ASCII码存储,一个字符占一个字节。

  `           (3)由于字符常量是按整数存储的,可以像整数一样在程序中参与相关的运算。、

        数据类型不同的平台可能占用内存大小不一样(分配的空间)

          sizeof  :测字节运算符  单位是 字节 bytes, sizeof() 括号中  可以跟变量名  类型  常量

          例如:     

#include <stdio.h>
void main()
{
   printf("%d\n",sizeof(double));
}      

    转义字符 

      在 C 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。。  

  4,字符串常量

    字符串常量 用 “” 包括的字符序列叫字符串

       编码:在ASCII 一个字符占一个字节,中文字符 属于 utf-8/Unicode/gbk  一个中文字符占两个字节

    字符串长度:从首地址开始到\0之间字符的个数

    字符串分配的空间:用sizeof可测 

    系统自动在每个字符串的末尾加上一个空字符NULL,\0’  作为字符串的结束。’\0’是一个ASCII码为0的字符

           字符串中的字符分为两类:

      普通字符

      特殊字符   第类,以‘%’开头的,表示格式化输出后面的值。 %格式控制字符   d hd f e c

           第类,以‘\’开头的,表示转意,例如\n表示换行。\转义字符       \n \t \0 \a \b \101

           第三类,‘%%’表示输出一个%

  5.自定义常量  

  • 使用 #define 预处理器: #define 可以在程序中定义一个常量,它在编译时会被替换为其对应的值。

        说明: 预处理命令#define也称为宏定义, 一个#define命令只能定义一个符号常量, 用一行书写, 不用分号结尾。

          符号常量名习惯用大写字母表示

          该命令通常放在文件头 在程序中, 符号常量不允许重新赋值。 

      • 例如:
      • #include <stdio.h>
        #define WELCOME "欢迎来蚂蚁软件学习"
        void main()
        {
           printf(WELCOME);
        }
        //1、用一个符号,来表示一堆代码就叫做宏。(符号常量就是宏的一个特例。
        
        //是编译时候,替换的,还是运行的时候替换的? 答案:编译时。更精确的说法,
        //应该是预编译阶段完成的。预编译有三个内容,
        //1,#include 
        //2,#define 宏定义
        //3#iffine 条件编译,解决重复引用的问题,需要用到条件编译。

      带参数的宏。

      • #define GET_VALUE(r) (3* (r) * (r)) //写带参数宏的时候,一定要用括号将每个参数括起来。
        
        int fun(int r)
        {
            return 3 * r * r;
        }
                
        void main()
        {
            int a = 0; 
            printf("%d\n",GET_VALUE(3+2));  
         }
        
        // 预编译时,将  printf("%d\n",GET_VALUE(3+2))替换成printf("%d\n",(3*(3+2)*(3+2)))
        函数调用,printf("%d\n",fun(3+2));//当函数调用时,是实参先进行运算,形参开辟空间,接收实参计算后的值。

    函数和带参数宏的区别:

1) 从程序的执行来看

  函数调用会带来额外的开销,它需要开辟一片栈空间,记录返回地址,将形参压栈,从函数返回还要释放栈。这种开销不仅会降低代码效率,而且代码量也会大大增加。而宏定义只在编译前进行,不分配内存,不占运行时间,只占编译时间,因此在代码规模和速度方面都比函数更胜一筹。
2) 从参数的类型来看
  函数的参数必须声明为一种特定的数据类型,如果参数的类型不同,就需要使用不同的函数来解决,即使这些函数执行的任务是相同的。而宏定义则不存在着类型问题,因此它的参数也是无类型的。也就是说,在宏定义中,只要参数的操作是合法的,它可以用于任何参数类型。

3) 从参数的副作用来看
  毋庸置疑,在宏定义中,在对宏参数传入自增(或者自减)之类的表达式时很容易引起副作用,尽管前面也给出了一些解决方案,但还是不能够完全杜绝这种情况的发生。与此同时,在进行宏替换时,如果不使用括号完备地保护各个宏参数,那么很可能还会产生意想不到的结果。除此之外,宏还缺少必要的类型检查。而函数却从根本上避免了这些情况的产生。
4) 从代码的长度来看
在每次使用宏时,一份宏定义代码的副本都会插入程序中。除非宏非常短,否则使用宏会大幅度地增加程序的长度。而函数代码则只会出现在一个地方,以后每次调用这个函数时,调用的都是那个地方的同一份代码。

  • onst 关键字

使用 const 前缀声明指定类型的常量,如下所示:

    const 数据类型 常量名 = 常量值;

下面的代码定义了一个名为MAX_VALUE的常量:

    const int MAX_VALUE = 100;

    在程序中使用该常量时,其值将始终为100,并且不能被修改。
  • 字符串常量和字符常量的主要区别:   
  1. 字符常量由单引号括起来, 字符串常量由双引号括起来。
  2. 字符常量只能是单个字符,字符串常量可以含有多个字符。

  3. 字符常量占一个字节, 字符串常量占的字节数等于字符个数加1。

三 变量

  变量的定义

  变量的初始化

  变量的不初始化

  变量的声明

    • 一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
    • 另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。

除非有 extern 关键字,否则都是变量的定义。

      extern int i; //声明,不是定义
      int i; //声明,也是定义
  • C 中的左值(Lvalues)和右值(Rvalues)

  c中有两种类型的表达式:
    1. 左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
    2. 右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。

变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句:

        int g = 20;

但是下面这个就不是一个有效的语句,会生成编译时错误:

        10 = 20;

四 运算符和表达式

  1.基础概念

     运算符(operator)是可以对数据进行相应操作的符号。

     操作数(operand)是程序操纵的数据实体,该数据可以是数值、逻辑值或其他类型。该操作数既可以是常量也可以为变量

    一元运算符二元运算符多元运算符    根据运算符可操作的操作数的个数,可把运算符分为一元运算符二元运算符多元运算符(一般三元)。 

    优先:C语言中,运算符的运算优先级共分为15级。1级最高,15级最低。 在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符

        优先级相同时,则按运算符的结合性所规定的结合方向处理。

     结合性:C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。

    优先级相同时看结合性,C语言结合性较为复杂,一般建议使用()来显式表达计算结合性。

    规律记住:
      单目运算都是右结合。
      双目运算都是左结合。
      啥叫左结合?从左边向右边计算就叫左结合

注!优先级相同时看结合性,因C的结合性较为复杂,一般用()来凸显结合性,但当出现 &&(逻辑与)||(逻辑或)(称伪0)这两种运算符时,优先级失效!&&||具有“短路”特性。

 

 2 算术运算

     算术运算符号。作用:做算术四则运算的
      +   需要记住由低类型向高类型转换。
      -   需要记住由低类型向高类型转换。
      *   需要记住由低类型向高类型转换
      /   (1)两个整数相除,必得整数,此为地板除。(2)分子或者分母有一个是浮点数,其结果就是通常意义的除法。
      %    ( 1 ) 用于计算两个数相处后得到的余数。
            (2)参与运算的量必须是整型量,其结果也是整型量。 5%3= 2 5.2%3(错了,不能算非整型数)
                            (3)某个数和N取余,其结果的范围是[0,N-1]

    i++/i--    (1)自增 自减运算符  仅代表 i的值自增1  自减1

         (2)为什么要有i++  是为了延迟自增  很多需求下  我们需要在循环中 使用i当前的值 遇到i再自增     

          (3)所以说i++ 使用会有很多误导  虽然可以简化操作  不理解用法时 尽量别用

                         

                不同数据类型间的转换与运算

      (1)实型数据赋给整型变量时,只取整数部分

      (2)整型数据赋给实型变量时,以浮点形式取值

      (3)字符型数据赋给整型变量时, 整型变量的高位补的数与char的最高位相同,  低八位为字符的ASCII码值。

      (4)整型数据赋给字符型时,只把低8位赋给字符变量

      在C程序中, 当不同类型的量进行运算时, 要转换成同一种类型然后再进行运算。

                例:   10+‘a’+1.5-8765.1234*‘b’

      自动转换: 数据类型自动由低级向高级转换。

             低类型数据与高类型数据参与运算得到的结果是高类型,由编译系统自动完成从低到高依次 字符  整型  实型(同类型看数据范围,无符号类型高于有符号类型)

      强制转换:  将表达式的运算结果强制转换成指定的数据类型。

        强制转换得到的是中间结果类型,原变量类型不变,数据类型说明符和表达式都必须加括号(单个变量除外)

                      格式:(数据类型)(表达式)2级

      强制类型转换并不会改变原数据 它只是一个中间类型的运算结果

                                                   

 3.关系运算(比较运算)

                  

      

    注意 : (1)一个等号=,是赋值运算符。
       (2)二个等号== ,是比较是否相等运算符号

         (3)用比较运算符 结果是一个逻辑值 0或1

4.逻辑运算符

  

      逻辑与 逻辑或  的短路情况

      当遇到这俩个运算符时  不能根据优先级去计算了  伪0级

  练习:请将一个整数高八位取出来

    •     #include <stdio.h>
          void main()
          { 
            int a = 0;
            int b = 0;
            scanf("%x",&a);//按16进制读入
            b = (a&0xff000000)>>24;
            printf("%x",b);//%x hexadecimal,这个是按16进制输出的意思。
          }
      
          说明
      
              0000 0000 0000 0000 0000 0000 0001 0001
            &     1111 1111 0000  0000  0000 0000 0000 0000
           -————————————————————————
              aaaa aaaa 0000 0000 0000 0000 0000 0000

  结论:当你想从一个整型数中得到一些二进制位的数值,应该用位与运算

      

 4.条件运算符

    条件运算符: 唯一的一个三目(三元)运算符,13  加括号是右结合,不加括号是左结合

    语法: 条件(逻辑值)?真:假  条件为真执行左边的表达式  为假执行右边的表达式

    结果:条件运算符的返回结果即为执行的表达式的结果

    •     #include<stdio.h>
      
          int main()
      
          {
      
            int a = 3;
      
            int b = 5;
      
            int max = 0;
      
            max =  a>b?a:b;
      
            printf("%d",max);
      
          }
      
          在条件表达式中, 各表达式的类型可以不同, 此时,条件表达式值的类型为表达式2和表达式3中较高的类型

5.赋值运算符     

                 

 

 

      赋值运算符:(右侧的计算值,赋值给左侧的变量

      a=a+3;  等价于  a+=3

      a=a-3           a-=3

      在实际工作中 不建议这样去写   会降低可读性

 

  赋值运算的类型转换规则(4点):

        1.实型数据赋给整型变量时,只取整数部分;

        2.整型数据赋给实型变量时,以浮点数形式取值;

        3.字符型数据赋给整型变量时,整型变量高位补得数与字符的最高位相同,低八位是字符的ASCII码值;

        4.整型数据赋给字符型变量时,只把低八位赋给字符变量。

 6.位运算符

   

    位与 都为1才为1

    位或    有1为1

    异或    不同为1 相同为0,不同为1:作用是两个整数二进位中哪些位不同

    •         void main()
      
              {
                short int a = 31;
                short int b = 15;
                short int c = a^b;
                printf("%hd",c);
              }
      
            分析;  a: 0000 0000 0001 1111
                b: 0000 0000 0000 1111
                ^
               ----------------------------------------
                  000;0 0000 0001 0000 所以结果就是16
      
            
      
          自反    所有二进制位取反 包括符号位  取相反数-1   单目
      
              #include <stdio.h>
              void main()
              {
                short int a = 5;
                a = ~a;
                printf("%hu",a);%hd这是按短整型有符号输出A
              }
                   说明:       5  = 0000 0000 0000 0101
                   ~5 =  1111  1111  1111 1010    (因为第一位是1,所以必须推到原码才可以正确的读出。)
                 补码 = 源码 -> 取反->+1            
      
                    1111 1111 1111 1010
                   - 0000 0000 0000 0001
                  _______________________
                    1111 1111 1111 1001
                  取反    1000 0000 0000 0110 =  - 6

   左移     左移就相当于 原数*2n次方

      右移    右移就相当于 原数地板除2的n次方

7、大小端:
   对于一个基本整型,是4个字节。在内存中如何存放呢?
   大端: 高字节放到低地址上,低字节就放到高地址。
      因为我们经常表示内存地址,左侧是低地址,右侧是高地址。
      12 34 56 78 对应二进制:00010010001101000101011001111000(b)
   小端:低字节放到低地址上,高字节就放到高地址。
      78 56 34 12 对应二进制:01111000010101100011010000010010

  • 例题:有个整型数字,本来是按小端存放的,但错误的认为是大端存放的,读出来这个数字为320,请问,这个数应该是什么?写个程序计算出来。
    
      分析:320 的16进制的写法是 0x00000140
    
      按小端存放: 00 00 01 40 四个字节是这样存放的。
      按大端存放: 0x40010000
    
       #include <stdio.h>
      void main()
      {
         int a = 320;
         int b = a&0x000000ff;//这个是低8位。
         int c = (a&0x0000ff00)>>8;//右数的第二个8位。
         int d = (a&0x00ff0000)>>16;//右边的第三个字节取出来了。
         int e = (a&0xff000000)>>24;//左边的第一个字节取出来了。
         printf("%d",e + (d<<8) + (c<<16) + (b<<24));
           }    

 7.逗号运算符

    逗号运算符是二元运算符   表达式1 , 表达式2

    逗号运算符确保操作数被顺序地处理:先计算左边的操作数,再计算右边的操作数。右操作数的类型和值作为整个表达式的结果。

     逗号运算符的优先级是所有运算符中最低的

 

    注!  重点*****

      1.整型数据赋给字符型变量时,只把低八位赋给字符变量。

      2.在计算机中,不同类型的数据是无法计算的,必须要转换成相同类型的才能参与运算;

      3.把一个浮点数按%d输出时,会输出一个乱值;

      4.把一个整型数按%f输出时,会输出零值。

      将整形值赋给实型变量时, 如: float a = 5;    5.000000

      将实型值赋给整型变量是,      int  a = 3.5   3 (只取整数部分)

      将字符值赋给整型变量时        int a = ‘A’     65(取ascii值)

      将整型值赋给字符变量时        char ch = 300;  只取低八位  300-256 ==44

 

 

 

posted @ 2020-02-10 20:56  直至成伤  阅读(1403)  评论(0)    收藏  举报