C语言程序设计超详细复习总结

C语言

注意:要了解c语言的函数库,会使用里面的函数,如math.h,stdlib.h库, rand()函数等

  1. 计算机元素
    (1)机器语言:机器指令的集合
    机器指令:计算机能够识别的二进制代码
    (2)符号语言(汇编语言):一些英文字母和数字表示一个指令,汇编程序转义为机器语言
    代真(汇编):转换过程
    (3)高级语言
    编译程序的软件把源程序转换为目标程序

  2. 高级语言发展阶段(了解)
    (1)非结构化的语言
    (2)结构化语言
    (3)面向对象的语言

  3. C语言的主要特点(掌握)
    (1)语言简洁、紧凑、使用方便、灵活(2)运算符丰富
    (3)数据类型丰富
    (4)具有结构化的控制语句
    (5)语法限制不太严格,程序设计自由度大
    (6)C语言允许直接访问物理地址,能进行位操作,能实现汇编语言大部分功能,直接对硬件操作
    (7)可移植性强
    (8)生成目标代码质量高,程序执行效率高

  4. C语言程序结构(掌握)
    (1)一个程序由一个或多个源程序文件组成
    (2)源程序:
    1)预处理命令:#include<stdio.h>、# define
    2)全局变量:函数之外数据声明
    3)函数定义
    (3)函数是C程序的主要组成部分,函数是C语言的基本单位
    (4)一个函数包含两个部分
    1)函数首部:函数名,函数类型,函数属性,函数参数名,参数类型
    2)函数体:声明部分和执行部分
    (5)程序总是从main函数开始执行
    (6)程序中要求计算机的操作是由函数中的C语句完成的
    (7)程序应当包含注释

  5. C语言运行步骤(掌握)
    源程序->目标程序->可执行程序
    (1)上机输入和编辑源程序 后缀名.c
    (2)编译,用预处理编译器对预处理指令编译预处理
    作用:检查源程序是否有误
    注:编译系统编译时自动包含预编译和正式编译
    (3)进行连接处理,得到后缀.obj,将编译后得到的所有目标文件(模块)连接装配,在与库函数连接成一个整体,生成可供计算机执行的目标程序,称为可执行文件
    在这里插入图片描述
    注:以上连接的工作由一个称为“连接编辑程序”的软件实现
    其中实现表示操作流程,虚线表示文件的输入输出
    集成开发环境(IDE):把程序的编辑、编译、连接、运行

  6. 程序分析任务
    (1)问题分析
    (2)设计算法
    (3)编写程序
    (4)对源程序进行编辑、编译、连接
    (5)运行程序、分析结果
    (6)编写程序文

  7. 结构化程序设计
    (1)自顶向下
    (2)逐步细化
    (3)模块化设计
    (4)结构化编码

算法

  1. 程序:
    (1)对数据的描述(数据结构)
    (2)对操作的描述(算法)
  2. 计算机算法
    (1)数值运算算法:求数值解
    (2)非数值运算算法:事务管理领域
  3. 算法的特性(掌握)
    (1)有穷性:步骤有限
    (2)确定性:步骤确定
    (3)有零个或多个输入
    (4)有一个或多个输出
    (5)有效性:步骤有效,若不能除以0
  4. 基本结构
    (1)顺序结构
    (2)选择结构
    (3)循环结构
    1)当型(while型)循环结构:先判定后执行
    2)直到型(until型)循环结构:先执行后判定
    (4)共同特点:
    1)只有一个入口
    2)只有一个出口
    3)结构内的每一部分都有机会被执行到
    4)结构内不存在死循环

数据

  1. 数据分为常量和变量
  2. 常量:其值不能被改变的量(掌握)
    (1)整形常量
    (2)实型常量:十进制小数,指数e或E前必须有数字,其e或E后必须是整数
    (3)字符常量:
    普通字符:ASCII代码存储,单撇号括起来的一个字符
    转义字符:
    在这里插入图片描述
    (4)字符串常量:双撇号把若干个字符括起来
    符号常量 用#define指令,指定用一个符号名称代表一个常量
    编译前,预处理器先对其进行处理,预编译后全部变成字面常量,不占空间,符号不存在,不能赋值
    习惯大写,区别变量名
    例: #define PI 3.1416//无分号
    好处:含义清楚;多处用到需要改变时可以一改全改
  3. 变量:有名字的具有特定属性的一个存储单元(掌握)
    (1)先定义后使用
    (2)变量名:一个名字代表的存储地址,编译连接时编译系统分配对应内存地址,变量取值找到对应的内存地址,该存储单元读取数据
  4. 常变量:变量类型前加const,变量存在期间不能改变
  5. 标识名:用于对常量名、函数、数组、类型等命名的有效字符序列(掌握)
    规范:所有字符必须是字母、数字、下划线,首字符必须是字母或下划线
  6. 数据类型(了解)
    在这里插入图片描述
    (1)算术类型:基本类型和枚举类型,值都是数值
    纯量类型:算术类型和指针类型,值是数字表示
    枚举类型是用户定义的整数类型
    (2)组合类型:数组类型和结构体类型
    共用体不属于组合类型,因为同一时间内只有一个成员具有值
    函数类型用未定义函数,描述一个函数的接口,包括函数返回值的数据类型和参数的类型
  7. 整数类型(掌握)
    (1)基本整形(int)4字节
    正数反码和补码是其本身,负数反码是其绝对值取反,补码是其反码加1
    32位取值范围 − 2 32 {-2}^{32} 232~ 2 32 − 1 {2}^{32}-1 2321
    (2)短整形(short int)2字节
    范围 − 2 15 {-2}^{15} 215~ 2 15 − 1 {2}^{15}-1 2151 4字节
    (3)长整型(long int)
    取值范围 − 2 32 {-2}^{32} 232~ 2 32 − 1 {2}^{32}-1 2321
    (4)双长整型(long long int) 8字节
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    注:只有整形数据(包括字符型)数据可以加signed或unsigned,实型数据不能
    无符号整形数据用“%u”输出,表示用无符号十进制数的格式输出
  8. 字符型
    (1)字符共127个
    (2)字符变量char(character缩写)
  9. 浮点型:小数位置可以浮动,实数的指数形式称浮点数
    (1) float型
    在这里插入图片描述
    计算机中二进制数来表示小数部分,2的次幂来表示指数部分
    (2)double型:浮点运算时都会将float型数据自动转化为double型,提高精确度
    在这里插入图片描述
  10. 常量类型
    (1)常量是长整型则在末尾加L或l 例123L
    (2)常量是单精度浮点型在末尾加F或f 例1.2f,系统默认实数常量为双精度浮点型
