C语言(郝斌)内容整理

郝斌老师C语言内容整理

  • C语言概述
    • 为什么要学习C语言
    • C语言简介
      • 第一讲:基本编程知识
      • 第二讲:数据类型
      • 第三讲:运算符和表达式
      • 第四讲:流程控制
      • 第五讲:函数(面向过程)(重点)
      • 第六讲:数组
      • 第七讲:指针
      • 第八讲:变量的作用域和存储方式
      • 第九讲:扩展数据类型
      • 第十讲:专题

C语言概述

为什么要学习C语言

1)程序设计语言的发展

第一代语言:机器语言(0,1)

第二代语言:汇编语言(add 1,2)

第三代语言:高级语言(结构化C-Fortran-Basic-Pascal、面向对象C+±Java-C#)a+b

2)C的特点

优点:代码量小、速度快、功能强大

缺点:危险性高、开发周期长、可移植性弱

3)应用领域

  • 系统软件开发:
    • 操作系统:Windows、Linux、Unix
    • 驱动程序:主板驱动、显卡驱动、摄像头驱动
    • 数据库:DB2、Oracle、Sql Server
  • 应用软件开发:
    • 办公软件:Wps
    • 图形图像多媒体:ACDsee Photograph MediaPlayer
    • 嵌入式软件开发:智能手机、掌上电脑
    • 游戏开发:2D、3D游戏

C语言简介

