Info
C 语言是一种通用的、面向过程式的计算机程序设计语言。1972 年,为了移植与开发 UNIX 操作系统,丹尼斯·里奇在贝尔电话实验室设计开发了 C 语言。
C 语言是一种广泛使用的计算机语言,它与 Java 编程语言一样普及,二者在现代软件程序员之间都得到广泛使用。
当前最新的 C 语言标准为 C18 ,在它之前的 C 语言标准有 C17、C11...C99 等。
C语言复习笔记¶
1 绪论¶
Hello, world!¶
C 程序的组成¶
2 基本数据类型¶
常量¶
int
long
long long
float
double
char
void
(空类型)
变量名¶
以字母或下划线开头, 后面可以跟若干个字母、数字和下划线, 区分大小写
整型¶
- 十六进制:
0x
开头 - 八进制:
0
开头 - 长整型: 后面加
l
- 超长整型: 后面加
ll
转义字符¶
\n
\t
\'
\"
\\
\0
\ddd
1~3 位八进制数所代表的 ASCII 码字符\xhh
1~2 位十六进制数所代表的 ASCII 码字符
计算机存储单位¶
GB
TB
PB
3 数据的输入和输出¶
数据输入输出¶
输入输出函数中, 键盘是标准输入设备 stdin
, 显示器是标准输出设备 stdout
头文件 #include<stdio.h>
基本的格式输出语句¶
printf("格式控制", 输出列表);
返回值为本次调用输出字符的个数, 包括回车等控制符
格式说明符¶
- 十进制:
%d
%ld
- 八进制:
%o
%lo
-
十六进制:
%x
%lx
-
小数:
%f
(普通)%e
(指数)%g
(自动选择) - 字符:
%c
- 字符串:
%s
- 变量的内存地址:
%p
- %:
%%
在 %
和说明符之间加入整数以限制宽度, 加入小数以限制精度, 加入 -
左对齐, 加入 0
补零而不是空格, 加入 +
显示正号
变场宽输出 printf("%*.*f", m, n, f)
强制转换¶
(类型名)(表达式)
在数据前加 (int/float 等数据类型)
格式输入函数¶
scanf("格式控制", 内存地址表);
返回值为本次 scanf()
调用正确输入的数据项的个数
格式控制¶
整型、字符、字符串与输出控制相同
- float:
%f
- double:
%lf
跳过读入:scanf("%d%*d%d%d", &x, &y, &z);
取地址符¶
&
字符输出函数¶
putchar(c);
字符输入函数¶
getchar();
头文件 #include <stdio.h>
_getch();
头文件#include <conio.h>
在按下相应键的同时接收从键盘输入的 一个字符。_getche();
由键盘输入的字符不在屏幕上显示_ungetch(c);
将字符放回输入流
4 表达式与宏定义¶
复合算术赋值运算符¶
+=
-=
*=
/=
%=
整型相除¶
7/6==1
(1/2+1/2)==0
关系运算符¶
<
<=
>
>=
==
!=
逻辑运算符¶
&&
||
!
1<x<2
\(\iff\) 1<x && x<2
书写时需要注意运算符优先级, 不确定带括号; 最好直接带括号
若已经能够判断则逻辑运算符后面的语句不执行
其他运算符¶
自增自减运算符 ++i
i++
--i
i--
sizeof()
逗号运算符: 从左到右计算表达式的值, 最右边表达式的值就是整个逗号表达式的值
常用头文件¶
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
宏定义¶
#define 符号常量名 字符串
不过这样的宏定义只是做简单的字符串替换
一行写不下可以在杭为加 \
在下一行继续写
带参数的宏定义
#define 宏名 (参数表) 字符串
如 #define P(x) printf ("%d\n", x)
一般需要将参数用括号括起来
在宏定义中使用 #
1. 将变量转换为字符串 如 #define PR(x) printf("%s=%d\n", #x, x)
2. 连接两个字符串 如 #define MP(x) printf("%d",a##x)
5 选择结构¶
if¶
else
只与同层最近的 if
配对
注意小数确定是否相等时用差值小于一个精度而不是直接用 ==
条件运算符¶
表达式 1 ? 表达式 2 : 表达式 3
switch¶
6 编译预处理¶
文件包含¶
#include < 文件名 >
#include "文件名"
一般不用 #include
命令引用 .c
文件 (C++
模版文件可能要这么做)
条件编译¶
文件 item.h
#undef
将已经定义的标志服变为未定义
pragma¶
一般形式 #pragma token-string
, 这指导编译器如何进行编译
如 #pragma once
, 让编译器把指定的文件只包含一次, 防止此文件被多次引用出现的重复定义等错误.
如 #pragma warning(disable:4996)
将 4996 类警报置为失效,让编译器不再显示这类警告
line¶
#line 数字 num ["文件名"]
从该行之后, 编译信息的行数将从第 num
行开始计算, 而不是原先代码的行数
7 循环结构¶
while¶
while(){...}
do {...} while ();
for¶
for(i=0; i<=n; i++) {...}
- i=0
和 i++
可以省略
- i<=n
也可以省略, 但是需要 break;
否则无法跳出循环
continue¶
结束本次循环的执行,但不退出循环结构
8 模块设计¶
C 语言中, 函数分以下两种: 标准库函数, 用户自己定义的函数
函数的一般形式¶
void
类型的函数不返回函数值, 只是完成某个任务
函数中的返回语句 return 表达式;
C 程序其中必须有且只能有一个主函数 int main()
C 程序总是从主函数开始执行 (不管它在程序中的什么位置), 而其他函数只能被调用
函数的说明¶
类型标志符 函数名 (形参列表);
这里形参列表可以是 形参 1 类型, 形参 2 类型, ...
, 也可以是 形参 1 类型 形参名 1, 形参 2 类型 形参名 2, ...
.
这种对被调用函数的说明称为函数原型 / 函数向前引用说明
函数参数传递¶
C 语言函数传参为值传递
全局变量¶
可以通过定义全局变量的方法实现各函数之间的参数传递, 全局变量的有效范围是从定义变量的位置开始到 本源文件结束
全局变量的引用说明 entern 类型名 变量名;
extern
全局变量的用途
1. 在同一文件中, 为了使全局变量定义点之前的函数中也能使用该全局变量, 则应在函数中用 extern
加以说明.
2. 使一个文件中的函数能使用另一个文件中的全局变量.
3. 利用静态外部变量, 使全局变量只能被本文件中的函数引用, 控制其作用域
变量的存储类型¶
- 数据类型: 如
int
float
char
double
- 存储类型: 分为自动类型
auto
(函数中局部变量默认为auto
类型), 静态类型static
, 寄存器类型register
, 外部类型extern
.
数据的存储类型决定了数据的存储区域
静态变量¶
用 static
说明的局部变量或外部变量在函数调用结束后其内存不会消失而保留原值, 即其占用的存储单元不释放, 在下一次调用时仍为上次调用结束时的值.
对局部静态变量赋初值是在编译时进行的
内部函数与外部函数¶
在函数定义前加 static
使函数成为只能在本文件的其他函数中调用的函数, 这种函数被称为内部函数
在函数定义前加 extern
使函数成为外部函数 (也即一般定义的函数)
递归函数¶
9 数组¶
一维数组¶
定义 类型名 数组名 [常量表达式];
注意 [ ]
中必须是常量
在 C 语言中, 只能逐个引用数组元素, 不能一次引用数组中的全部元素
二维数组¶
定义 类型名 数组名 [常量表达式 1][常量表达式 2];
字符串¶
字符串常量以结束符 \0
结尾
以下写法等价
常用的字符串处理函数¶
#include <string.h>
puts(s)
输出字符串gets(s)
读入字符串strcat(s1, s2)
将 s2 连接到 s1 后面, 并返回 s1 地址strcpy(s1, s2)
将 s2 复制到 s1 中strncpy(s1, s2)
将 s2 的前 n 个字符复制到 s1 中strcmp(s1, s2)
前大返回大于 0 的数strlen(s)
求字符串长度 (不包括\0
)strstr(s1, s2)
确认 s2 是否在 s1 中出现过, 是则返回第一次出现的位置, 否则返回NULL
strlwr(s)
strupr(s)
sprintf(字符数组名, "输出格式", 变量列表)
sscanf(字符数组名, "输入格式", 变量列表)
数组作为函数参数¶
int function(int a[], int n){...}
一般需要另外一个参数传递数组长度
二维传递可以强制转为一维数组
二维数组下标 i*n+j
10 指针¶
int *p = &x;
定义了整型指针 p
, 指向整型变量 x
的内存地址
*p = 3;
现在 *p
指的是指针 p
所指的值, 此语句使得变量 x
的值被更改为 3
*r++
和(*r)++
:*r++
: 指针加, 指向下一个单元(*r)++
: 指针所指的内容加
指针作为函数参数可以实现地址传递
10 动态内存申请与释放¶
malloc¶
malloc
函数的原型 void *malloc(申请内存的字节数)
使用, 如
p=(char *)malloc(sizeof(char)*20);
q=(double **)malloc(sizeof(double *)*10);
a=(int (*)[4])malloc(sizeof(int)*4*5);
一般用 malloc()
申请内存需要判断是否申请成功
使用 malloc()
可以先读入数组长度然后定义一个数组
free¶
函数原型 void free(void *ptr);
用 malloc()
申请的动态内存块, 需要用 free()
按照与申请顺序时向量的顺序释放回内存堆, 防止内存泄漏
字符串与指针¶
char s[] = "...";
char *s = "...";
二者的联系与区别¶
- 字符数组由元素组成; 字符指针变量中存放的是地址
- 字符数组中的元素可以单个字符赋值; 字符指针变量赋值是字符串首地址
- (字符) 数组名是常量, 值无法改变; 字符指针变量的值可以改变
- 可以用加号或下标形式索引字符串的单个元素. 如
char *s = "..."; printf("%c", s[4]); printf("%c", *(s+4))
; 如"..."+4
"..."[4]
- 指针指向的字符串值无法修改
- 可以通过输入字符串的方式为字符数组输入字符元素; 但不能通过输入函数让字符指针变量指向 一个字符串, 因为由键盘输入的字符串, 系统不分配存储空间
- 可以用指针变量所指向的字符串表示程序中的任何字符串, 用字符数组也可以
- 字符型指针数组可构造紧凑型字符串数组, 例如:
char *str[]={"abc", "de", "fghij", "k"};
也可以用字符型二维数组来存放字符串数组, 例如:char str[][6]={"abc", "de", "fghij", "k"};
- 以用
sizeof
操作符求字符串所占内存的大小
函数指针¶
指向函数的指针指向函数的入口地址
函数指针的定义:类型标识符 (* 指针变量名)(参数类型);
给函数指针变量赋值时, 只需给出函数名
调用方式:(* 函数指针变量名)(实参表)
函数指针变量名 (实参表)
返回指针值的函数¶
类型标识符 * 函数名 (形参表){...}
main 函数的形参¶
其中: argc
的值是命令行参数个数 +1
, argv
是字符型指针数组, 指向每一个参数字符串
字符串转 int 和 float
#include <stdlib.h>
atoi()
atof()
11 结构体与联合体¶
struct 结构体¶
定义: struct 结构体类型名 {成员表};
注: 分号别掉了
用大括号和逗号进行赋值
用 .
取结构体中变量的值
在定义结构体类型变量时, 需要使用结构体类型的全称. 如 struct date birthday;
也可以同时定义结构体类型和变量:
- 也可以定义无名结构体
- 结构体可以嵌套
- 结构体变量可以作为函数参数
- 结构体变量也可以定义数组
当结构体类型的指针变量 p
指向一个同类型的结构体类型变量 a
后, 下列四种表示是等价的:
a. 成员
(*p). 成员
p-> 成员
p[0]. 成员
pack¶
在程序开头加上 #pragma pack(4)
则其中结构体采用 4 字节对齐
也可以通过调整结构体内部变量顺序的方式来节省空间
综合应用 链表¶
联合体¶
一般形式
联合体 union
和 struct
的定义和使用是相同的. 但 union
中的各个数据之间共享同一个单元的, 所占内存单元的大小是几个数据项中长度最大的一个
枚举类型¶
enum 枚举类型名 {枚举元素列表};
如 enum WEEK {SUN, MON, TUE, WED, THU, FRI, SAT};
, 第一个值从 0 开始自动赋值. 也可以在常量名后面加等号自己赋值
自定义类型名¶
typedef 原类型名 新类型名;
typedef
也可以用来声明数组类型, 如 typedef int NUM[100];
12 文件¶
文本文件和二进制文件¶
用一般的编辑器能编辑、人能直接读懂的文件是文本文件, 是由 ASCII 字节流组成的 (也可能是其他编码)
如 .txt
.c
.h
. 除了这些文件基本上都是二进制文件
定义文件指针¶
FILE * 指针变量名;
打开文件¶
fp = fopen("文件", "文件打开方式");
打开方式
r 只读
为读打开一个文件. 若指定的文件不存在, 则返回空指针值 NULL.w 只写
为写打开一个新文件. 若指定的文件已存在, 则其中原有内容被删去; 否则创建一个新文件.a 追加写
向文件尾增加数据. 若指定的文件不存在, 则创建一个新文件.r+ 读写
为读写打开一个文件若指定的文件不存在, 则返回空指针值 NULL.w+ 读写
为读写打开一个新文件. 若指定的文件已存在, 则其中原有内容被删去; 否则创建一个新文件.a+ 读与追加写
为读写向文件尾增加数据打开一个文件, 若指定的文件不存在, 则创建一个新文件.
在后面附加 b
表示打开二进制文件
打开文件时的判断
关闭文件¶
fclose(fp);
文本文件读写¶
fgetc(fp);
读字符fputc(c, fp);
写字符fgets(char *s, int n, FILE *fp);
从fp
中读n
个字符作为一个字符串放到s
中fputs(const char *s, FILE *fp);
写字符串
数据块读写¶
feof(fp)
遇到文件尾返回非 0, 否则返回 0
fread(buffer, size, count, fp);
从fp
中读count
个字节数为size
的数据放到buffer
中fwrite(buffer, size, count, fp);
从buffer
中写count
个字节数为size
的文件到fp
中
格式读写¶
fscanf(文件指针, 格式控制, 地址表);
fprintf(文件指针, 格式控制, 输出表);
可以替换文件指针的量: 标准输入的设备名 stdin
, 标准输出的设备名 stdout
文件定位¶
rewind(fp);
(倒带) 将读写指针移动到文件开头
fseek(FILE *fp, long offset, int origin);
offset
为 ** 偏移量 **-
origin
为 ** 起始位置 **: -
SEEK_SET
或0
SEEK_CUR
或1
SEEK_END
或2
long ftell(FILE *fp);
返回文件当前读写指针的位置
其他函数¶
fflush(fp);
清空文件的输入输出缓冲区clearerr(fp);
清除由于读写等操作失败引起文件输入输出缓冲区处于的错误状态clearerr(stdin/fp);
应该和fflush(stdin/fp);
配对使用
13 位运算¶
&
按位与|
按位或^
按位异或~
按位取反<<
左移>>
右移
运算优先级¶
!(逻辑非)
→ 按位取反 ~
→算术运算符
→ 左移运算符 <<
右移运算符 >>
→ 关系运算符
→ 按位与 &
按位异或 ^
按位或 |
→ 逻辑与 &&
→逻辑或 ||
→ 赋值运算符
位段¶
struct 位段结构类型名 {成员表};
定义了一个位段结构类型,名为packed_d
,共包含2个成员(又称为位段),每个成员均为无符号short
类型,其中成员f1占2个二进制位,f2占1个二进制位
注意:
- 位段成员的类型必须是
unsigned型
; - 在位段结构类型中,可以定义无名位段,这种无名位段具有位段之间的分隔(或占位)作用.如果无名位段的宽度值为0,则表示下一个位段从一个新的字节开始存放
- 每个位段(成员)所占的二进制位数一般不能超过 编译器的一个字长(比如32位)
- 位段不能说明为数组,也不能用指针指向位段成员
- 不能用
sizeof()
求段位成员的大小 - 在位段结构类型定义中, 可以包含非位段成员