运算符
  1. 算术运算符:先自左向右,比较前后运算符优先级(掌握)
  2. 强制类型运算符:(类型名)(表达式)(掌握)
    在这里插入图片描述
    在这里插入图片描述
C语句
  1. 源文件包含若干个函数、预处理指令以及全局变量声明部分 ,一个函数包含声明部分和执行部分

  2. c语句类型:(掌握)
    在这里插入图片描述

  3. 控制语句:9种
    (1)控制语句
    在这里插入图片描述
    (2)函数调用语句:一个函数调用加一个分号
    (3)表达式语句:一个表达式加一个分号构成
    赋值语句:赋值表达式后加分号,注:x+y;合法,但无实际意义
    (4)空语句:只有分号
    (5)符合语句:用{}把一些语句和声明括起来成复合语句(又称语句块)

  4. 赋值语句(掌握)
    赋值表达式:标量 赋值运算符 表达式 a=3*5
    注:占字节多的类型变量转为少的可能数据会失真,且赋值要在类型允许的数值范围内
    赋初值时不能写成 int a=b=c=3;形式

数据的输入输出(掌握)
  1. printf中"%7.2lf":指定数据占7列,其中小数占2列
    好处:(1)通过实际需要输出小数位数(2)小数点对齐,数据整齐美观

  2. C语言本身不提供输入输出,输入输出来自C语言标准库函数实现

  3. putchar(输出字符)getchar(输入字符) printf(格式输出)scanf(格式输入)puts(输出字符串) gets(输入字符串)

  4. 要在程序文件的开头用预处理指令#include 把有关头文件放在本程序中
    #include <>:编译系统从存放c编译系统的子目录中去找所要包含的文件,称为标椎方式
    #include"" :编译系统现在用户的当前目录中寻找要包含的文件,若找不到,在按标椎方式查找。若在其他目录,写出文件路径(如#include “C:\temp\file.h”)

  5. printf函数一般格式:printf(“格式控制”,输出表列);
    “格式控制”:格式控制字符串,简称格式字符串
    格式声明:"%d"和格式字符组成
    普通字符:原样输出
    输出列表:需要输出的数据,常量,变量,表达式
    printf函数一般形式:printf(参数1,参数2…,参数n)

  6. 格式字符
    (1)int%d long int%ld long long int%lld
    (2)char %c 输出的数字超过范围则取范围内的数转换 例:如下图%c 取后八位输出y
    在这里插入图片描述
    (3)float,double,long double
    %f实数整数部分全部输出,小数部分后六位
    %m.nf:右对齐,宽度m列,小数占n列
    float:6位有效数字,double:15位有效数字
    %-m.nf:左对齐
    (4)字符串 %s
    (5)实数 %e指数形式输出 默认小数位数6位,指数部分5列,共13列
    在这里插入图片描述
    (6)八进制:%o不带符号 十六进制%x
    在这里插入图片描述
    注:除X,E,G,F外,其他格式字符必须用小写
    输出‘%’需要连续两个’%'表示

  7. scanf用法
    (1)scanf一般形式scanf(格式控制,地址表列)
    地址表列:若干个地址组成的表列,可以是变量的地址,或字符串首地址
    在这里插入图片描述
    在这里插入图片描述
    字符输入输出函数
    1.putchar(a): 输出一个字符
    2.char a = getchar(): 输入一个字符

选择和循环结构

选择结构
  1. if语句:表达式可以是关系表达式、逻辑表达式、数之表达式
    if(表达式) 语句1
    [else 语句2]
  2. 关系运算符优先级(掌握)
    在这里插入图片描述
    关系表达式:将两个数值或数值表达式连接起来的式子
  3. 逻辑表达式:将关系表达式或其他逻辑量连接起来的式子(掌握)
    在这里插入图片描述
    逻辑运算符优先级:! && ||
  4. 条件运算符:表达式 1? 表达式 2: 表达式 (掌握)
  5. switch语句:多分支选择语句(掌握)
    switch(表达式)
    {
    case 常量1 : 语句1
    case 常量2 : 语句2
    default : 语句 n
    }
    注:(1)一般语句后加break;结束,否则从某个符合case一直往后执行,不会再判断
    (2)switch括号内表达式类型必须为整数类型(包括字符型)
    (3)switch下面的花括号内是一个复合语句
    (4)case 和default都是起标号的作用,用来标志一个位置,case中无匹配执行default
    (5)可以无default,也可随意放置default先后顺序,case中常量必须互不相同
循环结构

while do…while… for(; ; )

数组

数组
  1. 数组:一组有序数据的集合,每个元素都属于同一类型
  2. 第一一维数组形式 类型说明符 数组名[常量表达式];
    数组元素的表示形式:数组名[下标]
    注:只能是常量 如a[3+7]
    在这里插入图片描述
  3. 一维数组初始化
    (1)在定义数组时对全部数组元素赋予初值
    (2)可以只给数组的一部分赋值,未赋值的部分自动设置为0,字符型数组初值’\0’,指针数组初始化NULL
    (3)如果赋值个数是全部元素,可以写成a[]={1,2,3,4,5};
    a[5]={1,2,3,4,5};
  4. 二维数组
    (1)定义形式:类型说明符 数组名[常量表达式][常量表达式]
    注:在内存中各元素是连续存放的,不是二维的,是线性的
    在这里插入图片描述(2)二维数组元素的表示形式为:数组名[下标][下标]
  5. 二维数组的初始化
    (1)分行给二维数组赋初值 int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    (2)可以将所有数据写在一个花括号内,按数组元素在内存中的排列顺序对各元素赋初值 int[2][3]={1,2,3,4,5,6};
    (3)可以对部分元素赋值int a[3][4]={{1},{2},{3}};