第一讲:基本编程知识

  1. CPU 内存条 硬盘 显卡 主板显示器 之间的关系

    内容顺序:硬盘==>内容==>CPU==>结果(显卡、声卡)

    主板提供一个整体框架

  2. helloworld程序如何运行起来的

    编译-链接==>.exe文件==>操作系统运行

  3. 什么是数据类型

    基本类型数据

    • 整数
      • 整型(int)4字节
      • 短整型(short int)2字节
      • 长整型(long int)8字节
    • 浮点数(实数)
      • 单精度浮点数(float)4字节
      • 双精度浮点数(double)8字节
    • 字符(char)1字节

    复合类型数据

    • 结构体
    • 枚举
    • 共用体
  4. 什么是变量

    在定义变量时,通过与内容条中的空闲地址进行链接,将赋值的内容放入地址中

    变量的本质就是内存中的一段存储空间

  5. cpu内存条 vc++6.0 操作系统 之间的关系

    定义变量与内存条的存储空间产生关联,执行生成.exe后,利用操作系统进行运行

  6. 变量为什么必须要初始化

    所谓初始化就是赋值的意思

    • 内存的基本概念

      1. 内存是用来存储数据的设备。它的存储速度介于寄存器和硬盘之间。

      2. 内存条时CPU唯一可以访问的大容量的存储设备!所有硬盘中的程序和数据必须调入内存后方可被CPU执行!切记:CPU不能直接处理硬盘中的数据!

      3. 内存的问题时软件开发中最核心的问题之一!如:内存的分配,内存的释放,内存什么时候分配,内存什么时候释放,由谁来分配,由谁来释放,分配在是什么地方,访问权限如何!

      4. 内存是多字节组成的线性一维存储空间。

      5. 内存的基本划分单位是字节。

      6. 每个字节含有8位。每一位存放1个0或1个1.

      7. 字节和编号是一一对应的。每个字节都有一个唯一确定的编号,一个编号对应一个字节!这个编号也叫地址。

      8. 一个系统所能管理的内存空间的大小取决于参与编号的二进制位数。

        如:DOS系统20位寻址方案可控制 2 20 2^{20} 220B 即1MB的内存

        386/486系统采用三字节编制,可寻址 2 24 2^{24} 224M 即16M的内存

        奔腾寻址空间32位,其有效寻址空间为 2 3 2 2^32 232M 即4GB

    • 软件运行与内存关系(垃圾数据)

      • 内存是在操作系统的统一管理下使用的!
      1. 软件在运行前需要向操作系统申请存储空间,在内存空闲空间足够时,操作系统将分配一段内存空间并将外存中软件拷贝一份存入该内存空间中,并启动该软件。
      2. 在软件运行期间,该软件所占内存空间不再分配给其他软件。
      3. 当软件运行完毕后,操作系统将回收该内存空间(注意:操作系统并不清空该内存空间中遗留下来的数据),以便再次分配给其他软件使用。

      综上所述,一个软件所分配到的空间中极有可能存在着以前其他软件使用过后的残留数据,这些数据被称之为垃圾数据。所以通常情况下我们为一个变量,为一个数组,分配好存储空间之后,都要对该内存空间初始化!

  7. 如何定义变量

    ​ 数据类型 变量名 = 要赋的值

    等价于

    ​ 数据类型 变量名;

    ​ 变量名 = 要赋的值

    举例子

    ​ int i = 3; 等价于 int i; i = 3;

    ​ int i, j; 等价于 int i; int j;

    ​ int i, j=3; 等价于 int i; int j; j=3

    ​ int i = 3; j = 5 等价于 int i ; int j; i=3; j=5

    ​ int i, j; i=j=5; 等价于 int i, j; i =5; j=5;

  8. 什么是进制

    十进制就是逢十进一 D %d表示以十进制输出

    二进制逢二进一 B

    八进制 O %o表示以八进制输出

    十六进制 H %x或%X表示以十六进制输出

    注释:// /**/

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCjzjjnI-1627043706049)(C:\Users\98614\AppData\Roaming\Typora\typora-user-images\image-20210628161500212.png)]

  9. 常量在C语言中是如何表示的

    • 整数

      • 十进制:传统的写法
      • 十六进制:前面加0x或0X
      • 八进制:前面0(数字零)
    • 浮点数

      • 传统的写法 float x = 3.2;

      • 科学计数法 float x = 3.2e3; //x的值时3200

        注:double赋给float可能会丢失数据,可在数据后加F,表示赋予float。

    • 字符

      • 单个字符用单引号括起来 ‘A’

      • 字符串用双引号括起来 “AB”

        注:“A”正确,因为“A”代表了‘A’‘\0’的组合

  10. 常量以什么样的二进制

    编码

    int i = 86;

    整数以补码的形式转换为二进制代码存储在计算机中的实数是以IEEE754标准转化为二进制代码存储在计算机中的字符的本质实际也是与整数的存储方式相同。

  11. 代码规范化

    缩进,一对括号, 函数后空格,运算符左右加空格,运算级别最低的地方敲空格

    换行,功能相对独立,进行换行。

  12. 什么是字节

    字节就是存储数据的单位,并且是硬件所能访问的最小单位。

    1 字 节 = 8 位 1字 节=8位 1=8 1 k = 1024 字 节 1k=1024字节 1k=1024 1 M = 1024 k 1M=1024k 1M=1024k 1 G = 1024 M 1G=1024M 1G=1024M

  13. 不同类型数据之间相互赋值的问题

    int i = 45;
    long j = 102345;
    i = j;
    printf("%ld %d\n", i, j);
    float x = 6.6;
    double y = 8.8;
    printf("%f %lf\n, x, y")
    
  14. 什么是ASCII

    ASCII不是一个值,而是一种规定。

    ASCII规定了不同的字符是使用哪个整数值去表示

    它规定了

    ‘A’–65; ‘B’–66; ‘a’–97; ‘b’–98; ‘0’–48 ………

  15. 字符的存储[字符本质上与整数的存储方式相同]

    基本的输入和输出函数的用法

    printf():将变量的内容输出到显示器上

    • 四种用法:
      • printf("字符串\n");
      • printf("输出控制符", 输出参数);// d是十进制,o是八进制,x十六进制
      • printf("输出控制符1 输出控制符2...", 输出参数1,输出参数2...); // 输出控制符和输出参数的个数必须一一对应
      • printf("输出控制符 非输出控制符,", 输出参数);// 输出控制符包含:%d %ld %c %f %lf %o %s %x(或者%X或者%#X)#表示显示字符关系
      • 为什么需要输出控制符:
        • 01组成的代码可以表示数据也可以表示指令
        • 如果01组成的代码表示数据的话,那么同样的01代码组合以不同的输出格式输出就会有不同的输出结果

    scanf():通过键盘将数据输入到变量中

    • 两种用法:
      • 用法一:scanf("输入控制符", 输入参数);功能:将从键盘输入的字符转化为输入控制符所规定格式的数据,然后存入以输入参数的值为地址的变量中。//&i 表示i的地址 &是一个取地址运算符scanf("%d", &i);

      • 用法二:scanf("非输入控制符 输入控制符", 输入参数);功能:将从键盘输入的字符转化为输入控制符所规定格式的数据,然后存入以输入参数的值为地址的变量中(非输入控制符必须原样输入)

      • 如何使用scanf编写出高质量代码

        1. 使用scanf之前最好先使用printf提示用户以什么样的方式来输入

        2. scanf中尽量不要使用非输入控制符,尤其是不要用\n(非常不好的格式,不要加\n)

        3. 应该编写代码对用户的非法输入做适当的处理。

          while ((ch=getchar()) != '\n')
          	continue;
          
      • scanf中输入会逐个字节解析,如果存在问题,则停止解析后面的,直接赋值,再次解析时,从停止位置开始,完成解析过程。

  16. 代码注释

    时间,功能,目的,输出结果,总结

