=========== C语言源文件的组成 ===================
预处理指令:
凡是以‘#’开头的行,都属于预处理指令。
include define ifndef endif 等
例如:
#include <stdio.h>
#define N 10
定义语句:
结构体类型定义、别名定义、全局变量定义等。
例如:
struct student {
int id;
int score;
};
typedef int int32;
int value = 10;
声明语句:
函数原型声明、全局变量声明等。
例如:
extern int fun(void);
extern int value;
函数:
指令的集合,C语言执行语句的基本单元,任何表达式都要被包含在函数内部。
========== C语言语法组成 =====================
关键字:
C语言内置保留的符号,用作语法控制,组成了C语言的语法框架。
存储类:auto extern register static
数据类型:int char short long float double signed unsigned void
控制语句:if else for while do break continue switch case default goto return
其他: sizeof volatile const union struct typedef enum
标识符:
用户自定义的符号,用来标记变量、函数、类型等。
标识符的定义规则:
1. 只能由英文大小写、下划线、数字组成。
2. 不能与关键字重名。
3. 不能以数字开头。
4. 长度限制(非标准)。
运算符:
C语言内置的符号,用作计算。在使用时要非常注意优先级和结合性。
算术: + - * / %
关系: > >= < <= == !=
逻辑: && || !
位: & | ~ ^ << >>
赋值: = += -= *= /= %= >>= <<= &= ^= |=
其他: () [] -> . & * (类型) sizeof ++ – + - ?: ,
间隔符:用作词法分析。
空格、换行、制表、注释
标点:
‘’ “” ; , () {}
================ C语言的数据 ===================
变量:用来暂存数据, 其数据值可以变化更改;
整数型:
char
空间大小: 1Byte /* 在绝大多数的编译环境下是1B */
取值范围: 0 ~ 255 /* 无符号 */
-128 ~ + 127 /* 有符号 */
short
空间大小: >= 2B, <= int /* 32bit环境下,一般是2B */
取值范围: 0 ~ 65535 /* 无符号 */
-32768 ~ +32767 /* 有符号 */
int
空间大小: 一般等于CPU的字长。 /* 32bit环境下, 一般为4B */
取值范围: 0 ~ 4G /* 无符号 */
-2G ~ +2G /* 有符号 */
long
空间大小: >= int /* 32bit环境下, 一般为4B, 同int */
取值范围: 0 ~ 4G /* 无符号 */
-2G ~ +2G /* 有符号 */
long long /* GCC扩展类型,非标准 */
空间大小: 双倍的long /* 32bit环境下,一般为8B */
取值范围: 0 ~ 10^20 /* 无符号 */
-10~19 ~ +10^19 /* 有符号 */
浮点型:
float /* 单精度浮点型 */
空间大小: 4B /* IEEE 标准下 */
取值范围: +-10^+-38
精度: 6~7位
double /* 双精度浮点型 */
空间大小: 8B /* IEEE 标准下 */
取值范围: +-10^+-308
精度: 15~16位 /* 至多保证16位精度,完全保证15位精度 */
指针型:
任何基本类型或构造类型都可以定义对应的指针类型。指针的大小固定,跟平台相关(32bit下4字节)。
int *p;
char *p1;
struct student *pstu;
常量:一般用作直接参与运算, 其数值在运行过程中不可变。
整数型:
十进制: 1, 2, 30, 1020333 /* 开头非0, 不加前缀,基数为0123456789*/
八进制: 010, 017, 077 /* 前缀为0, 基数为01234567 /
十六进制: 0x10, 0x1f, 0xfe / 前缀为0x,基数为0123456789abcdef /
二进制: 0b010 / 前缀为0b,基数为01 */
实数型:
浮点:3.14, 1234.5678
指数:3.14e1, 1.23456e5, -1.45e-10 /* 科学计数法,e左边是系数(精度),e右边是指数(10为底) */
字符型:
字符:‘a’, ‘b’, ‘0’, ‘1’
ASCII码: 97, 98, 48, 49 /* C语言中字符用ASCII码表示, ASCII码是范围为0~127的整数。 */
字符串: “helloworld”, “i love china”, “blue shit”
--------------计算机数据的存储----------------
1Byte = 8bit
B: 1字节
KB 千字 1024B 10bit
MB: 兆字 1024KB 20bit
GB: 吉字 1024MB 30bit
原码:数据的直接二进制表示,其中有符号数的最高位代表符号位,负数置1,正数置0。
反码:符号位不变的情况下其余位按位取反。
补码:反码的基础上加1。
计算机中正数统一按照原码存放,负数统一按照补码存放。
例如:
10000110 -6的原码
11111001 -6的反码
11111010 -6的补码
================= C语言语句及表达式 ==============
定义函数:函数是程序的基本单元,任何的表达式都要包含在函数内部。
返回值 函数名(参数1, 参数2, …) /* 参数个数根据实际需求决定,如果不需要则写void或不写 */
{
定义语句1;
定义语句2;
…
表达式1;
表达式2;
...
return 返回值表达式;
}
例如:
int main(void)
{
int i = 10;
int j = 20;
i += j;
j = 100;
return 0;
}
定义变量:变量用来存储数据,凡是定义在函数内部的都属于局部变量;凡是定义在函数外面的都属于全局变量。
存储类型 数据类型 标识符; /* 定义一个变量 /
存储类型 数据类型 标识符 = 值; / 定义并初始化变量 /
数据类型 标识符; / 存储类型可以省略,让编译器自动判别 /
例如:
auto int i; / 定义一个自动型整型变量,注意auto不能修饰全局变量 /
static int j; / 定义一个静态型整型变量 /
int value; / 定义一个整型变量, 常规写法;编译器会自动根据语境判别其存储类型 */
=============== C语言的运算符 ===================
除了赋值运算和自增自减运算之外,任何表达式都不会影响原操作数的值。
单目运算: 操作数只有一个。
双目运算: 操作数有两个。
三目运算: 操作数有三个。
在同一个表达式中出现两个以上的运算符则称为复合运算。
在复合表达式中要分清优先级,优先级高的要先运算。
例如: a = b + c * d;
遇到同级优先级要看结合性。
例如: a + b - c;
a = b = c;
单目运算(后缀++和后缀–除外)、三目运算、赋值运算都是右结合性,要优先计算右操作数。
其余的都是左结合性,要优先计算左操作数。
类型转换:C语言的类型转换分为两种, 隐式转换和显式转换。
其中基本类型(int/short/char/float)可以自动隐式转换;复杂类型(*/struct)需要强制转换。
有符号数和无符号数间:编译器先将所有的有符号数隐式转换为无符号数,结果为无符号数。
例如: int a = -20; unsigned int b = 10;
if (a < b)
puts("YES");
else
puts("NO");
以上结果打印"NO".
不同宽度的整数间:编译器会先将所有的类型隐式转换为两者间宽度更大的类型。例如:
char short short
int char int
short int int
浮点数和整数间: 编译器会先将所有的类型转换为浮点数并运算,表达式的结果为浮点数。例如:
int float float
char float float
float short float
int double double
单精度浮点和双精度浮点间:编译器会先将所有的类型转换为双精度浮点数并运算,表达式的结果为双精度浮点数。例如:
float double double
double float double
总结一下简单规律:宽度不同结果向宽扩展,遇到浮点结果为浮点。
布尔型和其他类型的转换:
任何类型都可隐式转换为布尔类型,此时非零为真,零为假。
布尔型可以隐式转换为整数型:真为1, 假为0。
算术运算:用来进行常规计算,包含加、减、乘、除、取模(取余)、自增、自减。
+、-、、/、%、++、–
注意:
前缀++或前缀–的运算方法是先运算后取值。
例如: int i = 10; ++i表达式的结果是11。
后缀++或后缀–的运算方法是先取值后运算。
例如: int i = 10; i++表达式的结果是10;但是运算之后变量i的值变为11。
逻辑运算:逻辑运算用来测试一个表达式结果的真假。包含&& || !
逻辑运算的结果是布尔值(逻辑值),只有真假两种结果。
真值表:
与:&&
真 假 假
真 真 真
假 真 假
假 假 假
或:||
真 真 真
真 假 真
假 真 真
假 假 假
非:!
真 假
假 真
短路特性:在与或运算中,如果左边表达式的结果已经能够决定整个与或运算的结果时,则右边表达式不再执行。
例如: int a = 10; int b = 20;
if (a > 0 || a += 10) / 因为a > 0为真,能够决定整个表达式的结果, 所以a+=10不执行 /
if (b <= a && b++) / 因为b <= a为假,能决定整个表达式的结果, 所以b++不执行 /
if (a > 5 && a++) / 因为a > 5为真,不能决定整个表达式的结果,所以a++执行 */
关系运算:用来进行数值比较,包含> >= < <= == !=。
注意:
关系运算的结果值是逻辑值(布尔值),只有真假两种结果。
等于运算是==。
>= <= == != 这些运算符都是两个符号,中间不能有任何间隔。
位运算:用来进行位操作,可以通过运算操作数据中某一位。包含& | ~ ^ << >>。
位与:参照逻辑运算的真值表。
6 110 &
5 101
4 100
位或:参照逻辑运算的真值表。
18 10010 |
15 01111
31 11111
异或:^ 相同为0, 不同为1。
1010101 ^
0100111
1110010
左移:符号位不变,整体左移,高位移出,低位补零。
1010110 << 2
1011000
赋值运算:用来更改一个变量的值。表达式的结果取最左值。
包含: += -= *= /= %= <<= >>= |= &= ^=
三目运算:可用来对分支语句进行简单替代。
if (a > b)
max = a;
else
max = b;
可替换为:
max = a > b ? a : b;
逗号表达式:使用‘,’符号间隔子语句。表达式的结果取最右值。
例如: ret = (a = 10, b = 20, c = 30); ret的值是30。
注意上述表达式的优先级,因为逗号比赋值的优先级低,所以要用圆括号包含起来。
其他符号: [] -> . & *
============ C语言输入输出函数 ================
读字符: getchar
写字符: putchar
读字符串: gets /注意gets函数的危险性/
写字符串: puts
格式化读: scanf
格式化写: printf
整数: %d %u %x %o
浮点: %f %g %e
字符: %c
字符串: %s
地址: %p
=================== C语言的控制语句 ===============
C语言中程序有三种结构:
顺序
以分号结尾的语句自上往下顺序执行。
分支
根据不同的情况做不同的处理。
if else
switch case break default
循环
重复性地执行某个语句或代码段。
for
while
do while
if goto
------------------------- 单分支 ---------------------------------
if (逻辑表达式) {
语句1;
语句2;
...
}
如果if控制的只有一条语句,可以不加{}。
if (逻辑表达式)
语句;
------------------------- 二分支 ----------------------------------
if (逻辑表达式) {
语句1;
语句2;
...
} else {
语句1;
语句2;
...
}
--------------------- 多路分支(阶梯形式) ------------------------------
if (逻辑表达式) {
语句1;
语句2;
...
} else if (逻辑表达式) {
语句1;
语句2;
...
} else {
语句1;
语句2;
...
}
----------------------- 多路分支(嵌套形式)------------------------
if (逻辑表达式) {
语句1;
语句2;
...
} else {
if (逻辑表达式) {
语句1;
语句2;
...
} else {
if (逻辑表达式) {
语句1;
语句2;
...
} else {
语句1;
语句2;
...
}
}
}
----------------- switch多路开关语句 ---------------------
switch(表达式) {
case 标号1:
语句1;
语句2;
...
break;
case 标号2:
语句1;
语句2;
...
break;
case 标号3:
语句1;
语句2;
...
break;
...
case 标号N:
语句1;
语句2;
...
break;
default:
语句1;
语句2;
...
break;
}
注意:
关键字case后面应该是一个常量标号。
也可以是一个常量表达式,但是不能出现任何变量及其表达式。
---------------------- while 循环 ----------------------------
while (逻辑表达式) {
语句1;
语句2;
...
}
说明: 当逻辑表达式结果为真,则循环执行被控制的代码段,直到结果为假。
------------------------- for 循环 ---------------------------
for (初始化表达式; 条件表达式; 增量表达式) {
语句1;
语句2;
...
}
说明: 初始化表达式,只在循环开始前执行一次,之后不再执行。
条件表达式, 在每次循环前执行一次,如果满足条件则进入被控制代码段,否则退出(同while)。
增量表达式, 在每次循环之后(被控制代码段之后)执行一次。
补充:for 后面的三个表达式都可以省略,但是’;'不能省略。
=============== 模块化编程 =================
按照功能划分,将程序分为几个功能模块,分别处理。
简单程序可参照以下方式划分:
输入
校验
处理
输出
========================= 数组 ==================
数组是相同类型的有限集合。
存储类型 数据类型 标识符[数量];
例如:
static int array[10];
数组的初始化: 数组在定义的同时可以整体赋值,此时称为初始化。
例如:
int a[5] = {1,2,3,4,5};
如果未完全赋满值,则未赋值的元素将清零。
例如:
int a[5] = {1,2,3,4};
此时,a[0]、a[1]、a[2]、a[3]的值分别为1、2、3、4, a[4]的值为0。
数组在定义之后不可以整体赋值或取值,需要依次用下标表示。
例如:
a[0] = 10;
a[3] = 30;
拥有N个元素的数组下标范围是0~N-1。
例如:
int a[5];
其有效的元素为a[0]、a[1]、a[2]、a[3]、a[4]。
a[5]不是有效元素,禁止使用。
===================== 指针 =====================
指针就是地址。
只有内存变量才可以取地址,常量和寄存器变量都不可以。
例如:
&4 /* 整数常量,不可以 /
register int i;
&i; / 寄存器变量,不可以 */
定义指针:
存储类型 数据类型 * 标识符;
例如:
static int *p;
int *p;
任何指针类型的大小都是固定的,并且与平台相关(32bit下是4字节)。
例如:
sizeof(int *) == 4;
sizeof(char *) == 4;
sizeof(struct student *) == 4;
-------------------- 指针的运算 ---------------------
算术运算:
指针和整数: + - /* 指针加整数往高地址偏移,指针减整数往低地址偏移。 /
相同类型的指针和指针: - / 指针减指针,结果是两者之间相差的元素个数,而不是字节数 /
单目指针: ++ – / 等同于 p = p + 1 /
关系运算:
相同类型指针和指针: > >= < <= == != / 指针的关系运算目的是比较两个同类型指针地址的高低 /
逻辑运算:
全部可以。 / 逻辑运算跟类型无关,任何数据都可以进行逻辑运算:非零为真,零为假 /
位运算:
不可以。 / 位运算对于指针而言没有意义,因此不可以运算。 /
赋值运算:
相同类型指针和指针:=
指针和整数:+= -=
其他:
* / 直接访存 /
[] / 偏移访存 /
-> / 结构指针访问成员 /
& / 取内存变量的地址 */
中括号表达式的含义:
a[b] == *(a+b) == *(b+a) == b[a];
========函数##############################
声明方法:
返回值 函数名(参数1, 参数2 …);
例如:
int add(int opd1, int opd2);
定义方法:
返回值 函数名(参数1, 参数2,...)
{
语句1;
语句2;
....
返回值表达式;
}
例如:
int add(int opd1, int opd2)
{
return opd1 + opd2;
}
调用方法:
函数名(参数1, 参数2 …);
例如:
add(100, 20);
--------------------- 名词解释 --------------------
主调函数:调用者。
被调函数:被调用者。
例如,main函数内调用了put函数,那么main属于主调函数,puts属于被调函数。
回调函数:
可重入函数:
形参:形式参数,函数声明或者定义时用来接收实参的参数的名字。
实参:实际参数,函数调用时传递给被调函数的参数名字。
--------------------- 函数传参 ----------------------
1.值传参:直接将参数的数值传递给被调函数。被调函数获得的是实际参数值的副本,因此被调函数无法更改主调函数实参的值。
2.地址传参:将实参的地址传递给被调函数。被调函数获得的是实参的地址,因此被调函数可以通过该地址访问或修改实参的值。
3.全局变量传参: 函数本身不需要传参,通过共享全局变量。
常用C库函数
输入输出类:
getchar putchar gets puts printf scanf
字符串操作类:
strlen strcpy strcat strncpy strncat atoi atof strtok sprintf sscanf
内存操作类:
malloc free memset bzero memcpy memmove
数据计算类:
sqrt sin cos rand