1000
2000
3000
(4)可以只对某几行赋值 int a[3][4]={{1},{5,6}}; int a[3][4]={{1},{},{9}};
1000
5600
0000
(5)如果对全部元素都赋初值(即提供全部初始数据),则定义数组对第一维的长度可以不指定,但第二维长度不能省
int a[][4]={1,2,3,4,5,6,7,8,9};
int a[3][4]={{1,2,3,4},{5,6,7,8},{9}};
  1. 字符数组
    (1)字符数组初始化 char c[10]={‘l’,’ ‘,‘a’,‘m’};
    花括号个数大于上限语法错误,小于上限未赋值部分赋初值’\0’
  2. 字符串 char c[]={“I am happy”};或char c[]=“I am happy”;
    字符串结束标志’\0’,用子数组存储字符串常量时会自动加一个’\0’作为结束符,占一个字节
  3. 字符数组和字符串区别:字符数组并不要求它的最后一个字符为’\0’,字符串会自动在结尾赋值’\0’,所以char c[]=“I am happy”;与char c[]={‘I’,’ ‘,‘a’,‘m’,’ ',‘h’,‘a’,‘p’,‘p’,‘y’};前者长度比后者多1
  4. 字符数组的输入输出:
    (1)逐个输入输出:%c
    (2)整个字符串一次输入或输出:%s
    注:(1)遇到第一个’\0’输出结束
    (2)遇到空格输入结束,字符串后全部赋值’\0’,且scanf输入的变量名前不需要地址符
字符串函数
  1. puts(字符数组):输出字符串
  2. gets(字符数组):输入字符串
  3. strcat(字符数组1,字符数组2):字符串连接
    注:将字符数组2接在字符数组1最后一个非’\0’字符后,所以字符数组1足够大
  4. strcpy(字符数组1,字符串2)和strncpy(字符数组1,字符串2,n):字符串复制
    注:(1)将字符数组2复制到字符数组1中,所以字符数组1长度不小于字符串2(包括’\0’),strncp是复制字符串2前n个到字符数组1,但n不超过str2字符个数(不包括’\0’),所以字符数组1要在最后手动赋值’\0’
    (2)字符数组1必须是数组名形式,字符串2可以是字符串常量
    (3)不能用赋值语句直接将字符串常量赋值给字符数组变量名,变量名只是一个地址
    在这里插入图片描述
  5. strcmp(字符串1,字符串2):字符串比较
    注:(1)规则:自左向右逐个按ASCII码比较,知道出现不同或’\0’为止
    全部相同:字符串相等
    出现不同,则以第一对不相同字符的比较大小为准,ASCII小的则字符串小
    (2)返回值:字符串1==字符串2,返回0
    字符串1>字符串2,返回正整数
    字符串1<字符串2,返回负整数
  6. strlen(字符数组):测字符串长度
    注:测的是字符串实际长度(不包括’\0’)
    char str[10]=“China”; strlen(str)结果为10
  7. strlwr(字符串):将所有大写字母转换为小写,其他字符不变
  8. strupr(字符串):将所小写字母转换为大写,其他字符不变
    注:以上函数头文件#include <string.h>

函数

  1. 模块化程序设计

  2. 一个C程序可由一个主函数和若干个其他函数构成
    (1)一个C程序:一个或多个程序模块组成,每一个程序模块作为一个源程序文件。
    注:较大的程序一般按分类放在若干个源程序文件中,由若干个源程序文件组成一个C程序,一个源程序文件可以为多个C程序共用
    (2)一个源程序文件:一个或多个函数以及其他相关内容(指令、数据声明与定义等)
    注:程序编译是以源程序文件为一个编译单位
    (3)程序执行从main函数开始
    (4)每个函数相互平行独立,不能嵌套定义,且main函数只能被系统调用
    (5)按用户分类:库函数;用户自定义函数
    (6)函数形式分类:无参;有参

  3. 函数的定义:先定义后使用
    (1)指定函数名
    (2)指定函数类型
    (3)指定函数参数名和类型
    (4)指定函数功能

  4. 定义函数的方法
    (1)定义无参函数
    (2)定义有参函数
    (3)定义空函数

  5. 调用函数
    形式:函数名(实参表列)
    (1)函数调用语句:即单独调用max(1,2)
    (2)函数表达式:调用出现在表达式中c=max(1,2);
    (3)函数参数:函数作为另一个函数的参数 如max(max(1,2),3);

  6. 形参:定义函数中的参数
    实参:调用函数中的参数
    虚实结合:函数过程中发生的实参与形参键的数据传递

  7. 函数调用过程
    (1)形参在函数未调用时不占内存存储单元;发生调用时,形参会被临时分配空间
    (2)实参传值给形参,对形参值进行处理,return返回值到主调函数,结束调用,形参释放
    注:形参和实参地址不同不会相互影响,只能将实参的值赋值给形参,单向传递
    在这里插入图片描述

  8. 函数值说明
    (1)return 获取 return (a);或return a;
    (2)函数值类型要与return表达式类型一致,函数类型决定返回值类型

  9. 调用函数的声明和函数原型
    (1)调用的函数必须已定义
    (2)函数的首行称为函数原型

  10. 函数类型种类
    (1)函数类型 函数名(参数类型1 参数名1,参数类型2 参数名2,…,参数类型n 参数名n);
    (2)函数类型 函数名(参数类型1,参数类型2,…,参数类型n);

  11. 函数的调用
    (1)嵌套调用:在调用函数中,又调用其他函数
    (2)递归调用:直接或间接地调用该函数本身

  12. 数组作为函数参数
    (1)数组元素可以做函数实参但不能用作形参。 在用数组元素作函数实参时,把实参的值传递给形参,是“值传递”方式。数据传递的方向是从实参传到形参,单向传递。
    (2)数组名做函数参数(实参和形参)
    注:数组名做函数实参时,向形参(数组名或指针变量)传递的是数组首元素的地址。
    注:数组形参的大小无任何作用,因为只是将实参的首地址传给形参数组名,并不检查形参的大小,同时如果改变形参值,实参也会改变
    (3)多维数组作形参,除第一维外,其他维度大小不能省略,因为二维数组是由若干个一维数组组成,在内存中数组按行存放,必须确定一行包含几个元素
    例:实参 int score[5][10] 形参 int array[][10]或int array[8][10]

  13. 局部变量
    (1)在函数的开头定义
    (2)在函数的复合语句内定义:称分程序(程序块)
    注:形式参数也是局部变量

  14. 全局变量
    也称外部变量:函数之外定义的变量
    注:(1)全局变量习惯第一个字母大写
    (2)变量名相同时:局部变量优先级大于全局变量
    (3)建议不必要使用全局变量原因:
    1)全部执行过程中都占用空间
    2)使函数通用性降低(文件移植变量冲突)
    3)降低代码清晰性
    代码要求:高内聚低耦合,移植性好,可读性高

  15. 变量存储方式和生存期
    (1)变量存储的两种方式:静态存储方式和动态存储方式
    静态存储:程序运行期间由系统分配固定的存储空间方式
    动态存储:在程序运行期间根据需要进行动态分配存储空间的方式
    存储空间:1)程序区 2)静态存储区:全局变量,开始执行程序分配,程序结束完毕释放 3)动态存储区:函数形式参数;自动变量;函数调用时的现场保护和返回地址。动态分配和释放空间,所以两次调用同一函数地址不一定相同

  16. 数据存储类别
    自动的(auto):函数调用结束后自动释放,可省略auto
    静态的(static):函数调用后不会消失而继续保留,即其占用的存储单元不释放,再次调用时该变量已有值
    寄存器的(register):将局部变量存储在CPU的寄存器中
    外部的(extern):外部变量声明,表示将外部变量的作用域扩展到此位置 extern int A,B,C;或extern A,B,C;
    注:auto static register不能单独使用

  17. 全局变量的存储类别
    (1)全局变量都是存放在静态存储区汇总
    (2)外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始到程序文件的末尾
    (3)编译遇到extern,先在文本文件中找外部变量的定义,找不到再到连接时从其他文件中找外部变量的定义,还是找不到报错
    (4)将外部变量的作用域限制在本文件中,使用static声明,称为静态外部变量
    注:局部变量的类型声明和全局变量的类型声明意义不同
    局部变量声明存储类型的作用指定变量存储的区域以及由此产生的生存期问题,而对于全局变量来说,由于都是编译时分配内存的,都存放在静态存储区,声明存储类型的作用是变量作用域的扩展问题

  18. 归纳:在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    注:变量的有效范围称为作用域(性质:可见性)
    变量存在的时期称为变量的生存期(性质:存在性)

  19. 变量的声明与定义:建立存储空间的声明称定义(定义性声明),不需要建立存储空间的声明称为声明(引用型声明)(如:extern)

  20. 内部函数和外部函数
    内部函数(静态函数):static 类型名 函数名(形参表);
    外部函数:extern 类型名 fun(形参表)
    extern可以默认省略
    注:声明时可以省略static和extern

