目录
*******************************************************
一、程序书写tips
(1)连续赋值c=b=a=0;是可以的,赋值从右向左依次进行,,但是不能int a=b=0,,这样b是未定义的
(2)exit(const),,表示终止程序,强制返回到操作系统,,,当const=0时,,正常退出,,,当const!=0时,,表示程序出现某种错误后退出头,包含的文件:“stdlib.h ” 或写 <cstdlib>
通常情况下,程序成功执行完一个操作正常退出的时候会带有值 EXIT_SUCCESS。在这里,EXIT_SUCCESS 是宏,它被定义为 0。
如果程序中存在一种错误情况,当您退出程序时,会带有状态值 EXIT_FAILURE,被定义为 -1。所以,上面的程序可以写成:
#include <stdio.h>
#include <stdlib.h>
main()
{
int dividend = 20;
int divisor = 5;
int quotient;
if( divisor == 0){
fprintf(stderr, "除数为 0 退出运行...\n");
exit(EXIT_FAILURE);
}
quotient = dividend / divisor;
fprintf(stderr, "quotient 变量的值为: %d\n", quotient );
exit(EXIT_SUCCESS);
}
(3)C语言没有global关键字,,python没有static关键字
(4)当前最新的C语言标准为 C11 ,在它之前的C语言标准为 C99。
gets(str); 读入字符串
puts(str); 打印字符串
(5)变量,数组,字符串,函数,结构体,,,,其实都是内存上一系列连续字节的集合,,C语言可以直接对这些内存进行操作(通过指针)
(6)由于 C 语言所产生的代码运行速度与汇编语言编写的代码运行速度几乎一样
(7)在 C 程序中,分号是语句结束符
(8)当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化:
数据类型 | 初始化默认值 |
---|---|
int | 0 |
char | '\0' |
float | 0 |
double | 0 |
pointer | NULL |
(9)
- #include < > 引用的是编译器的类库路径里面的头文件。
- #include " " 引用的是你程序目录的相对路径中的头文件,如果在程序目录没有找到引用的头文件则到编译器的类库路径的目录下找该头文件。
(10)sizeof()的返回值是以字节为单位,sizeof()可以通过一块数据的首地址指针来查看这块数据占多少字节
(11)注意:因为struct是一种数据类型,那么就肯定不能定义函数,所以在面向c的过程中,struct不能包含任何函数。否则编译器会报错
二、multi-sources处理
请看这篇文章
三、关键字
关键字 | 说明 |
---|---|
auto | 声明自动变量 |
break | 跳出当前循环 |
case | 开关语句分支 |
char | 声明字符型变量或函数返回值类型 |
const | 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变 |
continue | 结束当前循环,开始下一轮循环 |
default | 开关语句中的"其它"分支 |
do | 循环语句的循环体 |
double | 声明双精度浮点型变量或函数返回值类型 |
else | 条件语句否定分支(与 if 连用) |
enum | 声明枚举类型 |
extern | 声明变量或函数是在其它文件或本文件的其他位置定义 |
float | 声明浮点型变量或函数返回值类型 |
for | 一种循环语句 |
goto | 无条件跳转语句 |
if | 条件语句 |
int | 声明整型变量或函数 |
long | 声明长整型变量或函数返回值类型 |
register | 声明寄存器变量 |
return | 子程序返回语句(可以带参数,也可不带参数) |
short | 声明短整型变量或函数 |
signed | 声明有符号类型变量或函数 |
sizeof | 计算数据类型或变量长度(即所占字节数) |
static | 声明静态变量 |
struct | 声明结构体类型 |
switch | 用于开关语句 |
typedef | 用以给数据类型取别名 |
unsigned | 声明无符号类型变量或函数 |
union | 声明共用体类型 |
void | 声明函数无返回值或无参数,声明无类型指针 |
volatile | 说明变量在程序执行中可被隐含地改变 |
while | 循环语句的循环条件 |
四、输入流和输出流
(1)输出:
printf() 用于格式化输出到屏幕。printf() 函数在 "stdio.h" 头文件中声明。
- %f 以十进制形式输出 float 或double类型;
- %lf 以十进制形式输出 double 类型;
- 注意:scanf 输入情况下 double 必须用 %lf,float 必须用 %f 不能混用。
- %p输出地址
- %e 以指数形式输出 float 类型,输出结果中的 e 小写;
- %E 以指数形式输出 float 类型,输出结果中的 E 大写;
- %le 以指数形式输出 double 类型,输出结果中的 e 小写;
- %lE 以指数形式输出 double 类型,输出结果中的 E 大写。\
- %hd 输出short型十进制整数
printf("%-m.nf",123.45) // -表示数据左对齐,右补空格,,m表示整体数据占用的最小位宽,,n表示保留n位小数
printf("%%")输出一个%
(2)输入
从键盘输入信息时,C语言是按照内存缓冲区顺序读取内容的,必要时要用getchar()把回车符读走,,回车符是个特殊的符号,,它标志着输入的结束
scanf("%s %s %s",<&变量>,<&变量>,<&变量>);
- 1.scanf不接受 空格 制表符 回车符,中间的空格和回车符会被scanf删掉,最后一个回车符会被留在缓冲区里
- 2.gets(str)接受回车 制表符 和空格 (同时回车也标记着结束)
int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符
char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。
int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout。
#include <stdio.h>
int main( )
{
char str[100];
printf( "Enter a value :");
gets( str );
printf( "\nYou entered: ");
puts( str );
return 0;
}
五、数据类型
(1)简单数据类型
类型 | 存储大小 | 值范围 |
---|---|---|
char | 1 字节 | -128 到 127 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
float | 4 字节 | 1.2E-38 到 3.4E+38 | 6 位小数 |
double | 8 字节 | 2.3E-308 到 1.7E+308 | 15 位小数 |
long double | 16 字节 | 3.4E-4932 到 1.1E+4932 | 19 位小数 |
转义序列 | 含义 |
---|---|
\\ | \ 字符 |
\' | ' 字符 |
\" | " 字符 |
\? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
(2)容器数据类型
(1)数组
数组名表示数组首地址,一般可作为实参进行函数传递
数组名是常量指针
如果在定义的同时初始化,一维数组可不写长度,二维数组的列长度不可省略
如果只定义不初始化,n维数组都要完完全全的写上长度
(2)字符串
#include <stdio.h>
const int MAX = 4;
int main ()
{
const char *names[] = {
"Zara Ali",
"Hina Ali", //字符串本身表达的也是首字母地址
"Nuha Ali",
"Sara Ali",
};
int i = 0;
for ( i = 0; i < MAX; i++)
{
printf("Value of names[%d] = %s\n", i, names[i] );
}
return 0;
}
在 C 语言中,字符串实际上是使用 null 字符 '\0' 终止的一维字符数组
#include <stdio.h>
int main()
{
char greeting[6] = { 'H', 'e', 'l', 'l', 'o', }; //末尾的'\0'可以省略,正常输出字符串,如果想要输出地址,则用%p
printf("Greeting message: %s\n", greeting);
return 0;
}
序号 | 函数 & 目的 |
---|---|
1 | strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 |
2 | strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1); 返回字符串 s1 的长度。(不算'\0') |
4 | strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。 |
5 | strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
(2)枚举
enum 枚举名 {枚举元素1,枚举元素2,……};
枚举元素的值不能赋值为浮点数
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
//枚举的元素按整型递增,默认初始化为0
#include<stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
enum DAY day; //用枚举模板创造实例
day = WED; //说明枚举的值在外部是可以访问的
printf("%d",day);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum day
{
saturday,
sunday,
monday,
tuesday,
wednesday,
thursday,
friday
} workday;
int a = 1;
enum day weekend;
weekend = ( enum day ) a; //类型转换
//weekend = a; //错误
printf("weekend:%d",weekend);
return 0;
}
(3)指针
见这篇文章:关于NULL和void和0的区别
if(ptr) /* 如果指针p不指向NULL,则完成 */
if(!ptr) /* 如果指针p指向NULL,则完成 */
int *f() -----------表示一般函数的返回值是个指针 返回的是一块数据的首地址
指针类型p+ 1是指p + sizeof(基类型)---不是指单纯的加1,他们之间的区别由编译器来完成
牢记a[i] ↔ *(p+i)
int (*p)[3]; //表示的是数组名是个指针,因为数组名本来就是地址,它所以是二阶指针,
对于二维数组 a[3][3];
a[0]代表a[0][0]的地址,a[0]是指向二维数组第0行第0列的指针,是一阶指针
a代表的是a[0][X]的地址,a是指向二维数组的第0行的指针,它是二阶指针
*a==a[0], *a[0]==a[0][0]
*(*(p+i)+j) 取出第i行第j列的内容(int型元素的值),即a[i][j]
int a[3][3];
int* p=a[0]
for (i=0; i<m; i++) {
for (j=0; j<n; j++) {
printf("%d", *(p+i*n+j)); //当第一行遍历完时,用i*n保存住已经加的值,,相当于看成了一维数组,顺序遍历
}
}
sizeof()的返回值是以字节为单位,sizeof()可以通过一块数据的首地址来查看这块数据占多少字节
(3)结构体
结构体不允许使用==判断是否相等,,因为在存储结构体变量时,存在内存对齐现象
相同模板的结构体可以相互赋值,,所以经过结构体封装后的数组可以直接赋值
//此结构体的声明包含了其他的结构体
struct COMPLEX
{
char string[100];
struct SIMPLE a;
};
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
char string[100];
struct NODE *next_node; //struct NODE可以看作用户创造的数据类型,这里定义了一个指向该结构体的指针
};
如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:
struct B; //对结构体B进行不完整声明
//结构体A中包含指向结构体B的指针
struct A
{
struct B *partner;
//other members;
};
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
struct A *partner;
//other members;
};
结构体变量的初始化:
#include <stdio.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456}; //初始化
int main()
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}
(4)位域
为了节省内存,把一个字长划分为几个域分配给多个变量
struct bs{
int a:8; //a位域,占用8个bit,但是不能大于64bit
int b:2; //b位域
int c:6; //c位域
}data;
说明 data 为 bs 变量,共占两个字节。其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位。
一个位域不允许跨字长,如果计算机字长是64位,则一个位域最大就64个位,超出之后编译器可能出现错误
位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的:
struct k{
int a:1;
int :2; /* 该 2 位不能使用 */
int b:3;
int c:2;
};
main(){
struct bs{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.b=7; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.c=15; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
printf("%d,%d,%d\n",bit.a,bit.b,bit.c); /* 以整型量格式输出三个域的内容 */
pbit=&bit; /* 初始化*/
pbit->a=0; /* 用指针方式给位域 a 重新赋值,赋为 0 */
pbit->b&=3; /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
pbit->c|=1; /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c); /* 用指针方式输出了这三个域的值 */
}
下面的结构中,status 变量将占用 4 个字节的内存空间(int型),但是只有 2 位被用来存储值。如果您用了 32 个变量,每一个变量宽度为 1 位,那么 status 结构将继续使用 4 个字节
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
}
#include <stdio.h>
#include <string.h>
struct
{
unsigned int age : 3;
} Age;
int main( )
{
Age.age = 4;
printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
printf( "Age.age : %d\n", Age.age );
Age.age = 7;
printf( "Age.age : %d\n", Age.age );
Age.age = 8; // 二进制表示为 1000 有四位,超出
printf( "Age.age : %d\n", Age.age );
return 0;
}
age 变量将只使用 3 位来存储这个值,如果您试图使用超过 3 位,则无法完成
(4)共用体
顾名思义,共用内存
共用体占用的内存应足够存储共用体中最大的成员
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf( "Memory size occupied by data : %d\n", sizeof(data));
return 0;
}
Memory size occupied by data : 20
在这里,我们可以看到共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
return 0;
}
******************
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
(3)数据类型转换
强制类型转换形式: (类型说明符)(表达式)
隐式转换:
(4)存储类型
auto //这是一个缺省类型,,又叫做动态局部类型,,语句块里的变量默认为auto,,但是auto不能用在语句块外
extern //当其他文件或者变量在定义之前需要用到该变量时,需要用extern声明
static //分为静态局部变量和静态全局变量,,其作用是变量只能被初始化一次,,且外文件无法访问
register //现在计算机会默认把变量存储在寄存器里,也是缺省类型
(1)静态存储区
生命周期是整个程序
用于存放 全局变量(定义在函数体之外) 和 静态变量(用static修饰)
自动初始化为0
(2)动态存储区
生命周期在块内
用于存放 局部变量 和 形参
六、运算符
(1)优先级
单目运算符 > 算术运算符 > 比较运算符 > && > || > ?:>赋值运算符>逗号运算符
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] -> . ++ - - | 从左到右 |
一元 | + - ! ~ ++ - - (type)* & sizeof | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与 AND | & | 从左到右 |
位异或 XOR | ^ | 从左到右 |
位或 OR | | | 从左到右 |
逻辑与 AND | && | 从左到右 |
逻辑或 OR | || | 从左到右 |
条件 | ?: | 从右到左 |
赋值 | = += -= *= /= %=>>= <<= &= ^= |= | 从右到左 |
逗号 | , | 从左到右 |
可以对一个变量自增或自减,,但是不能对表达式自增或自减
单目运算符 赋值运算符 条件运算符 的运算顺序是从右向左 的
七、控制流
省略
八、函数
- 在定义函数中制定的形参,在没有出现函数调用时不占用内存中的存储单元。在函数调用时才分配内存
- 函数的形式参数,被当作该函数内的局部变量,如果与全局变量同名它们会优先使用
C 语言支持递归,即一个函数可以调用其自身。但在使用递归时,程序员需要注意定义一个从函数退出的条件,否则会进入死循环。
递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。
#include <stdio.h>
double factorial(unsigned int i)
{
if(i <= 1)
{
return 1;
}
return i * factorial(i - 1);
}
int main()
{
int i = 15;
printf("%d 的阶乘为 %f\n", i, factorial(i));
return 0;
}
(1)函数指针
int (*f)() -----------表示函数指针,初始化为f=fun,,fun表示fun()的首地址
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}
(2)回调函数
#include <stdlib.h>
#include <stdio.h>
//typedef unsigned int size_t
// 回调函数
void populate_array(int* array, size_t arraySize, int (*getNextValue)(void))
{
for (size_t i = 0; i < arraySize; i++)
array[i] = (*getNextValue)(); //右值也可以写成 getNextValue();
}
// 获取随机值
int getNextRandomValue(void)
{
return rand();
}
int main(void)
{
int myarray[10];
populate_array(myarray, 10, getNextRandomValue);
for (int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}
(3)命令行参数
见这篇文章
九、面向对象基础
无
十、文件操作
模式 | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
(1)打开文件
函数原型为:_CRTIMP FILE * __cdecl fopen(const char *, const char *);
第一参数为文件名,第二个参数为打开模式。
打开成功,fopen返回一个文件指针地址,否则返回一个NULL。如果没有指定文件路径,则默认为当前工作目录。如:
- FILE *fp;
- fp = fopen("c:\\temp\\test.txt", "r") //由于反斜杠\是控制字符,所以必须再加一个反斜杠
使用fopen()函数打开的文件会先将文件复制到缓冲区。注意:所下达的读取或写入动作,都是针对缓冲区进行存取而不是磁盘,只有当使用fclose()函数关闭文件时,缓冲区中的数据才会写入磁盘。
(2)关闭文件
函数原型为:_CRTIMP int __cdecl fclose(FILE *);
关闭成功返回值0,否则返回非零
FILE *fp;
fp = fopen("c:\\temp\\test.txt", "r");
if(fp == NULL)
printf("fail to open the file! \n");
else
{
printf("The file is open! \n");
fclose(fp); //记得关闭文件
}
(3)读写文件
(1)单个字符
函数原型为:
_CRTIMP int __cdecl fputc(<要写入的字符>, FILE *); //把字符逐一放到文件中(利用循环)
_CRTIMP int __cdecl fgetc(FILE *); //从文件中逐一得到字符(利用循环)
字符读取函数fgetc()可从文件数据流中一次读取一个字符,然后读取光标移动到下一个字符,可以利用循环将文件的内容读出。
如果字符读取成功,则返回所读取的字符,否则返回EOF(end of file)。EOF是表示数据结尾的常量,真值为-1。
另外,要判断文件是否读取完毕,可利用feof()进行检查。未完返回0,已完返回非零。
feof()函数原型为:_CRTIMP int __cdecl feof(FILE *);
利用feof()函数检查文件是否读取完毕
#include <stdio.h>
main()
{
FILE *fp;
fp = fopen("c:\\temp\\test.txt", "r");
if(fp != NULL)
{
while(!feof(fp))
printf("%c", fgetc(fp));
}
else
printf("fail to open! \n");
fclose(fp);
return 0;
}
(2)字符串
函数原型为:
_CRTIMP int __cdecl fputs(const char *, FILE *);
_CRTIMP char * __cdecl fgets(char *, int, FILE *);
fgets函数的作用是从指定文件读入一个字符串,如:fgets(str, n, fp);
参数n为要求得到的字符个数,但只从fp指向的文件输入n-1个字符,然后在最后加一个'\0'字符,因此得到的字符串共有n个字符,把它们放在字符数组str中。如果在读完n-1个字符之前遇到换行符或EOF,读入结束。
fputs函数的作用是向指定文件写入一个字符串,如:fputs("Hey", fp);
把字符串"Hey"输出到fp指向的文件。fputs函数的第一个参数可以是字符串常量、字符数组名或字符型指针。若输出成功,则返回0,否则返回EOF。
(3)格式化读写函数
函数原型为:
_CRTIMP int __cdecl fprintf(FILE *, const char *, ...);
_CRTIMP int __cdecl fscanf(FILE *, const char *, ...);
它们与printf和scanf函数相仿,都是格式化读写函数。不同的是:fprintf和fscanf函数的读写对象不是终端(标准输入输出),而是文件。printf函数是将内容写入到终端(屏幕),因此,fprintf就是将内容写入到文件了。
#include<stdio.h>
#define N 2
struct stu{
char name[10];
int num;
int age;
float score;
} boya[N], boyb[N], *pa, *pb;
int main(){
FILE *fp;
int i;
pa=boya;
pb=boyb;
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
//从键盘读入数据,保存到boya
printf("Input data:\n");
for(i=0; i<N; i++,pa++){
scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);
}
pa = boya;
//将boya中的数据写入到文件
for(i=0; i<N; i++,pa++){
fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);
}
//重置文件指针
rewind(fp); //很重要
//从文件中读取数据,保存到boyb
for(i=0; i<N; i++,pb++){
fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);
}
pb=boyb;
//将boyb中的数据输出到显示器
for(i=0; i<N; i++,pb++){
printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
}
fclose(fp);
return 0;
}
(4)文件指针
函数原型为:
_CRTIMP void __cdecl rewind(FILE *);;
rewind函数的作用是使位置指针重返回文件的开头,属于文件的定位
(5)fread和fwrite
(6)文件定位函数rewind和fseek
(7)EOF
十一、异常处理
C 语言提供了 perror() 和 strerror() 函数来显示与 errno 相关的文本消息。
- perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
- strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
另外有一点需要注意,您必须使用 stderr 文件流来输出所有的错误。
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern int errno ;
int main ()
{
FILE * pf;
int errnum;
pf = fopen ("unexist.txt", "rb");
if (pf == NULL)
{
errnum = errno;
fprintf(stderr, "错误号: %d\n", errno);
perror("通过 perror 输出错误");
fprintf(stderr, "打开文件错误: %s\n", strerror( errnum ));
}
else
{
fclose (pf);
}
return 0;
}
*****************
错误号: 2
通过 perror 输出错误: No such file or directory
打开文件错误: No such file or directory
十二、C预处理器
可以和这篇文章一块看
(1)预处理指令
指令 | 描述 |
---|---|
#define | 定义宏 |
#include | 包含一个源代码文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面代码 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个 #if……#else 条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
以下这些标准库中的宏可以在编程中直接使用:
宏 | 描述 |
---|---|
__DATE__ | 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。 |
__TIME__ | 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。 |
__FILE__ | 这会包含当前文件名,一个字符串常量。 |
__LINE__ | 这会包含当前行号,一个十进制常量。 |
__STDC__ | 当编译器以 ANSI 标准编译时,则定义为 1。 |
(2)预处理器运算符
(1)宏延续运算符
个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)
(2)字符串常量化运算符
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#),向字符串常量进行类型转换
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n") 字符串常量化运算符
int main(void)
{
message_for(Carole, Debra);
return 0;
}
(3)标号合并运算符
使用##可以连接两个标号,其实变量名就是一种标号:
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n) 把标号token和标号34连接起来成为token34
int main(void)
{
int token34 = 40;
tokenpaster(34);
return 0;
}
(4)defined()运算符
用来确定一个标识符是否已经使用 #define 定义过
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void)
{
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
十三、动态内存申请
序号 | 函数和描述 |
---|---|
1 | void *calloc(int num, int size); 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。 |
2 | void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 |
3 | void *malloc(int num); 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 |
4 | void *realloc(void *address, int newsize); 该函数重新分配内存,把内存扩展到 newsize。 |
void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。
#include<stdlib.h>
void* malloc(unsignedint size); //申请一个size大小字节的内存
void* calloc(unsignedint num,unsignedint size); //申请num个size字节的内存
若申请失败,函数返回NULL
void*表示返回的首地址的类型为指向空的指针,,用户需进行强转才行
p = (int *)malloc( n * sizeof(int) ); //把申请到的内存首地址指针强转为int*型
void *realloc( void *p, unsigned int size); 改变申请的内存的大小
void free(void* p); //释放内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char name[100];
char *description;
strcpy(name, "Zara Ali");
/* 动态分配内存 */
description = (char *)malloc( 200 * sizeof(char) );
if( description == NULL )
{
fprintf(stderr, "Error - unable to allocate required memory\n");
}
else
{
strcpy( description, "Zara ali a DPS student in class 10th");
}
printf("Name = %s\n", name );
printf("Description: %s\n", description );
}