第二讲:数据类型

第三讲:运算符和表达式

  • 算术运算符: + - * / %
    • 除法/的运算结果和运算对象的数据类型有关,两个数都是int,则商就是int,若商有小数,则截取小数部分;被除数和除数中只要有一个或两个都是浮点型数据,则商也是浮点型,不截取小数部分。
    • 取余%的运算对象必须是整数,结果是整除后的余数,其余数的符号与除数相同
  • 关系运算符: > >= < <= !=(不等于) ==
  • 逻辑运算符:!(非) &&(并且) ||(或)
    • C语言对真假的处理:非零是真,零是假;真是1表示,假是0表示
    • &&左边的表达式为假,右边的表达式不执行 (不含分号为表达式,含分号叫语句)
    • ||左边的表达式为真,右边的表达式不执行
  • 赋值运算符:= += -= *= /=
  • 优先级别:算术>关系>逻辑>赋值

第四讲:流程控制

  1. 什么是流程控制

    成都代码执行的顺序

  2. 流程控制的分类

    • 顺序执行

    • 选择执行

      • 定义:有些代码可能执行,也可能不执行,有选择的执行某些代码

      • 分类:

        • if

          1. if 最简单的用法

            格式:

            if (表达式)
            	语句
            

            功能:

            ​ 如果表达式为真,执行语句;不成立,语句不执行

          2. if的范围问题

            if (表达式)

            ​ 语句A;

            ​ 语句B;

            解释:if默认只能控制语句A的执行或不执行

            ​ if无法控制语句B的执行或不执行(语句B是一定执行的)

            if (表达式)
            {
            	语句A;
            	语句B;
            }
            

            此时if可以控制语句A和语句B

            由此可见:if默认只能控制一个语句的执行或者不执行,如果想控制多个语句的执行或者不执行就必须把这些语句用{}括起来

          3. if…else…的用法

          4. if…else if…else…的用法

            格式:

            if (1)
            		A;
            	else if (2)
            		B;
            	else if (3)
            		C;
            	else
            		D;
            
          5. C语言对真假的处理

            非零是真

            零就是假

          ​ 真用1表示

          ​ 假用零表示

          1. if举例–求分数的等级

          2. if的常见问题解析

            1. 空语句的问题

              if (3>2);
              等价于
              if (3>2)
              	;
              
            2. ;问题

            if (表达式1)
            	A;
            else
                B;
            正确
            if (表达式1);
            	A;
            else
                B;
            错误
            
            1. 执行顺序

              if (表达式1)
              	A;
              else if (表达式2)
              	B;
              else if (表达式3)
              	C;
              else
              	D;
              即使表达式12都成立,也只执行A
              
            2. 缺少else

              if (表达式1)
              	A;
              else if (表达式2)
              	B;
              else if (表达式3)
              	C;
              这样写语法不会有错误,但逻辑有漏洞
              
            3. else没有表达式

              if (表达式1)
              	A;
              else if (表达式2)
              	B;
              else if (表达式3)
              	C;
              else (表达式)
              	D;
              else没有表达式错误
              
        • switch

          switch (语句)
          {
              case 判断1: 语句1break; // 如果只有一条语句,可以不加{},默认break属于上一条case
              case 判断2: 语句2; break;
              default: 语句; break; // 可省略
          }
          
    • 循环执行

      • 定义:某些代码会被重复执行

      • 分类:

        • for

          1. 格式:
          for (1;2;3)
          	语句A;
          
          1. 执行的流程【重点】:1->2->A->3->2->A->3->2

          2. 范围问题

          3. 强制类型转换 (float)(i)

            格式:(数据类型)(表达式)
            功能:把表达式的值强制转化为前面所执行的数据类型
            
          4. 浮点数的存储所带来的问题

            float和double都不能保证可以精确的存储一个小数

            循环中更新的变量不能定义成浮点型:浮点型非准确存储

            例如:

            float i=99.9

            printf(“%f\n”, i);

            输出结果:99.900002

            因为浮点数无法准确存储,所以就衍生出来两个编程问题

            有一个浮点型变量x,如何判断x的值是否是零

            if(|x-0.000001|<=0.000001)

            ​ 是零

            else

            ​ 不是零

        • while

          1. 执行顺序

            格式

            while(表达式)
            	语句;
            
          2. 与for的相互比较

            for和while可以相互转换

            for (1;2;3)
            	A;
            等价于
            
            1;
            while(2)
            {
            	A;
            	3;
            }
            
          3. 举例

            回文数 121 323 正着写和倒着写一样

          4. 什么时候使用while,什么时候使用for

        • do while

          格式:

          do
          {
          。。。。
          } while(表达式);
          至少执行一次,但是while不一定一定执行一次
          主要用于人机交互
          

      自增[自减]

      分类:

      • 前自增 ++i
      • 后自增 i++
    • 相同:最终都使i的值加1

    • 不同

      • 前自增整体表达式的值是i加1之后的值
      • 后自增整体表达式的值是i加1之前的值
    • 优点:代码更精炼,自增的速度更快

    • 编程时注意:

      • 应尽量屏蔽掉前自增和后自增的差别
      • i++和++i单独成一个语句,不要把它作为一个复合语句来实现

      三目运算符

      A ?B:C
      等价于
      if (A)
      	B;
      else
      	C;
      

      逗号表达式

      格式:(A, B, C, D)

      功能:从左到右执行

      printf(“格式运算符,格式运算符,格式运算符,格式运算符”, A, B, C, D);

      从右向左入栈,输出从左向右出栈

      break

      break 如果用于循环是用来终止循环

      break 如果用于switch,则是用来终止switch

      break 不能直接用于if,除非if属于循环内部的一个子句

      在多层循环中,break只能终止距离它最近的那个循环

      在多层switch嵌套中,break只能终止距离它最近的switch

      continue

      continue用于跳过本次循环余下的语句,转去判断是否需要执行下次循环