指针

  1. 指针:地址指向该变量单元(包括存储单元编号表示的存地址和存储单元的数据类型)
    2.(1)直接访问:按变量名进行访问
    (2)间接访问:将变量i的地址存放在另一变量中,然后通过该变量来找到变量i的地址
    例:i_pointer=&i;
    (3)赋值i=3等价于* i_pointer = 3; * i_pointer表示指向的位置,其称为指针变量(地址变量),指针变量的值是地址
    注:区分指针和指针变量
  2. 定义指针变量:类型名 *指针变量名;
    基类型:用于指定此指针变量可以指向的变量类型。必须有类型名
    *:表示是指针型变量
    指针类型文字表示:指向整形数据的指针类型表示为“int *”,读作“指向int的指针”或简称"int指针”
    注:指针不能赋值常数,同时必须赋值,不然会随机指向不确定的空间,导致原始有用数据受影响
  3. 各种形式的含义
#include<stdio.h>
int main(){
	int a = 10;
	int *point = &a;
	printf(" a = %d(a的值)\n &a = %d(a的地址)\n point = %d(a的地址)\n *point = %d(a的值)\n &point = %d(point的地址)\n",a,&a,point,*point,&point);
	return 0;
} 

在这里插入图片描述

  1. 指针做函数形参,是将实参变量地址传入调用函数;值传递是将实参的值赋给创建的形参
    注意:指针做形参也符合值传递,所以形参指针直接交换指向地址,不改变实参指针指向地址

  2. 所谓数组元素的指针就是数组元素的地址。引用数组元素可以是下标法也可以是指针法。例p=&a[0];等价于p=a;
    (1)p+1指向同一数组中的下一个元素,p-1指向同一数组的上一个元素
    (2)“[]”:变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值
    (3)地址相减:是指地址值相减除以类型长度,相减时类型必须相同
    (4)引用数组元素:1)a[i] 2)*(a+i) 或 *(p+i)
    注:1)数组名a代表数组首元素的地址,是一个指针常量,所以不能移动,如:a++,指针p移动p++要更快,因为指针变量直接指向元素,不必每次重新计算地址。
    2)指针也可以p[2],是从p指向的位置开始,容易写错尽量少用

  3. (1) *p++等价于 *(p++):先输出 *p指向值,p值加一
    (2) *(++p):p值加一,再输出 *p指向值
    在这里插入图片描述

  4. 二维数组指针
    int a[4][3];
    (1)a:二维数组首地址
    (2)a+1:&a[1],行地址加一,指向a[1],第二行a[1]首地址
    (3) *(a+1):a[1]即&a[1][0],a[1][0]地址
    (4) *(a+1)+1: &a[1][1],a[1][0]地址
    (5) *( *(a+1)+1):a[1][1]或 *(a[1]+1)或 *(a[1]+1)
    (6)地址等价:a+i、a[i]、 *(a+i)、&a[i]、&a[i][0]
    在这里插入图片描述
    注:a[0]与a地址相同,但基类型不同,指向的数据类型不同,前者是整形数据,后者是一维数组。指向一维数组的指针:int ( * pt)[4];
    在这里插入图片描述

  5. 指向多维数组的指针
    (1)指向数组元素的指针变量
    int p = &a[0][0]
    a[i][j]的地址是&a[0][0]的地址为“&a[0][0]+(i * m+j)”或“p+(i * m + j)”
    a[2][3]的地址是(p+2
    4+3)即(p+11)
    (2)指向由m个元素组成的一维数组的指针变量
    int ( *p)[4] = &a[0]
    p+1指向a[1],p的增值以一维数组的长度为单位
    注:int ( * p)[4]类型int ( *)[4]型,p被定义为指向一维整形数组的指针变量,一维数组有4个元素,因此p的基类型是一维数组,其长度是16字节

