1、宏定义define
用于程序开头,比如定义标识符来表示常量,后面要修改常量的值,这个常量的值在整个程序中就会被替换
#define size 10 //后面没有分号" ; ",如果写上分号就会出现错误
比如定义一个数组
int data[size]; //加上分号就会出现如下错误
2、关键字typedef
①typedef为数据类型起一个新的别名,就像人的“绰号”
typedef oldName newName;
//如typedef int INTEGER;
//用INTEGER来代替int,INTEGER a;就是int a;
②typedef 还可以给数组、指针、结构体等类型定义别名。
给数组类型定义别名的例子如下:
typedef char Array20[20]; //这里的Array20是char[20]的别名,如果定义Array20 a;等价于char a[20];
给结构体类型定义别名如下:
typedef struct stu{
....;
} STU;
//STU就是struct stu的别名,STU student;等价于struct stu student;
typedef还可以为函数类型定义别名,如
typedef int (*PTR_TO_FUNC)(int,int);
int max(int a,int b){....;}
PTR_TO_FUNC pfunc=max;
//调用max(10,20); 可直接(*pfunc)(10,20);
typedef和#define的区别
typedef 在表现上有时候类似于 #define,但它和宏替换之间存在一个关键性的区别。把 typedef 看成一种彻底的“封装”类型,声明之后不能再往里面增加别的东西。如
#define INTERGE int
unsigned INTERGE n; //没问题
typedef int INTERGE;
unsigned INTERGE n; //错误,不能在 INTERGE 前面添加 unsigned
3、C与C++库文件的区别
定义库文件用 #include<>
C语言里stdio.h头文件是主要的,而C只是C++的一个子集,且在C++中已不推荐使用C的类库,但为了对已有代码的保护,还是对原来的头文件支持。
C语言里定义库文件用
#include<xxx.h>
如 #include<stdio.h>
而C++里定义库文件用
#include<cxxx.h>
如#include<cstdio.h>
在C++里还使用
#include<iostream>
using namespace std; //必须的
//iostream是C++里定义标准输入/输出流对象的头,输入输出可用cin,cout。
想要了解更多的库文件可登录网页http://www.cplusplus.com/
下面是一些常用的库文件
stdio.h执行输入/输出操作(C里定义输入输出函数的头,输出可用printf)
stdlib.h定义了几个通用函数,包括动态内存管理、随机数生成、与环境的通信、整数算术、搜索、排序和转换。
string.h定义了几个函数来操作C字符串和数组
math.h定义了几个函数执行数学操作
ctype.h声明了一组用于分类和转换单个字符的函数
time.h定义了获取和操作日期和时间信息的函数
string.h是C库头文件,对应基于char*的字符串处理函数
cstring是c++对标准c字符串处理函数的c++增强
string是C++标准库(STL)中string字符串类,定义了字符串的各种操作
4、C语言定义结构体
结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。
①先定义结构体类型,再定义结构体变量
struct student{
结构体成员;
};
struct student stu1;
②定义结构体类型的同时定义结构体变量
struct student{
结构体成员;
}stu1;
//之后还可以继续定义结构体变量,如struct student stu2;
③不指定结构体类型而直接定义结构体变量
struct {
结构体成员;
}stu1;
//一般不使用这种方法,因为直接定义结构体变量stu1之后,就不能再继续定义该类型的变量。
④用typedef定义结构体变量
typedef struct student {
结构体成员;
}STU;
//用STU代替struct student,之后定义变量可以直接STU stu1;
结构体用点号" . “获取单个成员,如stu1.成员名;
定义结构体时后面的分号” ; "不可少。需要注意的是,结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。
5、路径分析
绝对路径:指文件在硬盘上真正存在的路径,如"E:\egg\count.cpp".
相对路径:相对于目标文件的位置。
如果1.txt在"E:\egg"目录下,和count.cpp同目录下,count.cpp读1.txt,路径route=“1.txt”。
如果1.txt在file目录下,而file与count.cpp同目录下,则count.cpp读1.txt,路径route=“file/1.txt”。
如果1.txt的位置是"E:\1.txt",在count.cpp的上一级目录,则count.cpp读1.txt,路径route="…/1.txt"。
根目录:指逻辑驱动器的最上一级目录,是相对于子目录来说的。
相对虚拟目录:如route="/file/1.txt";file前面有个"/“字符,这个“/”代表的是虚拟目录的根目录。
若把"E:\egg"设为虚拟目录,那么”/file/1.txt"的真实路径为"E:\egg\file\1.txt"。
6、C语言输入输出
①输出。在C语言有三个函数可以用来在显示器上输出数据。
a、puts():(output string)只能输出字符串,并且在输出结束后会自动换行。
b、putchar():只能输出单个字符。
c、printf():可以输出各种类型的数据,完全可以替代 puts() 和 putchar()。
printf输出
格式控制符 说明
%c 输出一个单一的字符
%hd\%d\%ld 以十进制、有符号的形式输出 short、int、long 类型的整数
%hu\%u\%lu 以十进制、无符号的形式输出 short、int、long 类型的整数
%ho\%o\%lo 以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数
%#ho\%#o%#lo 以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数
%hx\%x\%lx 以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。这里的x也可大写,若是大写的X,那么输出的十六
进制和前缀也要大写
%#hx\%#x\%#lx 以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数.x与X与上面同理。
%f\%lf 以十进制的形式输出 float、double 类型的小数
%s 输出一个字符串
printf()整齐输出用法:
printf()格式控制符的完整形式如下:
%[flag][width][.precision]type
//flag 是标志字符。如%#x中flag为 #,%-3d中flag为 - 。
标志符号 含义
- 表示左对齐。如果没有,就按照默认的对齐方式,默认一般为右对齐。
+ 用于整数或者小数,表示输出符号(正负号)。如果没有,那么只有负数才会输出符号。
空格 用于整数或者小数,输出值为正时冠以空格,为负时冠以负号。
# 对于八进制(%o)和十六进制(%x / %X)整数,# 表示在输出时添加前缀;八进制的前缀是 0,十六进制的前缀是 0x / 0X。
对于小数(%f / %e / %g),# 表示强迫输出小数点。如果没有小数部分,默认是不输出小数点的,加上 # 以后,即使没有小数部分也会带上小数点。
//width 表示至少占用几个字符的位置,如%-3d,这里的width=3表示输出结果最少占用3个字符的宽度。若输出结果的宽度不足width,以空格补齐。若没有对齐方式,默认会在左边补齐空格。
//.precision 表示输出精度,也就是小数的位数。
- 当小数部分的位数大于 precision 时,会按照四舍五入的原则丢掉多余的数字。
- 当小数部分的位数小于 precision 时,会在后面补 0。
-
//.precision 也可以用于整数和字符串,但是功能却是相反的
- 用于整数时,.precision 表示最小输出宽度。与 width 不同的是,整数的宽度不足时会在左边补 0,而不是补空格。
- 用于字符串时,.precision 表示最大输出宽度,或者说截取字符串。当字符串的长度大于 precision 时,会截掉多余的字符;当字符串的长度小于 precision 时,.precision 就不再起作用。
//type 表示输出类型,如上面的d,f,c,x。
②输入。从键盘获得数据。
a、scanf():和printf类似,scanf()可以输入多种类型的数据。
对于scanf(),输入数据的格式和控制字符串的格式保持一致。如:
scanf("%d %d",&m,&n);这里的%d之间是有两个空格的,所以在键入m和n的值时,也要有空格(不管有几个只要有就行),如5 7。
如果输入m后按下回车键,scanf到缓冲区读取数据,如果符合要求,程序继续等待输入。如果不符合要求就读取失败,不再让用户输入。这个是针对空格的情况,回车键相当于空格键。
如果scanf("%d,%d",&m,&n);要求输入的m和n之间有 ",",这时输入m就按下回车键,会读取失败。
如果scanf("%d ,%d",&m,&n);","之前有个空格,这时输入m就按下回车键,会再让用户输入,但是必须输入",n"才能正确读取。
以上情况是对于一个scanf()的。若是两个scanf()如:
scanf("a=%d",&a);
scanf("b=%d",&b);
这里需要输入"a=10b=5"才能符合,中间并没有空格。
但如果scanf("a=%d ",&a);%d后面有空格就可以按下回车键。
scanf()与printf()非常相似,只是scanf的变量前要带有符号 "&",这个是取地址符号,取变量在内存中的地址。
我们从键盘输入的数据并没有直接交给 scanf(),而是放入了缓冲区中,直到我们按下回车键,scanf() 才到缓冲区中读取数据。如果缓冲区中的数据符合 scanf() 的要求,那么就读取结束;如果不符合要求,那么就继续等待用户输入,或者干脆读取失败。
在C语言中没有专门的字符串类型,我们只能使用数组或者指针来间接地存储字符串。如
char str1[]="aaadvffg";
char *str2="ccccccc";
//这两种形式其实是有区别的,第一种形式的字符串所在的内存既有读取权限又有写入权限,第二种形式的字符串所在的内存只有读取权限,没有写入权限。printf()、puts() 等字符串输出函数只要求字符串有读取权限,而 scanf()、gets() 等字符串输入函数要求字符串有写入权限,所以,第一种形式的字符串既可以用于输出函数又可以用于输入函数,而第二种形式的字符串只能用于输出函数。
scanf("%s",str);
str前面可以不加&,因为字符串的名字会自动转换为字符串的地址。
**scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串**
b、getchar()、getche()、getch():这三个函数都用于单个字符。
getchar()就是scanf("%c", c),getchar() 就是 scanf() 的一个简化版本。
getche()没有缓冲区,输入一个字符后会立即读取,不用等待用户按下回车键,这是它和 scanf()、getchar() 的最大区别。
getche() 位于 conio.h 头文件中,而这个头文件是 Windows 特有的,不能在 Linux 和 Mac OS 下使用。
getch() 也没有缓冲区,输入一个字符后会立即读取,不用按下回车键,这一点和 getche() 相同。getch() 的特别之处是它没有回显,看不到输入的字符。
getch() 也位于 conio.h 头文件中,也不是标准函数,默认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用。
c、gets():获取一行数据,并作为字符串处理。
gets() 是有缓冲区的,每次按下回车键,就代表当前输入结束了,gets() 开始从缓冲区中读取内容,这一点和 scanf() 是一样的。gets() 和 scanf() 的主要区别是:
- scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
- gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。
7、文件操作
常见的文件有Word、txt、源文件等,而在操作系统里不同的硬件设备也被看成一个文件,对这些文件的操作也等同于磁盘上普通文件的操作。如:
通常把显示器成为标准输出文件,printf就是向这个文件输出数据。
通常把键盘称为标准输入文件,scanf就是从这个文件读取数据。
stdin 标准输入文件,一般指键盘;scanf()、getchar() 等函数默认从 stdin 获取输入。
stdout 标准输出文件,一般指显示器;printf()、putchar() 等函数默认向 stdout 输出数据。
stderr 标准错误文件,一般指显示器;perror() 等函数默认向 stderr 输出数据。
stdprn 标准打印文件,一般指打印机。
操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件。文件在进行读写操作之前要先打开,使用完毕要关闭。打开文件,就是获取文件的有关信息,例如文件名、文件状态、当前读写位置等,这些信息会被保存到一个 FILE 类型的结构体变量中。关闭文件就是断开与文件之间的联系,释放结构体变量,同时禁止再对该文件进行操作。数据从文件复制到内存的过程叫做输入流,从内存保存到文件的过程叫做输出流。
①打开文件
<stdio.h>头文件里的fopen()函数打开文件,用法为:
FILE *fopen(char *filename, char *mode);
//会返回指针,即保存相关信息的结构体变量(FILE类型的)
//如FILE *fp = fopen("1.txt","r");fp指向该文件,若打开文件出错,fopen()会返回一个空指针NULL。
//filename文件名(包括文件路径),mode表示打开方式,他们都是字符串
//mode为打开文件方式
控制读写权限的字符串(必须指明)
| 打开方式 | 说明 |
| "r" | 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。|
| "w" | 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(删了再创)。|
| "a" | 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾|
| "r+" | 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。|
| "w+" | 以“写入/更新”方式打开文件,相当于w和r+叠加的效果。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容|
| "a+" | 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。|
控制读写方式的字符串(可以不写)
| 打开方式 | 说明 |
| "t" | 文本文件。如果不写,默认为"t"|
| "b" | 二进制文件|
读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部,就是说不能将读写方式放在读写权限的开头。
将读写方式放在读写权限的末尾:“rb”、“wt”、“ab”、“r+b”、“w+t”、“a+t”
将读写方式放在读写权限的中间:“rb+”、“wt+”、“ab+”
文件打开方式由 r、w、a、t、b、+ 六个字符拼成,各字符的含义是:
- r(read):读
- w(write):写
- a(append):追加
- t(text):文本文件
- b(banary):二进制文件
- +:读和写
②关闭文件
fclose()函数把文件关闭,如fclose(fp);
③读写文件
a、字符读取函数fgetc(file get char,即从文件中读取一个字符)。用法如下:
int fgetc(FILE *fp);
//fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF(end of file).是定义在stdio.h的宏,它的值是一个负数,往往是-1。
//文件内部有一个位置指针,用来指向当前读写到的位置。在文件打开时,该指针总是指向文件的第一个字节。使用 fgetc() 函数后,该指针会向后移动一个字节,所以可以连续多次使用 fgetc() 读取多个字符。
很多函数在读取出错时也返回 EOF,我们可以借助 stdio.h 中的两个函数来判断文件读取完毕了还是读取出错了.
feof() 函数用来判断文件内部指针是否指向了文件末尾,用法如下:
int feof ( FILE * fp );
//当指向文件末尾时返回非零值,否则返回零值。
ferror() 函数用来判断文件操作是否出错,用法如下:
int ferror ( FILE *fp );
//出错时返回非零值,否则返回零值
b、字符写入函数 fputc(file output char,向指定的文件中写入一个字符)。用法如下:
int fputc ( int ch, FILE *fp );
//ch 为要写入的字符,fp 为文件指针。fputc() 写入成功时返回写入的字符,失败时返回 EOF.
//若文件不存在时则创建该文件。
//每写入一个字符,文件内部位置指针向后移动一个字节
c、读字符串函数 fgets,从指定的文件中读取一个字符串,并保存到字符数组中,用法如下:
char *fgets ( char *str, int n, FILE *fp );
//str 为字符数组,n 为要读取的字符数目,fp 为文件指针。
//返回值:读取成功时返回字符数组首地址,也即 str;读取失败时返回 NULL;如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL。
//读取到的字符串会在末尾自动添加 '\0',n 个字符也包括 '\0',实际只读取到了 n-1 个字符。
在读取到 n-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。fgets() 最多只能读取一行数据,不能跨行。
fgets() 遇到换行时,会将换行符一并读取到当前字符串。gets() 不一样,它会忽略换行符。
d、写字符串函数 fputs,向指定的文件写入一个字符串。用法如下:
int fputs( char *str, FILE *fp );
//str 为要写入的字符串,fp 为文件指针。写入成功返回非负数,失败返回 EOF。
e、读写数据块
fread()用来从指定文件中读取块数据。块数据,就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据,并没有什么限制。用法如下:
size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
fwrite() 函数用来向文件中写入块数据。用法如下:
size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );
- ptr 为内存区块的指针,它可以是数组、变量、结构体等。fread() 中的 ptr 用来存放读取到的数据,fwrite() 中的 ptr 用来存放要写入的数据。
- size:表示每个数据块的字节数。
- count:表示要读写的数据块的块数。
- fp:表示文件指针。
size_t 是在 stdio.h 和 stdlib.h 头文件中使用 typedef 定义的数据类型,表示无符号整数,也即非负数,常用来表示数量。
返回值:返回成功读写的块数,也即 count。如果返回值小于 count:
- 对于 fwrite() 来说,肯定发生了写入错误,可以用 ferror() 函数检测。
- 对于 fread() 来说,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
函数rewind(),头文件是#include<stdio.h>。用于将文件指针重新指向文件的开头,同时清除和文件流相关的错误和eof标记。用法如下:
void rewind(FILE * stream);
//stream为打开文件的指针,相当于上面的fp;
相当于调用
fseek(stream, 0, SEEK_SET);
函数ftell()获得当前的位置指针。
注意:fgetc和fputc是每次操作一个字符,fgets和fputs是每次操作一行,fread和fwrite是操作块数据,可以是一个字符,可以是一个字符串,可以是多行数据。
④格式化读写文件
fscanf() 和 fprintf() 函数与前面使用的 scanf() 和 printf() 功能相似,都是格式化读写函数,两者的区别在于 fscanf() 和 fprintf() 的读写对象不是键盘和显示器,而是磁盘文件。两个函数的原型是:
int fscanf ( FILE *fp, char * format, ... );
int fprintf ( FILE *fp, char * format, ... );
//与 scanf() 和 printf() 相比,它们仅仅多了一个 fp 参数。
//scanf()从键盘读入数据,fscanf()是从文件读入数据。若fp设置为stdin,那么 fscanf() 函数将会从键盘读取数据,与 scanf 的作用相同。
//printf()输出数据到显示器,fprintf()输出数据到文件。若fp设置为 stdout,那么 fprintf() 函数将会向显示器输出内容,与 printf 的作用相同。
⑤随机读写文件
移动文件内部位置指针的函数主要有两个,即 rewind() 和 fseek()。
rewind() 用来将位置指针移动到文件开头。用法如下:
void rewind ( FILE *fp );
fseek() 用来将位置指针移动到任意位置。用法如下:
int fseek ( FILE *fp, long offset, int origin );
//fp 为文件指针,也就是被移动的文件
// offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。
//origin 为起始位置,也就是从何处开始计算偏移量。
起始点 常量名 常量值
文件开头 SEEK_SET 0
当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2