第五讲:函数(面向过程)(重点)

为什么需要函数

  • 避免了重复性操作
  • 有利于程序的模块化

什么叫函数

  • 逻辑上:能够完成特定功能的独立的代码块
  • 物理上:能够接受数据[当然也可以不接受数据];能够对接受的数据进行处理;能够将数据处理的结果返回[当然也可以不返回任何值];
  • 总结:函数是一个工具,它是为了解决大量类似问题而设计的函数可以当做一个黑匣子

如何定义函数

  • 函数的返回值 函数的名字(函数的形参列表)
    {
    	函数的执行体
    }
    
    1. 函数定义的本质是详细描述函数之所以能够实现某个特定功能的具体方法
    2. return 表达式; 的含义:
      1. 终止被调函数,向主调函数返回表达式的值
      2. 如果表达式为空,则只终止函数,不向被调函数返回任何值
      3. break是用来终止循环和switch的,return是用来终止函数的
    3. 函数返回值的类型也称为函数的类型,因为如果函数名前面的返回值类型和函数执行体中 return表达式类型不同的话,则最终函数返回值以函数名前的返回值类型为准

函数的分类

  • 有参函数和无参函数

  • 有返回值和无返回值

  • 库函数和用户自定义函数

  • 值传递函数和地址传递函数

  • 普通函数和主函数(main函数)

    一个程序必须有且只有一个主函数

    主函数可以调用普通函数,普通函数不能调用主函数

    普通函数可以相互调用

    主函数是程序的入口,也是程序的出口