int main() {
	int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
	int (* p)[4] = a;
	printf("%d\n", p);//指向a[0]的首地址
	printf("%d\n", p+1);//指向a[1]的首地址
	printf("%d\n", *p+1);//指向a[0][1]的首地址 
	printf("%d\n", (*p + 1)[1]);//a[0][1]的值 
	printf("%d\n", *(p+1)[1]);//a[2][0]的值 []优先级大于* 
	printf("%d\n", (*(p+1))[1]);//a[1][1]的值 
	printf("%d\n", *(p+1)+1);//指向a[1][1]的地址 
	printf("%d\n", *(*(p+1)+1));//a[1][1]的值
	 
	return 0;
}

在这里插入图片描述

  1. 指向数组的指针做函数参数
    (1)用指向变量的指针变量
    void fun1(int *p, int n);
    int a[3][4];
    fun1(*a);
    (2)用指向一堆数组的指针变量
    void fun2(int (*p)[4],int n)
    int a[3][4];
    fun2(a);
    注:注意类型,不同类型不能赋值

  2. 指针引用字符串
    (1)使用字符串的更加灵活的方法就是使用指针
    输出整个字符printf(“%s\n”,string);
    注:c语言中只有字符变量,没有字符串变量,char *string;被定义为一个指针变量,基类型为字符型
    (2)形参和实参可以分别用字符数组名或字符指针变量
    在这里插入图片描述

  3. 使用字符指针变量和字符数组的比较
    (1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量存放的是地址
    (2)赋值方式:可以对字符指针变量赋值,但不能对数组名赋值
    char *a;
    a = “I love China!”;//正确,将字符串首元素地址赋给指针变量
    char str[14];
    str=“I love China!”;//错误,数组名是地址是常量
    (3)初始化的含义
    char *a = “I love China!”;
    str[]=“I love China!”;//错误,企图把字符串赋值给各元素
    char str1[14] = “I love China!”;//正确
    (4)存储单元内容
    编译时为字符数组分配若干存储单元,存放各值,二对字符指针变量,只分配一个存储单元
    注:指针变量一定要赋初值
    (5)指针变量的值可以改变,字符数组名代表一个固定值,不能改变。
    char str[]=“I love China!”;
    str = str + 7;//错误
    (6)字符数组中各元素的值可以改变,但字符指针变量指向的字符串常量的内容不可改变
    char a[]="I love China!";
    char * p="I love China!";
    a[2] = 'r';//正确
    p[2] = 'r';//错误
    (7)引用数组元素。a[5]或 *(a+5)
    (8)用指针变量指向一个格式字符串
    char * format;
    format=“a=%d,b=%f\n”;
    printf(format,a,b);
    称为可变格数输出函数
    注:也可以用字符数组实现,但只能在采用定义数组时初始化或逐个对元素赋值的方法

  4. 指向函数的指针
    (1)函数名代表函数的地址
    int ( * p)(int ,int);//指向只有两个整形参数的函数。p类型:( *)(int,int)表示
    (2)通过指针变量调用它所指向的函数
    int max(int x, int y){return z;}
    主函数:
    int max(int,int);
    int ( *p)(int,int);
    p = max;//赋值函数入口地址
    int a = 1, b =2;
    int c = ( *p )(a,b);
    注: int *p(a,b);括号优先级高,所以代表先声明一个p的函数,这个函数的返回值是指向整型变量的指针
    (3)用指向函数的指针作函数参数
    将函数的入口地址作为参数传递到其他函数
    如:void fun(int ( *x1)(int),int( *x2)(int ,int)){}

  5. 返回指针值的函数,即地址
    类型名 * 函数名(参数表列)

  6. 指针数组和多重指针
    (1)若其元素均为指针类型数据,称为指针数组
    形式:类型名 * 数组名[数组长度]
    char *a[5]={“good”,“bad”,“hi”,“food”,“too”}
    函数声明
    void fun(char *a[], 5);
    函数调用
    fun(a,5);
    注:指针数组的元素只能存放地址,不能存储方整数
    (2)指向指针数据的指针变量
    指向指针数据的指针变量,简称为指向指针的指针
    int **p;
    p=a;
    (3)指针数组作main函数的形参
    1)main函数形式
    int main()
    int main(void):表示函数没有参数,调用main函数时不必给出实参
    一般形式:int main(int argc, char * argv[])
    argc(argument count):参数个数
    argv(argument vector):参数向量,一个 *char指针数组,数组每一个元素指向命令行中的一个字符串的首字符
    2)为什么main需要参数
    main函数是操作系统调用,实参只能由操作系统给出。在操作命令状态下,实参是和执行文件的命令一起给出的。
    命令行的一般形式
    命令名 参数1 参数2…参数n
    命令名是可执行文件(包含main函数)
    如:执行文件名file1.exe,将“china”和“Beijing”做为传给main函数的参数
    命令行形式:file1 China Beijing
    以上命令行参数个数为3,命令行都是字符串,字符串首地址构成指针数组

  7. 动态内存分配与指向他的指针变量
    (1)栈:非静态局部变量分配在内存中的动态存储区
    (2)堆:建立动态分配区域,需要时开辟,不需要时释放,只能通过指针引用
    (3)存储动态分配的库函数
    头文件#include<stdlib.h>
    1)molloc函数开辟动态存储空间
    void *malloc(unsigned int size);
    2)calloc函数开辟动态存储空间
    void *calloc(unsigned n, unsigned size)
    3)realloc函数重新分配
    void *realloc(void *p, unsigned int size);
    4)free函数释放动态存储区
    void free(void * p);
    (4)void指针类型
    解释:指向空类型或不指向确定的类型
    在动态分配数组时默认类型为void *,然后强转赋值

  8. 归纳
    在这里插入图片描述
    注:
    (1)地址型的数据包含:
    1)表示内存编号的纯地址
    2)它本身的类型,即指针类型
    3)以它为标识的存储单元中存放的是什么类型的数据,即基类型
    (2)int *p = NULL指针变量指向空
    stdio.h 头文件定义 #define NULL 0
    (3)区别指针和指针变量:指针就是地址,指针变量是用来存放地址的变量

