C语言复习
本文通过菜鸟教程、《C程序设计》课本以及课件对C语言难点进行复习梳理
C语言知识点大概梳理图
文章目录
一、程序设计基础
1、语言对比 [1]
首先,C++、Java和Python都是面向对象的编程语言,也都是强类型语言。
C++更接近于底层,方便直接操作内存。C++不仅拥有计算机高效运行的实用性特征,同时还致力于提高大规模程序的编程质量与程序设计语言的问题描述能力。
来源于C,C是一种面向过程的语言。由于C++源于C,它良好的兼容了C功能,完全支持C,因此既可以面向对象、也可以面向过程。近几年又在此基础上发展出C#,C#使用起来更类似于Java。
C++和C中都有指针的概念,C++还可以使用智能指针,boost库。指针可以理解为一个地址,可以由程序员使用指针直接拿到该地址的数据,但是由于指针在使用的过程中,如果没有处理好,容易出现空指针和野指针,带来一些麻烦,因此Java去掉了指针的功能,规避了指针使用中可能存在的混乱情况。Java在C++的基础上又做了更高级的封装,具有良好的平台间的可移植性。C++一次编写到处编译,而Java一次编译到处运行。Java中的功能C++都能够实现,只是相对来说在编程方面会比较复杂一些。
C++ 编译成本地机器码, 可以由操作系统直接运行,运行效率较高。
Java 编译成字节码,需由Java虚拟机读取运行, 所以效率偏低,但可通过JIT提高运行效率。
垃圾回收方面,C++需要程序员自己回收,对内存进行良好的管理,而Java则不需要程序员自己完成这个工作,有自己的垃圾回收机制。
各种语言的基本语法都是相似的,比如int、float、double、bool等基本数据类型。
但是实现起来肯定是不同,比如C++把声明和实现分别放在头文件和源文件中,而Java则不使用头文件。C++引用其他内容时需要include,而Java则用package和import。这些都是细节的差别,但都是最基础的内容。
Java还可以应用到Android开发和JSP开发中。Java可以用来编写Android的应用程序,C#也可以。JSP则是用Java来做网站的。
Python是一种脚本语言,面向对象的思想个人认为体现的不是很明显。它更适合用来做算法。Python简单易学,对于新手来讲是编程入门的好选择。Python其实也是基于C++创造的,它们的区别主要体现在,C++效率高,编程难;Python效率低,编程简单。比如同样的事情,Python程序员可以很快的写出代码,但机器运行却可能需要成倍于C++的时间;反之,C++程序员编程实现的难度比较大,但在机器上的运行效率很高。因此,很多公司在核心的功能、需要大量运行的部分更倾向于选择C++,而在执行次数不多,但对写代码速度要求比较高的部分则更倾向于使用Python。
2、结构化程序设计
- 结构化程序设计(structured programming)(面向过程)是进行以模块功能和处理过程设计为主的详细设计的基本原则
- 基本思想:
- 自顶向下、逐步细化
- 模块化设计
- 使用三种基本结构(顺序、选择、循环)
- 显著特点:
- 代码和数据分离,程序各个模块除了进行必要的信息交换外彼此独立。
- 淡化了数据的地位,如果需要对数据结构进行修改,那么所有与之对应的操作过程都要进行修改,数据和过程的关系极其复杂混乱。
3、面向对象程序设计
- 面向对象程序设计OOP(object oriented programming)吸收了结构化程序设计的全部优点,以现实世界的实体作为对象,每个对象都有自身的属性和行为特征。多个相同类型的对象的共同特征的抽象描述形成面向对象方法中的类。
- 特征
- 类和对象
- 封装和信息隐蔽
- 抽象
- 继承与重用
- 多态性
- 消息传送与处理
- 优点
- 采用对象描述现实问题,符合人类认识问题、分析问题和解决问题的一般规律。
- 通过信息隐蔽、抽象、继承和重载技术,可以很容易修改、添加或删除现有对象属性,创建符合要求的对象。
- 类包装了对象的实现细节,在使用过程中只需要了解对象向外提供的接口,降低了使用代码的复杂性。
4、C程序开发步骤
二、数据类型与表达式
1、数据类型
2、进制计数
- 十进制“D”(Decimal)
- 二进制“B”(Binary)
- 八进制“O或Q”(Octonary)
- 十六进制“H”(Hexadecimal)
3、信息存储单位
4、常量(Constant)
- 符号常量:用标识符表示的常量。
- 字面常量(Literal Constant):从字面形式即可识别的常量
- 字符串常量
5、存储类别
- auto 变量默认的存储类别,称为自动变量
- static 静态存储类别的变量,称为静态变量
- register 寄存器变量
- extern 外部变量
6、运算符
-
取长度运算符
- sizeof (typename) :取类型typename的长度
- sizeof (expr) :取变量、常量或表达式的长度
- sizeof expr :取变量、常量或表达式的长度
-
逗号右边表达式的值为整个逗号表达式的值
三、程序控制结构
1、格式化输出
四、函数
1、函数声明和函数原型
//C语言默认的函数调用时实参的求值顺序是自右向左。
int i=1,j=2;
printf("%d,%d,%d\n",i+j,j++,i++);
//运行结果:5,2,1
2、标准库函数
标准库名称 | 头文件名 | 标准库名称 | 头文件名 |
---|---|---|---|
断言验证 | <assert.h> | 复数算术运算 | <complex.h> |
字符类型 | <ctype.h> | 出错码 | <errno.h> |
浮点环境 | <fenv.h> | 浮点常量 | <float.h> |
整型格式转换 | <inttypes.h> | 替代记号 | <iso646.h> |
整型大小 | <limits.h> | 本地化 | <locale.h> |
数学 | <math.h> | 时间日期 | <time.h> |
信号量处理 | <signal.h> | 可变参数 | <stdarg.h> |
布尔类型 | <stdbool.h> | 标准定义 | <stddef.h> |
整型类型 | <stdint.h> | 标准输入输出 | <stdio.h> |
实用函数 | <stdlib.h> | 字符串 | <string.h> |
2、内联函数
在编译时将被调函数的代码直接嵌入到主调函数中,取消调用这个环节。
inline 返回类型 函数名(形参列表)
{
函数体
}
//或者在函数原型的类型前加上inline
inline 返回类型 函数名(形参列表);
优缺点:
- 没有函数的调用,不会产生函数来回调用的效率问题。
- 增加目标代码量,增加空间开销
- 注:内联函数不允许使用循环语句和switch语句,递归函数不能做内联函数。当编译器无法对代码进行嵌入时,就会忽略inline声明。
3、全局变量
若全局变量p定义点之前的函数f1想引用p,可以在函 数f1内做外部变量说明,使p的有效范围扩展到f1中。
f1(int a)
{ extern int p;
if(p>a) …; p有效
}
int p, q;
全局变量可采用static标识,只允许本文件中函数引用 ;不标识,允许本源文件中的函数及其它源文件中的 函数引用;但要求在其它源文件作外部变量声明。
五、预处理命令
1、文件包含
#include "文件名"
#include <文件名>
//" "先在当前目录查找指定的文件,未找到再沿系统的设置(INCLUDE路径)查找;
//<>仅在系统的设置中查找;
避免重复包含的方法
- 使用条件编译
#if !defined(_FILE)
define _FILE
#endif
- 使用预处理命令#progma
在文件第一行增加该命令,意思是:在编译一个源文件时,只对该文件包含(打开)一次。
2、条件编译
- #define 定义一个预处理宏
- #undef 取消宏的定义
- #if 编译预处理中的条件命令,相当于C语法中的if语句
- #ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
- #ifndef 与#ifdef相反,判断某个宏是否未被定义
- #elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
- #else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
- #endif #if, #ifdef, #ifndef这些条件命令的结束标志.
- defined 与#if, #elif配合使用,判断某个宏是否被定义
#ifndef ABCD_H
#define ABCD_H
// ... some declaration codes
#endif // #ifndef ABCD_H
六、数组
1、多维数组作为函数的参数
多维数组作为函数的参数,形参数组第1维可以与实 参相同,也可以不相同;可以是任意长度,也可以不 写长度;但其他维的长度需要相同。
int a[5][10]
f(a);
void f(int A[5][10]); //正确
void f(int A[2][10]); //正确
void f(int A[][10]); //正确
void f(int A[][]); //错误,第2维长度必须给出
void f(int A[5][5]); //错误,第2维长度必须相同
void f(int A[50]); //错误,必须是二维数组
2、字符数组
C语言没有提供字符串类型,对于字符串的处理是用字符数组来实现的。
- 字符数组的定义 如:char s[20];
- 字符数组的初始化 如:char s[4]={‘J’ , ‘a’ , ‘v’ , ‘a’};
- 给出初值列表的情况下,也可以省略长度值。 如:char s[]={‘H’ , ‘e’ , ‘l’ , ‘l’ , ‘o’ , ‘! '};
- 字符数组的引用只能以字符元素为单位引用,不能一次引用整个字符数组, 如:不能对字符数组整体进行赋值、算术运算等。
- 如:char s1[5]={‘B’ , ‘a’ , ‘s’ , ‘i’ , ‘c’} , s2[5];
- s2 = s1; //错误
- s2[0] = s1[0]; //正确,数组元素赋值
字符串:
-
定义:C语言规定字符串是以’\0’(ASCII值为0)字符作为 结束符的字符数组,其中’\0’字符称为空字符( NULL字符)或零字符(Z字符)。
-
初始化:
char s[12]={"Hello World"}; //数组长度为字符串长度加1 char s[12]="Hello World"; //字符串初始化 char s[]="Hello World"; //字符串初始化 相当于: char s[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘ ’,'W','o','r','l','d','\0'}; 但char s[4]={‘J’,‘a’,‘v’,‘a’};不能算字符串
七、指针
1、行位移量与元素位移量
- 二维数组
- 若:
int a[3][4];
- a+i的含义: i行的起始地址,称为i行的指针;
- 表达式a+i的值:
a+i*列长*元素类型长
; 其中:i为行位移量;
- 若:
- 与一维数组比较:
- 若:
int a[10];
- 则a+i的含义:元素a[i]的指针;
- 表达式a+i的值:a+i*元素类型长; 其中:i为元素位移量。
- 若:
2、函数指针
-
用指向函数的指针变量调用函数
-
指向函数的指针变量的定义
- 返回类型 (*函数指针变量名)(形参列表 );
-
指向函数的指针变量的赋值
- 函数指针变量=函数指针
- 其中:函数指针用函数名表示,可以是库函数名或 用户自定义函数名。
-
利用指向函数的指针变量调用函数 (*指针变量名)(实参表); 指针变量名(实参表);
#include <stdio.h> int max(int a, int b) //求最大值 { return a>b ? a:b ; } int min(int a, int b) //求最小值 { return a<b ? a:b ; } int main() { int (*p)(int a,int b);//定义函数指针变量 p=max; //p指向max函数 printf("%d ",p(3,4)); //通过p调用函数 p=min; //p指向min函数 printf("%d ",p(3,4)); //通过p调用函数 return 0; }
-
八、自定义数据类型
1、结构体字节对齐
- 为了加快数据存取的速度,编译器默认会使得对结 构体成员和结构体本身的内存起始地址是一定字节数的 倍数,而不是连续存放,称为字节对齐。
- 设对齐字节数为n(n=1,2,4,8,16),每个成员内存长度为Li,最大的成员内存长度为L。
- 字节对齐规则是:
- 结构体对象的起始地址能够被L所整除;
- 每个成员相对于起始地址的偏移量,即对齐值应是 min(n,Li)的倍数。若不满足,则用空字节补足;
- 结构体的总长度值应是min(n, L)的倍数,若不满足, 则在最后用空字节补足。
九、链表
1、概念
- 链表是连成一行的数据项集合,每一个数据项 (元素)称为结点,可以在链表中的任意位置进 行结点插入或删除操作,使链表数据项的个数 随之增加或减少。
- 链表主要用于两个目的:
- 建立不定长度的数组。
- 链表可以在不重新安排整个存储结构的情况下,方便且迅速地插入和删除数据元素。
十、文件
[1] :https://www.zhihu.com/question/21976478/answer/158145376
上一篇: PHP实现session共享