目录
一、C语言的优缺点
二、格式化输入输出
三、语句和表达式
四、数据类型
五、引用和指针
六、时间日期
七、字符串
八、输入输出流
九、底层程序设计
十一、标准库及其他库函数
十二、附录
正文
一、C语言的优缺点
C语言是面向过程的程序设计语言,一门“死而不僵”的语言,优点是稳定、可靠、通用性极强,但是对大型程序来说面向过程的语言使用起来会比较麻烦。
二、格式化输入输出
1.从命令行获取输入内容
使用函数scanf("XX0XX1...",&YY0,&YY1,...)。其中&表示获得数据的地址,获取数据后放到这个地址中,XX是一般的数据类型比如%d、%o等,YY是要赋值的对象。使用转换说明%nXX也可以限制接收的个数,比如%7s、只接收7个字符装入字符串中。
2.输出内容到命令行
使用函数printf("%m.nXX0",XX1)。m表示占位个数,少于实际长度将显示全部内容、多出实际长度将用空格填充(正数空格左填充,负数空格右填充),n表示取值个数(数值类型取小数点后边几位,字符串表示先取几位、后填充),之后按照这个格式输出;XX0是常见数据类型,比如%o八进制、%x十六进制、%e指数形式、%g自动选择、%u无符号整形、%s字符串(此时的n可以控制字符串输出的位数),另外可以外加\n换行等转义字符。
3.C99输出的奇怪数
-1.#INF00负无穷、1.#INF00正无穷、NaN非数
三、语句和表达式
1.顺序、条件、循环语句
(1)switch_case
一般是顺序语句,程序从上到下、从左到右顺序执行;条件语句可以跳转,用if、else进行条件的判断,不过要注意的是这种特殊情况switch_case。表达式具有某个常量返回值,当符合case的常量时执行后边的语句块;如果需要跳出switch,在相应的case语句块后面加上break即可,否则程序会在符合的地方一直执行下去。
switch(表达式)
{
case 常量: 语句块
case 常量: 语句块
default: 语句块
}
(2) 循环
先判断条件,符合后执行循环体;可以不执行循环体
while(表达式)语句;
先执行循环体,在判断条件,如果满足继续执行循环体;至少执行一次循环体
do 语句 while(表达式);
如果知道循环的具体范围,多数情况下都用for循环;表达式1做初始化用、表达式2是条件、表达式3一般用做自增自减
for(表达式1;表达式2;表达式3)语句
2.表达式
表达式就是一个可以运算的式子,容易出错的地方是运算符优先级:
优先级 |
运算符 |
0 |
() [] . |
1 |
! 正+ 负- ~ ++ -- *指针取地址内容 |
2 |
* / % |
3 |
加+ 减- |
4 |
<< >> >>> |
5 |
< <= > >= instanceof |
6 |
== != |
7 |
按位与& |
8 |
^ |
9 |
| |
10 |
&& |
11 |
?: |
12 |
= += -= *= /= % = &= |= ^= ~= <<= >>= >>>= |
四、数据类型
- 常规数据类型
(1)定义和修饰
除下表外其他数据类型修饰字:%o八进制,%x十六进制,%g自动选择格式不输出无意义的0
数据类型/数据类型限定字 |
格式化输入输出表示 |
大小 |
short或usigned short |
%h其他 |
|
char |
%c |
字符型,占1个byte;1个byte占4个bit位 |
char * |
%s |
字符串,本身占4个byte |
int |
%d或%i |
整形,占4个byte位 |
long |
%ld |
长整形,占4个byte位 |
unsigned |
%u |
无符号整形,占4个byte位 |
float |
%f |
单精度浮点数,占4个byte位 |
double |
%lf |
双精度浮点数,占8个byte位 |
long double |
%Lf |
|
%n |
记录%n出现之前的所有字符数 |
|
static |
变量只声明一次,而且一直保留它的最新状态,直到程序彻底结束 |
|
const |
常量 |
(2) 整型补充
整型和整型的除法得到的结果不等于理论值,而是对理论值的绝对值向下取整;数值类型的排序使用sort()会方便很多(默认从小到大排列,也可以自定义),比如sort(a,a+10,cmp);,对象a可以是数组也可以是其他含有数据的容器、cmp是自定义的函数。
(3) 字符型补充
字符型变量可以用整形的方式读出,得到的是它的ASII码值,小写字母的ASCII码减去大写字母的ASCII码值为32
① 字符转义序列
名称 |
转义序列 |
警报 |
\a |
换行符 |
\n |
回车符 |
\r |
水平制表符 |
\t |
垂直制表符 |
\v |
反斜杠 |
\\ |
问号 |
\? |
单引号 |
\’ |
双引号 |
\” |
回退符 |
\b |
换页符 |
\f |
② 比较实用的函数
函数 |
头文件 |
作用 |
参数及说明 |
toupper(‘XX’) |
<ctype.h> |
将XX转大写 |
XX是字符 |
tolower(‘XX’) |
<ctype.h> |
将XX转小写 |
XX是字符 |
getchar() |
从命令行接收一个字符 |
速度上比scanf要快 |
|
getch() |
<conio.h> |
同步接收一个字符 |
解决“一闪而过”的问题 |
putchar(XX) |
在命令行上打印一个字符 |
速度上比printf要快 |
|
sizeof(XX) |
获取数据类型在内存中的字节大小 |
XX是任意一种数据类型 |
- 联合、枚举
union联合,它的内存空间是共用的,最终内存的大小取决于最大的那个对象;enum枚举,同一个作用域内如果对象对应里面的值,相当于得到里面的编号、编号从0开始。
- 自定义的数据类型
C中的自定义数据类型指的是结构体struct,C++中还包括类;结构体名称就是类型的名称,访问里面元素可以点取,如果是结构体指针,还可以用箭头获取;结构体别名可以对结构体提前声明或定义,也是结构体的另一种名字,有别名的结构体可以像使用内置变量一样使用它。
(1) 定义和初始化
struct
{
属性序列
}别名0或别名0初始化,别名1或别名1初始化...;
或者
struct 结构体名称
{
属性序列;
}别名0或别名0初始化,别名1或别名1初始化...;
或者
typedef struct 结构体名称
{
属性序列;
}别名0,别名1,...;
struct 结构体名称 自定义变量{属性序列对应的值};
指针和引用
指针pointer可以理解为地址序列的首地址,但是指针自身有一个用来区别其他指针的地址;加*表示读出地址中的数据、加&表示取地址,进入下一个地址pointer++或者++pointer。
1.指针的赋值
int i=8848;
int *p=&i;
或者
int i=8848;
int *p;
p=&i;
2.分配内存空间
可以用malloc()或者calloc()来分配空间、 colloc()将清空空间中的内容,用realloc()可以重新改变分配空间的大小而尽量不改变其中的内容,最后用free()释放这些空间;全局的指针malloc后需要显式调用free来销毁,比如作为类(C++)或者结构体属性的变量,局部分配的指针可以由编译器自动销毁。
- 指针的用途
(1)数组元素是指针
比如char* planets[],这是个很特殊的数组,数组的每个元素都是char*也即字符串,很明显每个元素的长度可以不同。
(2)指向指针的指针
倘若二级指针p分配有足够的内存空间,那么访问i行j列就是*(*(p+i)+j),意思是二级指针p偏移i个单位、取地址得到一级指针,一级指针偏移j个单位、最终取地址中的数据。
(3)指向函数的指针
函数名是它自己的函数指针,函数指针比如double (*f)(double),如果函数也类似这样的形式、这个函数指针f就可以指向它;函数指针也可以做成数组,比如void (*file_cmd[])(void)={new_cmd,open_cmd,close_cmd},花括号中是定义过的函数,然后就像正常使用函数一样的使用file_cmd。
(4)受限指针
在一些底层文件上会看到受限指针,比如int* restrict p;,表示被指向的对象只允许p指针对它操作。
- const限定字
(1) const int* p,const在*左边,表示内容*p不能改变但是p的指向可以再变,这种情况做参数的时候比较常见;
(2) int* const p,const在*右边,表示p的指向不能改变、一般在定义的时候就初始化了;
(3) const后面的变量不能做左值、也不能做函数返回值,反之可以做左值或返回值;
(4) const的数据可以赋值给非const的数据,如果是指针这会造成安全隐患!(C++中不允许这样的赋值)
- 引用
C中没有引用专门的内容,不过有必要补充下。当递归含有参数并且这个参数需要统一的时候,C++可以用引用来唯一指代这个对象,避免每一级递归都修改参数最终无法统一;C的一个解决办法是将这个参数定义为全局变量。
五、时间日期
- 用法
以一个程序为例,获取本地时间并格式化输出;其他更详细的介绍可以查阅参考文档。
time_t obj;
time(&obj);
struct tm* bag=localtime(&obj);
char str[30];
strftime(str,30,"%Y-%m-%d %H:%M:%S",bag);
printf("%s\n",str);
七、字符串
C中的字符串变量就是字符串数组,这个数组的末尾装了空字符’\0’。
- 字符串的书写
如果字符串过长可以在句子后加\,换行从头书写;或者在句后加”,换行再加上”,这是用空白字符串做拼接、是更好的做法。
- 字符串的赋值
C允许对字符串取下标,就像数组一样;另外注意总需要留出一个位置给空字符。用sizeof()可以求出数组的真实长度。
char p[]="june 14";
char k[]={'j','u','n','e',' ','1','4','\0'};
const char* p=”abc”;
printf("%c\n",p[1]);
printf("%c\n",”abc”[1]);
- 字符数组和字符指针
char data0[]=”june 14”;
char* data1=”june 14”;
前者可以修改”june 14”中的内容,后者不可以,因为它没有分配内存空间。一种做法是字符指针指向已有的数组,另一种做法是为指针动态分配内存空间。
- 字符串的读写
scanf、gets可以实现读,gets与scanf不同的是它不会跳过空白字符、而且一定要注意输入的字符个数限制;
printf、puts可以实现写,puts与printf不同的是会在输出后自动添加换行符。
- C语言的字符串库
#include <string.h>,函数的具体使用方法可以查看api文档
strcpy |
字符串复制 |
strlen |
字符串的长度,即字符串数组的实际长度-1 |
strcat |
字符串的拼接 |
strcmp |
字符串比较,返回0表示相等、1表示大于、-1表示小于 |
八、输入输出流
1.输入输出重定向
将文件作为输入传递给应用程序,或者将程序的输出保存到文件,比如demo是一个应用程序:
demo <in.dat
demo >out.dat
>和<可以同时出现,没有顺序的要求,如果出现错误将输出到屏幕。
2.文件输入/输出流
以写方式打开的文件不管打开是否成功最后都要关闭
(1)比较实用的函数
tmpfile |
产生临时文件 |
fopen |
打开文件 |
fclose |
关闭文件 |
fflush |
清理缓冲区 |
remove |
删除文件 |
rename |
重命名文件,文件是已经关闭了的 |
fprintf |
对流格式化的输出,比如对stderr错误流输出 |
fscanf |
从流中读出数据,成功返回1 |
feof |
返回非零的值表明流中设置了文件末尾指示器 |
ferror |
返回非零值表明设置了错误的指示器,也即读错误 |
tmpnam(null); |
返回一个无重复的文件名 |
(2)文件的读写模式
带有+的模式从读转到写需要使用fflush()或者文件定位函数;二进制的文件读写模式在下面的基础上加上b,意思不变。
r |
只读,文件不存在会出错 |
w |
只写,文件不存在会新建 |
a |
追加 |
r+ |
读和写,从文件头开始 |
w+ |
读和写,将覆盖原有内容 |
a+ |
读和写,文件存在就追加 |
(3) 扫描集
在类似scanf()的函数中可能会用到,scanf()是一个匹配的函数,会根据输入和扫描集匹配接收一些值。
扫描集默认各元素默认以空格作分隔;%*屏蔽与它相邻的后一个元素;%[集合]匹配出现在集合中的情况;%[^集合]匹配不在集合中的情况。
(4) 文件内容的定位
fseek |
设置文件位置 |
ftell |
以长整型返回当前文件位置 |
rewind |
文件位置设置在起始位置,同时清除错误指示器 |
fgetpos |
超大文件中设置文件位置 |
fsetpos |
超大文件中获取文件位置 |
(5) 字符形式的输入输出
fputc、fgetc |
输入输出,只做函数调用 |
putc、getc |
输入输出,像宏一样调用、速度要快 |
putchar、getchar |
标准输入输出 |
(6) 字符串形式的输入输出
puts、gets用于标准输入输出流,puts会自动添加上换行符;fputs、fgets对流不挑剔,使用范围会更广些。
(7) 块形式的输入输出
fread、fwrite主要用于二进制流,比如结构体读写,返回值表示的是实际读入或写入的数量。
3.字符串输入/输出流
用字符串做输入输出,可以用字符串的方法操作里面的内容(格式化等),然后在需要的时候取出,这是很有意义的
sprintf(str,”%ld”,31415926); |
将数字输入到字符串str |
snprintf(str,25,”%ld”,31415926); |
将数字输入到字符串str,实际大小不会超过限定的数目 |
sscanf(str,”%d”,&a); |
将数据str给某变量a |
九、底层程序设计
1. 宏定义
2.数的进制
0x |
16进制,比如0x00ff,每一位表示4个bit位 |
2. 运算符
考虑到可移植性,最好仅对无符号数(比如unsigned short)进行移位运算
<< |
左移位 |
>> |
右移位 |
~ |
按位取反 |
& |
按位与 |
^ |
按位异或 |
| |
按位或 |
|= |
设置某一位的值,比如i |= 0x0010把对应位设置为1 |
&= |
清除某一位的值,比如i &= ~0x0010把对应位设置为0 |
3. 操作二进制某一位
i |= 1<<j |
从右往左、将i的对应位置为1,j从0开始 |
i &=~(1<<j) |
从右往左、将i的对应位置为0,j从0开始 |
if(i&(1<<j)){} |
测试i的对应位置是否为1,j从0开始 |
(4) 操作位域
顾名思义,就是操作遗传二进制序列
示例 |
说明 |
i = i &~0x0070 | j<<4 |
先清除4到6位的值,然后将4到6位的值置为j |
j = (i>>4) & 0x0007 |
获取4到6位的值 |
十、程序结构(函数、预处理、编写大型程序)
1.头文件、源文件
头文件指.h文件、源文件指.c文件,头文件只是声明、源文件具体来定义
2.将程序分为多个文件
对于较复杂或者庞大的程序来说这是很有用的,头文件中虽然也可以写定义的内容,但是最好只写声明、将具体的定义交给源文件。
3.引入文件
#include <文件名> |
搜寻系统头文件所在的目录 |
#include “文件名” |
先搜寻当前目录,再搜寻系统头文件所在的目录;文件名中可以有相对路径 |
#include 标记 |
这个“标记”是用#define定义的 |
十一、标准库及其他库函数
布尔值:C89中没有布尔值,一种做法用0和1代替变量的真和假,但是并不直观;另一种做法是宏定义,#define TRUE 1、#define FALSE 0;C99对此有了进一步的/改进,提供了专门的数据类型_Bool,但是这个布尔值是用整型数据来代替的;除_Bool外,C99提供了一个新的头文件<stdbool.h>,更容易的使用布尔变量。
函数 |
头文件 |
说明 |
assert(条件语句); |
#include<cassert> |
可以做调试语句用,条件语句为真才能通过“测试” |
_findfirst(const char*,struct _finddata_t*); _findnext(intptr_t,struct _finddata_t*); _findclose(); |
#include<io.h> |
获取某个目录下的文件名及文件,不是标准库文件、但是为核心库提供支持。 |
十二、附录
1.CodeBlocks快捷键
ctrl+d |
复制行或者选中块 |
ctrl+滚轮 |
放缩内容大小 |
ctrl+shift+c |
注释行或者选中块 |
ctrl+shift+x |
反注释行或者选中块 |
ctrl+l |
删除行或者选中块 |
ctrl+g |
到达指定行 |
ctrl+j |
自动补全框架 |
ctrl+pgup |
到达上一个函数 |
ctrl+pgdown |
到达下一个函数 |
ctrl+r |
替换内容 |
alt+拖动 |
拖动选中的内容 |
alt+鼠标选中 |
编辑列 |
f10 |
全屏 |
f5 |
添加/去除断点 |
f7 |
next line |
f8 |
debug |
f9 |
编译运行 |
ctrl+b |
加书签 |
alt+pgup/pgdn |
到上/下一个书签的位置 |
- CodeBlocks下使用Debug
(1) 确定语法上没有错误,再考虑使用Debug来检测错误
(2) 声明用于测试的变量,下划线加上要测试的对象名字,之后把它们添加到需要测试的地方
(3) 在函数开头、循环开头添加断点,可以方便的对这些地方进行测试,不想测试的时候也可以跳出
(4) 运行Debug,跟踪最新变量的变化是否正确
(5) 测试结束后,删除所有用于测试的变量