自定义数据类型

  1. 结构体:用户自己建立由不同类型数据组成的组合型的数据结构
    一般形式:struct 结构体名
    {成员表列}
    成员表列也称“域表”,每一个成员是结构体中的一个域。成员名命名与变量名相同。

  2. 定义结构体类型变量(掌握)
    (1)先声明,在定义
    struct Student{
    int num;
    int sex;
    };
    struct Student student1;
    (2)声明的同时定义变量
    struct Student{
    int num;
    char sex;
    }student1;
    (3)不指定类型名而直接定义结构体类型
    struct{
    成员列表
    }变量名表列
    struct{
    int num;
    char sex;
    }student1={1,‘a’};

struct Birthday{
	int day;
	int monday;
};
struct Student{
	int num;
	char name[10];
	Birthday bt;
};
int main(){
	Student student1 = {1, "Zhang"};
	Birthday bir = {11,1};
	student1.bt = bir;
	
	printf("%d %s %d %d",student1.num,student1.name,student1.bt.day,student1.bt.month);
}
  1. 结构体指针
    (1)stu. 成员(如stu.num)
    (2)(*p).成员名
    (3)p->成员名

  2. 指针处理链表
    (1)建立静态链表
    (2)建立动态链表

  3. 共用体(了解)
    union 共用体名
    {成员表列}变量表列;
    注:同一时刻只能对成员变量其中一个进行赋值,初始化也只需一个值
    如:
    union Data{
    int i;
    char ch;
    float f;
    }a={16};
    a.i = 10;//赋值后所有类型值都将改变
    注:共用体变量地址和它各成员的地址都是同一地址
    不能对共用体赋值,C99可以共用体变量作为函数参数,以前只能用指向共用体的指针做参数

  4. 枚举类型(了解)
    (1)枚举所有可能值(花括号中为枚举常量或枚举元素)
    例:enum Weekday{sun,mon,wed,thu,fri,sat};
    (2)枚举变量
    enum Weekday workday;
    注枚举常量默认从0开始自动赋值,例sun=0,mon=1
    enum Weekday{sun=7,mon,wed,thu,fri,sat};//常量只有定义时才能自定义赋值

  5. typedef声明新类型(掌握)
    使用方法:
    (1)先定义变量的方法写出定义体
    (2)将变量名换成新类型名
    (3)在最前面加typedef
    (4)然后用新类型名定义变量

优先级

1、圆括号【()】、下标运算符【[]】、分量运算符的指向结构体成员运算符【->】、结构体成员运算符【.】;

2、逻辑非运算符【!】、按位取反运算符【~】、自增自减运算符【++】【 --】、负号运算符【-】、类型转换运算符【(类型)】、指针运算符和取地址运算符【*】【&】、长度运算符【sizeof】;

3、乘法运算符【*】、除法运算符【/】、取余运算符【%】;

4、加法运算符【+】、减法运算符【-】;

5、左移动运算符【<<】、右移动运算符【>>】;

6、关系运算符【< 】【>】【<=】【 >= 】;

7、等于运算符【==】、不等于运算符【!=】;

8、按位与运算符【&】;

9、按位异或运算符【^】;

10、按位或运算符【|】;

11、逻辑与运算符【&&】;

12、逻辑或运算符【||】;

13、条件运算符【?:】;

14、赋值运算符【=】【/=】【*=】【%=】【+=】【-=】【<<=】【>>=】【&=】【^=】【|=】;

15、逗号运算符【,】。

位运算

http://blog.chinaunix.net/uid-21411227-id-1826986.html

排序

#include <stdio.h>

//插入排序 假设当前元素前都已排好序,只要将当前元素插入前面符合的位置
/*空间复杂度O(1)
最好时间复杂度O(n) 比较次数最少n-1
最坏时间复杂度O(n^2) 比较次数最多n*(n-1)/2
平均时间复杂度O(n^2)
稳定 
*/ 
void insert_sort(int *nums, int n){
	for(int i = 1; i < n; i++){
		int cur = nums[i];//暂存当前元素 
		int j = i-1;
		for(; j>=0&&nums[j] > cur; j--)//将其前面大于当前元素的元素后移一位 
			nums[j+1] = nums[j];
		nums[j+1] = cur;//插入符合的位置 
	}
}

//折半插入排序:先折半查找,再插入 
void binary_insert_sort(int *nums, int n){
	for(int i = 1; i < n; i++){
		int cur = nums[i];
		int left = 0, right = i-1;
		while(left<=right){//二分查找,找到符合的位置 
			int mid = (left + right)/2;
			if(nums[mid] > cur){
				right = mid-1;
			}else{
				left = mid+1;
			}
		}
		for(int j = i-1; j > right; j--){
			nums[j+1] = nums[j];
		}
		nums[left] = cur;
	}
} 