注意的问题

  • 函数调用和函数定义的顺序
    • 如果函数调用写在了函数定义的前面,则必须加函数前置声明(如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调用)
    • 函数前置声明的作用:
      1. 告诉编译器即将可能出现的若干个字母代表的是一个函数
      2. 告诉编译器即将可能出现的若干个字母所代表的函数的形参和返回值的具体情况
      3. 函数声明是一个语句,末尾必须加分号
      4. 对库函数的声明是通过# include <库函数所在的文件的名字.h>来实现的
  • 形参和实参
    • 个数相同 位置一一对应 数据类型必须相互兼容
  • 如何在软件开发中合理的设计函数来解决实际问题
    • 一个函数的功能尽量独立,单一
    • 多学习,多模仿牛人的代码
  • 函数是C语言的基本单位,类是Java,C#,C++的基本单位

常用的系统函数

  • double sqrt(double x); 求的x的平方根
  • int abs(int x); 求x的绝对值(整数)
  • double fabs(double x); 求x的绝对值(浮点数)

专题:递归

第六讲:数组

为什么需要数组

​ 为了解决大量同类型数据的存储和使用问题

​ 为了模拟现实世界

数组的分类

  • 一维数组

    • 怎样定义一维数组

      • 为n个变量连续分配存储空间
      • 所有的变量数据类型必须相同
      • 所有变量所占的字节大小必须相等
    • 有关一维数组的操作

      • 初始化

        • 完全初始化

          int a[5]={1,2,3,4,5};

        • 不完全初始化,为被初始化的元素自动为零

          int a[5]={1,2,3};

        • 不初始化,所有元素是垃圾值

          int a[5];

        • 清零

          int a[5]={0};

        • 错误写法

          int a[5];
          a[5] = {1,2,3,4,5};// 错误
          只有在定义数组的同时才可以整体赋值,其他情况下整体赋值都是错误的
          a[5] = 100; // 错误,没有a[5]
          
          int a[5]={1,2,3,4,5}
          int b[5];
          
          如果要把a数组中的值全部复制给b数组
          b = a; // 错误
          正确写法
          for(int i=0; i<5;i++)
          	b[i] = a[i]
          
        • 赋值

        • 排序

        • 求最大值、最小值

        • 倒置

        • 查找

        • 插入

        • 删除

  • 二维数组

    int a[3][4]总共12个元素,可以当成3行4列看待,这12个元素的名字依次是

    a[0][0] a[0][1] a[0][2] a[0][3]

    a[1][0] a[1][1] a[1][2] a[1][3]

    a[2][0] a[2][1] a[2][2] a[2][3]

    a[i][j]表示第i+1行,第j+1列的元素

    int a[m][n]; 该二维数组右下角的元素位置只能是a[m-1][n-1]

    初始化

    • int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

    • int a[3][4]={

      ​ {1,2,3,4},

      ​ {5,6,7,8},

      ​ {9,10,11,12}

      ​ };

    输出二维数组内容:

    for (i=0; i<3; i++)
    	{
            for (j=0; j<4; j++)
                printf("%-5d	", a\[i][j]);
            printf("\n");
    	}	
    
  • 多维数组

    是否存在多维数组:

    不存在,因为内存是线性一维的,n维数组可以当做每个元素是n-1维数组的一维数组

    比如:int a[3][4]; 该数组是含有3个元素的一维数组,只不过每个元素都可以再分成4个小元素

    int a[3][4][5]; 该数组是含有3个元素的一维数组,只不过每个元素都是4行5列的二维数组

第七讲:指针

指针的重要性

  • 表示一些复杂的数据结构

  • 快速的传递数据,减少了内存的消耗

  • 使函数返回一个以上的值

  • 能直接访问硬件

  • 能够方便的处理字符串

  • 是理解面向对象语言中引用的基础

    总结:指针是C语言的灵魂

指针的定义

  • 地址
    • 内存单元的编号
    • 从零开始的非负整数
    • 范围:4G [0–4G-1] 32位
  • 指针
    • 指针就是地址,地址就是指针
    • 指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量
    • 指针和指针变量事两个不同的概念
    • 但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
    • 指针的本质就是一个操作受限的非负整数