//希尔排序 
/*
空间复杂度O(1)
时间复杂度:未知
不稳定 
*/ 
void shell_sort(int *nums, int n){
	for(int d = n/2; d>=1; d = d/2){
		for(int i = d+1; i < n; i++){
			int cur = nums[i];
			int j = i-d;
			for(;j >= 0&&nums[j] > cur; j=j-d){
				nums[j+d] = nums[j];
			}
			nums[j+d] = cur;
		}
	}
} 
//冒泡排序 : 从后往前两两比较,前者大于后者则交换,经过一轮交换将最小值移到最前面 
/*空间复杂度O(1)
最好时间复杂度O(n) 比较次数最少n-1
最坏时间复杂度O(n^2) 比较次数最多n*(n-1)/2
平均时间复杂度O(n^2)
稳定 
*/ 
void bubble_sort(int *nums, int n){
	for(int i = 0; i < n-1; i++){//执行n-1轮 
		bool flag = false; 
		for(int j = n-1; j > i; j--){//从后往前两两比较 
			if(nums[j-1]  > nums[j]){//前者大于后者交换 
				int temp = nums[j];
				nums[j] = nums[j-1];
				nums[j-1] = temp;
				flag = true; 
			}
			
		}if(flag==false)break;//没有执行交换,说明都排好序了,停止 
	}
}

void bubble_sort2(int *nums, int n){
	for(int i = n-1 ;i > 0; i--){
		bool flag = false; 
		for(int j = 0; j < i; j++){
			if(nums[j+1]  < nums[j]){
				int temp = nums[j];
				nums[j] = nums[j+1];
				nums[j+1] = temp;
				flag = true;
			}
		}
		if(flag==false)break;
	}
} 

//选择排序 :每次选择最小的元素,与当前排好序的元素末尾元素交换 
/*空间复杂度O(1)
时间复杂度O(n^2) 比较次数n*(n-1)/2
不稳定 
*/ 
void select_sort(int *nums, int n){
	for(int i = 0 ; i < n; i++){
		int cur_min = nums[i];//存储当前元素 
		int cur_min_index = i;//存储当前元素位置 
		for(int j = i+1; j < n; j++){
			if(cur_min > nums[j]){//找最小值位置和元素值 
				cur_min = nums[j];
				cur_min_index = j;
			}
		}
		if(i!=cur_min_index){//如果最小值不等于当前元素,则交换 
			int temp = nums[i]; nums[i] = cur_min; nums[cur_min_index] = temp;
		}
	}
}

//快速排序 : 从第一个元素开始,将小于该元素的值放在该元素的左边,大于该元素的放在该元素右边,不断分治执行上述操作 
/*最好空间复杂度O(n)
最坏空间复杂度O(log2(n)) 
最好时间复杂度O(nlog2(n)):如均等分的数组 
最坏时间复杂度O(n^2):如排好序的数组 
平均时间复杂度O(nlog2(n))
不稳定 
代码记忆方法:
1.找中轴
2.左边快排
3.右边快排 
找中轴也分为三步:
1.选定一个中轴(以最左边的中轴为例)
2.从右往左找,右边赋值给左边
3.从左往右找,左边赋值给右边
*/ 
int partition(int *nums, int left, int right){
	int cur = nums[left];//存储第一个元素,便空出一个位置 
	while(left<right){
		while(left<right&&cur<=nums[right])right--;//从右往左找到比第一个元素小的元素,大的不用考虑 
		nums[left] = nums[right];//将小的元素放在空出位置,即第一个元素左边,right位置空出 
		while(left<right&&cur>=nums[left])left++;//从左往右找到比第一个元素大的元素 
		nums[right] = nums[left];//将大的元素放置在 right空出位置 
	} 
	nums[left] = cur;//存储第一个位置,代表将数组划分为大小两边 
	return left;//返回划分位置 
}

void quick_sort(int *nums, int left, int right){
	if(left<right){//不断划分 
		int index = partition(nums,left,right);
		quick_sort(nums, left, index-1);
		quick_sort(nums, index+1, right);
	}
}
void swap(int *a, int *b){
	int temp = *a;
	*a = *b;
	*b = temp;
}
//堆排序(大根堆) :
/*
1.先将数组变成大根堆
2.将树根元素与数组最后一个元素交换位置(每次排好数组最后一个数)
将交换后的树根元素下沉,将剩下元素继续符合大根堆,循环执行,直到全部交换完成
如何变成大根堆:
所谓大根堆即父节点值要大于左右子节点的值,从n/2-1开始才有子结点
从n/2-1开始下沉元素,直到父节点大于左右子结点
如何下沉元素: 
让当前要下沉的值,与其左右子结点比较,如果左右节点其中之一大于当前节点,则交换, 
当前子结点继续与子结点的子结点比较,以此类推,直到无子结点或者不能交换为止 
*/ 
/*
空间复杂度O(1)
时间复杂度 建堆O(n)、排序O(nlog2(n)),总 O(nlog2(n))
不稳定 
*/
void HeadAdjust(int *nums, int k, int len){
	for(int i = k*2+1; i < len; i*=2+1){
		if(i+1<len&&nums[i]<nums[i+1]){
			i++;
		}
		if(nums[i] > nums[k]){
			swap(&nums[i],&nums[k]);
			k=i;
		}else{
			break;
		} 
	} 
}

//小根堆
void RHeadAdjust(int *nums, int k, int len){
	for(int i=k*2+1; i < len; i*=2+1){
		if(i+1<len&&nums[i]>nums[i+1]){
			i++;
		}
		if(nums[k]>nums[i]){
			swap(&nums[k],&nums[i]);
			k = i;
		}else{
			break;
		}
	}
}
void BuildMaxHeap(int *num, int n){
	for(int i = n/2-1; i>=0; i--){
		HeadAdjust(num,i,n);
		//RHeadAdjust(num,i,n);
	} 
}
 

void heap_sort(int *nums, int n){
	BuildMaxHeap(nums,n);
	for(int i = n-1; i>=0; i--){
		swap(&nums[i],&nums[0]);
		HeadAdjust(nums,0,i); 
		//RHeadAdjust(nums,0,i); 
	} 
} 

 
#include<stdlib.h>