指针的分类

  1. 基本类型指针

    int * p; // 定义p为指针变量
    int i=3;
    p = &i; //把i的地址赋给p
    *p // 表示i,以p的内容为地址的变量
    

    *的含义:

    1. 乘法
    2. 定义指针变量 int *p; // 定义了一个名字叫p的变量,int *表示p只能存放int变量的地址
    3. 指针运算符:
      • 该运算符放在已经定义好的指针变量的前面
      • 如果p是一个已经定义好的指针变量,则 *p表示以p的内容为地址的变量

    如何通过被调函数修改主调函数普通变量的值

    1. 实参必须为该普通变量的地址
    2. 形参必须为指针变量
    3. 在被调函数中通过*形参名 = 。。。的方式就可以修改主调函数相关变量的值
  2. 指针和数组

    指针和一维数组

    • 数组名

      一维数组名a

      • 一维数组名是个指针常量
      • 它存放的是一维数组第一个元素的地址
    • 下标和指针的关系

      如果p是个指针变量,则p[i]永远等价于 *(p+i)

    • 确定一个一维数组需要几个参数【如果一个函数要处理一个一维数组,则需要接受该数组的哪些信息】

      需要两个参数:

      • 数组第一个元素的地址
      • 数组的长度
    • 指针变量的运算

      指针变量不能相加,不能相乘,也不能相除

      如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减

    • 一个指针变量到底占几个字节

      sizeof(数据类型/变量名)

      • 功能:返回值就是该数据类型所占的字节数

      假设p指向char类型变量(1个字节)

      假设q指向int类型变量(4个节字)

      假设r指向double类型变量(8个字节)

      p、q、r本身所占的字节数一样,都占4个字节。—4字节=32位,32根线控制地址,2的32次方组合结果,使用4个字节可以完全保存地址总线

      总结:

      • 一个指针变量,无论它指向的变量占几个字节,该指针变量本身只占四个字节
      • 一个变量的地址使用该变量首字节的地址来表示

    指针和二维数组

  3. 指针和函数

  4. 指针和结构体

  5. 多级指针

    int i = 10;
    int *p = &i;
    int **q = &p;
    int ***r = &q;
    

第八讲:变量的作用域和存储方式

分类:

  • 按作用域分:全局变量和局部变量
    • 全局变量:在所有函数外部定义的变量叫全局变量。
      • 使用范围:从定义位置开始到整个程序结束
    • 局部变量:在一个函数内部定义的变量或者函数的形参都统称为局部变量
      • 使用范围:只能在本函数内部使用
  • 按变量的存储方式:静态变量、自动变量和寄存器变量
    • 静态存储方式:是指在程序运行期间分配固定的存储空间的方式。
      动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。
    • 全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放;
    • 函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。
    • 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数调用结束后即释放。
    • 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
    • 如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。
    • 为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。
    • 只有局部自动变量和形式参数可以作为寄存器变量;
    • 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
    • 局部静态变量不能定义为寄存器变量。

注意的问题:

  • 全局变量和局部变量命名冲突的问题

    在一个函数内部如果定义的局部变量的名字和全局变量名字一样时,局部变量会屏蔽掉全局变量

第九讲:扩展数据类型

第十讲:专题

动态内存分配

  • 传统数组的缺点:

    1. 数组长度必须事先制定,且只能是常整数,不能是变量

      例子:int a[5];正确

      ​ int len = 5; int a[len]; //error

    2. 传统形式定义的数组,该数组的内存程序员无法手动释放

      在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会释放

    3. 数组的长度一旦定义,其长度就不能在更改

      数组的长度不能在函数运行的过程中动态的扩充或缩小

    4. A函数定义的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕之后,A函数中的数组将无法在被其他函数使用

  • 为什么需要动态分配内存

    动态数组很好的解决了传统数组的这4个缺陷

    传统数组也叫静态数组

    malloc 是 memory(内存) allocate(分配)

  • 动态内存分配举例——动态数组的构造

    1. 要使用malloc函数,必须添加malloc.h这个头文件

    2. malloc函数只有一个形参,并且形参是整型

    3. 整数表示请求系统为本程序分配字节数

    4. malloc函数只能返回第一个字节的地址

    5. int *p = (int *)malloc(4);
      分配了8个字节,p变量占4个字节,p所指的内存也占4个字节
      p本身所占的内存是静态分配的,p所指向的内存是动态分配的
      
    6. free(p)表示把p所指向的内存给释放掉,p本身的内存是静态的,不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放
      
  • 静态内存和动态内存的比较

    • 静态内存是由系统自动分配,由系统自动释放
    • 静态内存是在栈分配的
    • 动态内存是由程序员手动分配,手动释放
    • 动态内存是在堆分配的
  • 跨函数使用内存的问题

    • 静态变量不能跨函数使用内存
    • 动态变量能跨函数使用内存