//归并排序 :将数组不断分割,回溯时将分割的两数组按从小到大比较合并 
/*
空间复杂度O(n)
时间复杂度O(nlog2(n))
稳定 
代码记忆:
1.左分割
2.右分割
3.左右合并
左右合并方法:
1.暂存数组
2.依次比大小
3.存储剩余元素 
*/ 
void merge(int *nums,int left,int mid, int right){
	int *temp = (int *)malloc((right-left)*sizeof(int));//创建right-left动态空间 
	for(int i = left; i <= right; i++){//将数组元素赋值给临时数组 
		temp[i-left] = nums[i];
	}
	int l1 = left, l2 = mid+1, i = left;//分成两个数组,一个从left开始,一个从mid+1开始 
	for(; l1<=mid&&l2<=right&&i <= right; i++){
		if(temp[l1-left] <= temp[l2-left]){//比较两数组元素大小,将较小的放入数组,依次类推 
			nums[i] = temp[l1-left];
			l1++; 
		}else{
			nums[i] = temp[l2-left];
			l2++;
		}
	}
	//若一个数组比较完,另一个还有元素,剩下的存放剩余数组元素 
	while(l1<=mid){nums[i++] = temp[l1-left];l1++;} 
	while(l2<=right){nums[i++] = temp[l2-left];l2++;}
}

void merge_sort(int *nums, int left, int right){
	if(left<right){//归并排序,不断分割递归 
		int mid = (left+right)/2;
		merge_sort(nums,left,mid);
		merge_sort(nums,mid+1,right);
		merge(nums,left,mid,right);
	}
} 
void print(int *nums, int n){
	for(int i = 0; i < n; i++){
		printf("%d ",nums[i]);
	}
	printf("\n");
}

int main(){
	int nums[10]={7,5,4,2,1,10,5,9,6,8};
	heap_sort(nums,10);
	print(nums,10);
	return 0;
}

归并排序升序每一趟结果
{10,18,4,3,6,12,1,9,15,8}
首先是分解,注意;的位置:
1)10,18,4,3,6;12,1,9,15,8
2)10,18,4;3,6;12,1,9;15,8
3)10,18;4;3;6;12,1;9;15;8
4)10;18;4;3;6;12;1;9;15;8

然后才是归并(merge),也是该题要的答案:
1)10,18;4;3;6;1,12;9;15;8
2)4,10,18;3,6;1,9,12;8,15
3)3,4,6,10,18;1,8,9,12,15
4)1,3,4,6,8,9,10,12,15,18

错题归纳

  1. #define后不需要加分号
  2. typedef不能增加新类型
  3. switch表达式只能是整型或字符型,case只能是常量可以case 1+2:
  4. 在一个c源程序文件中所定义的全局变量,其作用域由具体定义位置和extern说明来决定范围
  5. int *a, *b;
    a=b=(int *)malloc(sizeof(int));开辟同一片空间
  6. 对象基本特点:分类性,多态性,标识唯一性,封装性,模块独立性好
  7. 函数包括函数首部和函数体
  8. %f默认输出小数点后六位,写结果时不要忘记输出字符串的其他内容
  9. 算法基本结构单元顺序、循环、分支(选择)
  10. 只允许本源文件中所有函数使用的全局变量,类型为static
  11. 采用任何一种借助 “比较” 的排序算法对 7个记录构成的序列进行排序,最坏情况下所需进行的比较次数至少13。公式:log2(n!)
  12. static未赋初值为0
  13. 将程序prog.c编译后运行:prog –nla hello world则 * ( *(argv+2))是() A、 ’p’ B、 ‘-‘ C、 ‘h’ D、 ‘w’ 选(D)
  14. int a,* p = a;//这里号表示声明一个指针。这里的号就是说明p是一个指针变量
    |* p=3; //这里*号表示间址运算符。这里的星号表示取出p所指地址里面的内容
  15. 字符串常量赋值 const char *b; b = “12434”;字符数组不能分开赋值char b[] ={“12434”};
  16. 算法的空间复杂度是指算法在执行过程中所需要的计算机存储空间

算法题

  1. 最大公约数和最小公倍数

//最大公约数
//更相减损法递归 
int GCD1(int a,int b){
	if(a>b)
		return GCD1(b,a-b);
	else if(a<b){
		return GCD1(a,b-a);
	}else{
		return a;
	}
	
} 
//更相减损法迭代 
int GCD2(int a,int b){
	while(a!=b){
		if(a>b)a = a-b;
		if(a<b)b = b-a;
	}
	return a;
}

//辗转相除法递归
int GCD3(int a, int b){
	if(b == 0)return a;
	return GCD3(b,a%b);
}

//辗转相除法迭代 
int GCD4(int a,int b){
	int c = a%b;
	while(c){
		a = b;
		b = c;
		c = a%b;
	}
	return b;
}

//最小公倍数
int LCM(int a,int b){
	int c = GCD1(a,b);
	return a*b/c;
} 
int main(){
	int a,b;
	while(~scanf("%d%d",&a,&b))
		printf("%d\n",LCM(a,b));
}
  1. 用指向指针的指针的方法对10个字符串排序并输出。
#define N 5
#include<stdio.h> 
#include<string.h>
#include<stdlib.h> 
int main(){
	char *a[N],**p = a;
	
	for(int i = 0; i <N; i++){
//		a[i]=(char*)malloc(sizeof(char)*20);
		*(p+i)= (char*)malloc(sizeof(char)*20);
		gets(*(p+i));
	} 
	for(int i = 0; i < N; i++){
		for(int j = 0; j < N-i-1; j++){
			if(strcmp(p[j],p[j+1])>0){
				char* temp;
				temp = p[j];
				p[j] = p[j+1];
				p[j+1] = temp;
			}
		}
	}
	for(int i = 0; i< N; i++){
		puts(a[i]);
	}
} 
  1. 汉诺塔
#include<stdio.h>

void hanoi(int n, char a, char b, char c){
	if(n == 1){
		printf("将第%d层从塔%c移动到塔%c\n",n,a,c);
		return;
	}
	hanoi(n-1,a,c,b);
	printf("将第%d层从塔%c移动到塔%c\n",n,a,c);
	hanoi(n-1,b,a,c);
}
int main(){
	int n;
	scanf("%d",&n);
	hanoi(n,'A','B','C');
} 
  • 7
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Baal Austin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值