结构体

为什么需要结构体

  • 为了表示一些复杂的事物,而普通的基本类型无法满足实际要求

声明叫结构体

  • 把一些基本类型数据组合在一起形一个新的复合数据类型,这个就叫结构体

如何定义一个结构体

  • 3种方式(推荐使用第一种)

    // 第一种方式
    struct Student
    {
        int age;
        float score;
        char sex;
    };
    
    // 第二种方式
    struct Student
    {
        int age;
        float score;
        char sex;
    } st2;  // 只能定义一次
    
    // 第三种方式
    struct
    {
        int age;
        float score;
        char sex;
    } st3;
    

怎样使用结构体变量

  • 赋值和初始化

    定义的同时可以整体赋初值

    如果定义完之后,则只能单个的赋初值

    struct Student st = {80, 66.6, 'f'}; //初始化,定义的同时赋初值
    struct Student st2;
    st2.age = 10;
    st2.score = 88;
    st2.sex = 'f';
    
  • 如何取出结构体变量中的每一个成员

    1. 结构体变量名.成员名

      st.age;
      st.score;
      st.sex;
      
    2. 指针变量名->成员名

      指针变量名->成员名 在计算机内部会被转化成(*指针变量名).成员名的方式来执行

      所以说这两种方式是等价的

      struct Student *pst = &st;
      pst->age = 88;
      pst->score = 66.6;
      pst->sex = 'f';
      

      pst->age在计算机内部会被转化为(*pst).age

  • 结构体变量的运算

    结构体变量不能相加,不能相减,也不能相互乘除,但结构体变量可以相互赋值

  • 结构体变量和结构体变量指针作为函数参数传递的问题

    推荐使用结构体指针变量作为函数参数来传递

举例:动态构造存放学生信息的结构体数组

​ 动态构造一个数组,存放学生的信息,然后按分数排序输出

枚举:

  • 什么是枚举

    把一个事物所有可能的取值一一列举出来

  • 怎样使用枚举

  • 枚举的优缺点

    代码更安全

    书写麻烦

进制:

  • 逢n进一
  • 十进制到其它进制,除以进制数,直至结果为0,余数倒输出。
  • 其它进制到十进制,进制的次方相加
  • 二进制<=>八进制:3位一体
  • 二进制<=>十六进制:4位一体
  • 八进制和十六进制:利用二进制或者十进制转化

补码:

  • 原码:

    • 也叫符号-绝对值码
    • 最高位0表示正,1表示负,其余二进制位是该数字的绝对值的二进制位
    • 原码简单易懂
    • 加减运算复杂
    • 存在加减乘除四种运算,增加了CPU的复杂度
    • 零的表示不唯一(正0,负0)
  • 反码

    • 运算不便,也没有在计算机中应用
  • 移码

    • 移码表示数值平移n位,n称为移码量
    • 移码主要用于浮点数的解码的存储
  • 补码

    • 十进制转二进制

      • 正整数转二进制

        除2取余,直至商为零,余数倒序排序

      • 负整数转二进制

        先求与该负数相对应的正整数的二进制代码,然后将所有位取反,末尾加1,不够位数时,左边补一

      • 零转二进制

        全是零

    • 已知二进制求十进制

      • 如果首位是0,则表明是正整数,按普通方法来求
      • 如果首位是1,则表明是负整数,将所有位取反,末尾加1,所得数字就是该负数的绝对值

    二进制全部为零的含义:0000000000000000000的含义

    1. 数值0

    2. 字符串结束标记符‘\0’

    3. 空指针NULL

      NULL本质也是零,而这个零不代表数字零,而表示的是内存单元的编号零

      我们计算机规定了,以零为编号的存储单元的内容不可读,不可写

    int类型的变量所能存储的数字的范围是:

    • int类型变量所能存储的最大正数十六进制表示是:7FFFFFFF
    • int类型变量所能存储的绝对值最大的负整数用十六进制表示是:80000000
    • 绝对值最小负数的二进制代码是:FFFFFFFF

链表:

  • 算法:

    • 通俗定义

      解题的方法和步骤

    • 狭义定义

      对存储数据的操作

      对不同的存储结构,要完成某一个功能所执行的操作是不一样的

      比如:

      要输出数组中所有的元素的操作和要输出链表中所有元素的操作肯定是不一样的

      这说明:算法是依附于存储结构的,不同的存储结构,所执行的算法是不一样的

    • 广义定义

      广义的算法也叫泛型

      无论数据是如何存储的,对该数据的操作都是一样的

    我们至少可以通过两种结构来存储数据

    • 数组

      • 优点

        存储速度很快

      • 缺点

        需要一个连续的很大的内存

        插入和删除元素的效率很低

    • 链表

      • 专业术语

        首节点

        • 存放第一个有效数据的节点

        尾节点

        • 存放最后一个有效数据的节点

        头结点:

        • 头结点的数据类型和首节点类型是一模一样的
        • 头结点是首节点前面的那个节点
        • 头结点并不存放有效数据
        • 设置头结点的目的是为了方便对链表的操作

        头指针

        • 存放头结点地址的指针变量

        确定一个链表需要一个参数

        • 头指针
      • 优点

        插入删除元素效率高

        不需要一个连续的很大的内存

      • 缺点

        查找某个位置的元素效率低

位运算符:

  • &:按位与;&&逻辑与,也叫并且,&&与&的含义完全不同

  • |:按位或,||逻辑或

  • ~:按位取反

  • ^:按位异或,相同为零,不同为1

  • <<:按位左移,右边补零:左移n位相当于乘以2的n次方(前提是数据不能溢出)

    面试题:

    • i = i*8;

    • i = i<<3; ------------快

      请问上述两个语句,哪个语句执行的速度快

  • >>:按位右移,左边一般补零(也可以补1);右移n位相当于除以2的n次方(前提是数据不能溢出)

    通过位运算符可以对数据的操作精确到每一位
    3. 空指针NULL

    NULL本质也是零,而这个零不代表数字零,而表示的是内存单元的编号零

    我们计算机规定了,以零为编号的存储单元的内容不可读,不可写

    int类型的变量所能存储的数字的范围是:

    • int类型变量所能存储的最大正数十六进制表示是:7FFFFFFF
    • int类型变量所能存储的绝对值最大的负整数用十六进制表示是:80000000
    • 绝对值最小负数的二进制代码是:FFFFFFFF

链表:

  • 算法:

    • 通俗定义

      解题的方法和步骤

    • 狭义定义

      对存储数据的操作

      对不同的存储结构,要完成某一个功能所执行的操作是不一样的

      比如:

      要输出数组中所有的元素的操作和要输出链表中所有元素的操作肯定是不一样的

      这说明:算法是依附于存储结构的,不同的存储结构,所执行的算法是不一样的

    • 广义定义

      广义的算法也叫泛型

      无论数据是如何存储的,对该数据的操作都是一样的

    我们至少可以通过两种结构来存储数据

    • 数组

      • 优点

        存储速度很快

      • 缺点

        需要一个连续的很大的内存

        插入和删除元素的效率很低

    • 链表

      • 专业术语

        首节点

        • 存放第一个有效数据的节点

        尾节点

        • 存放最后一个有效数据的节点

        头结点:

        • 头结点的数据类型和首节点类型是一模一样的
        • 头结点是首节点前面的那个节点
        • 头结点并不存放有效数据
        • 设置头结点的目的是为了方便对链表的操作

        头指针

        • 存放头结点地址的指针变量

        确定一个链表需要一个参数

        • 头指针
      • 优点

        插入删除元素效率高

        不需要一个连续的很大的内存

      • 缺点

        查找某个位置的元素效率低

位运算符:

  • &:按位与;&&逻辑与,也叫并且,&&与&的含义完全不同

  • |:按位或,||逻辑或

  • ~:按位取反

  • ^:按位异或,相同为零,不同为1

  • <<:按位左移,右边补零:左移n位相当于乘以2的n次方(前提是数据不能溢出)

    面试题:

    • i = i*8;

    • i = i<<3; ------------快

      请问上述两个语句,哪个语句执行的速度快

  • >>:按位右移,左边一般补零(也可以补1);右移n位相当于除以2的n次方(前提是数据不能溢出)

    通过位运算符可以对数据的操作精确到每一位

你可能感兴趣的:(C语言,C/C++,c语言)