https://www.runoob.com/cprogramming/c-tutorial.html
对于不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为NULL(所有字节的值都是0),其他所有变量的初始值是未定义的
需要建立存储空间的变量:变量在声明的时候就已经建立了存储空间
无需建立存储空间:通过extern
关键字声明变量名,而不定义他,这种可以在别的文件中定义
变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储
声明与定义???
extern int i
是声明,不是定义
int a
是声明,也是定义
左值:指向内存位置的表达式, 叫做左值表达式
右值:内存中某些地址的数值
字面量:常量 – 不允许进行修改
整型常量、浮点常量、字符常量、字符字面值、枚举常量、定义常量
定义常量
#define
预处理器const
关键字
// #define
#define Variable value // 注意没有分号 可以定义字符型、字符串型的常量// const
const type variable=value; // 注意有分号,且必须直接赋值
main函数也是函数,main函数中定义的变量也是局部变量
- 在函数或块内部的局部变量
在某个函数或块内部声明的变量称为局部变量,只能被函数或该代码块内部的语句使用
- 在所有函数外部的全局变量
全局变量在整个声明周期内都是有效的,在任意的函数内部都能访问全局变量
- 形参
被当做函数内的局部变量
命名冲突
局部作用域内,优先使用局部变量
全局变量:保存在内存的全局存储区中,占用静态的存储单元
局部变量:保存在栈中,只有在所在函数被调用时才动态的为变量分配存储单元
初始化局部变量和全局变量
局部变量被定义时,系统不会对其初始化(或随机初始化,根据编译器的不同策略不同),必须手动对其初始化
全局变量被定义时,系统会自动对其初始化,默认是0,(因为全局变量存储在内存分区中的全局数据区,这个区域中的数据在程序载入内存后会初始化为0)
在函数内部修改全局变量会影响其他函数,
int 0
;char
-\0
;float 0
;double 0
;pointer NULL
未初始化的变量会导致一些在内存位置中已经可用的垃圾值
a=1
b=2
c=4
def f():print(c)c=a+b
f()
print(c)
# c为局部变量,会报错!!!
数据以二进制的形式保存在内存中,字节是最小的可操作单位
在内存管理中,为每个字节分配了一个编号,使用该字节时,只要知道编号就可以,这个编号,就是地址
获取地址
&data
,(data可以是数值、字符)
存储类:定义c程序中变量/函数的范围(可见性)和声明周期
说明符,放在所修饰类型的类型之前
- auto
- register
- static
- extern
所有局部变量的存储类,只修饰局部变量,只用在函数内
int func()
{// 下面两种写法都表示的是局部变量int mount;auto int month;
}
定义存储在寄存器中而不是RAM中的局部变量,意味着变量的最大只存等于寄存器的大小(一个字节),且不能也对它应用一元运算符???
寄存器只用于需要快速访问的变量,如计数器等(定义了register,并不意味着变量将被存储在寄存器中,只是意味着变量可能存储在寄存器中,这取决于硬件和实现的限制)
int func()
{register int miles;
}
https://www.runoob.com/w3cnote/cpp-static-usage.html – 还没看
指示 编译器 在 程序 的生命周期内 保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁,因此使用
static
修饰局部变量可以在函数调用之间保持局部变量的值
static
也可以修饰全局变量,当修饰全局变量的时候,会是变量的作用域限制在声明它的文件中。全局声明的一个static变量或方法可以被任何函数或方法调用,只是必须与被修改的变量或方法在同一个文件中
static var
表示var的作用域为当前的文件,其他文件无效
int func()
{// num 是局部变量,但是只被初始化一次,每次调用函数func的时候,num不会被重置static int num=10num--;
}
提供一个全局变量的引用,全局变量对所有程序文件都是可见的
使用extern
无法初始化变量,会把变量名指向一个之前定义过的存储位置
其实就是 A文件中定义了一个可以在B文件中使用的全局变量或函数,那么在B文件中就可以使用extern来得到A文件中定义的变量或函数的引用(用在在另一个文件中声明一个全局变量或函数)
进阶:A/B文件可以存在相互的使用extern
// A.c
#include
int count;
extern void write_extern();int main()
{count=5;write_extern();
}
// B.c
#include
extern int count;
void write_extern(void)
{printf("count is %d\n",count)
}
只能被赋值一定的离散整数值变量
类型说明符,表明没有可用的值
指针类型、数组类型、结构类型、共用体类型和函数类型
其中:函数类型指的是函数返回值的类型
char
1个字节,[-128,127] 或[0,255]
unsigned char
1个字节,[0,255]
int
2或4字节,(win 4字节)
unsigned int
2或4字节
short
/unsigned short
2字节
long
/unsigned long
4字节
double
8字节
前缀:表示进制关系
后缀:表示有符号(默认)和无符号整型U、长整型L,大小写任意,顺序任意
有符号和无符号
当以有符号数的形式输出时,printf会读取数字所占用的内存,并把最高位作为符号位,把剩下的内存作为数值位,但是对于有符号的正数而言,最高位恰好是0,所以可以按照无符号数的形式读取,且不会有任何的数值影响
当以无符号的形式输出时,printf也会读取数字所占用的内存,并把所有的内存都作为数值位对待
0b 0B # 二进制数
0x 0X # 十六进制数
0 #八进制
注意标准编译器不支持0b的二进制写法,只是某些编译器在扩展后支持
注意printf 输出形式
整型只控制字节长度,和数值范围
进制数才是实际数值的表现形式
一个数字不管以何种进制来表示,都可以 以任意进制的形式输出,因为数字在内存中始终以二进制的形式存储
其他进制的数字在存储前都必须转换成二进制形式,而在输出前也需要反转
原码:数值的二进制数,包括符号位
反码:正数的反码=原码
负数的犯法=符号位不动,其他位置取反补码:
正数的补码=原码
负数的补码:是反码加1
-128的补码就是1000 0000
补码 1000 0000 就表示-128 记录就可以了
深究里边,是因为符号位会被数值的高位进行多次覆盖
计算机内存中,整型一律采用补码的形式存储,因此读取整数时还要采取逆向的转换,也就是将补码转换为原码
https://blog.csdn.net/u012782268/article/details/40021887
float
4字节,32位
double
8字节,64位
对于小数默认是double类型
long double
16字节
float x=1.2 # 赋值的时候,会先从double类型转换为float类型# 后缀
float x=1.2f # 赋值的时候直接是默认的float类型
注意所有的浮点型都是以双精度double进行运算的,即使是float类型,也要先转换成double类型
定义了宏,可以在程序中使用这些值和其他有关实数二进制表示是的细节
#include FLT_MIN # 最大值
FLT_MAX # 最小值
FLT_DIG # 精度值
单引号
char
,unsigned char
是一个字符类型,用来存放字符,但是同时也是一个整数类型,也可以用来存放整数!!!,注意取值范围
char
1个字节,[-128,127]
unsigned char
1个字节,[0,255]
char
类型只能存储ASCII字符,不能存储其他字符,根据上面的定义,也可以存放范围以内的进制数
由于
char
类型只能存储ASCII字符,字符集及其编码其实本质上就是二进制数,(本质上与整型没有区别)
定义一个char
类型字符,会先转换成ASCII字符对应的编码再存储
而存储一个整型的时候,不需要任何转换直接存储
由于数据类型不同,所以需要的字节位数不同,因此在存储和读取的时候占用的内存不一样,所以在读取 字符型和整型 的时候并不会有冲突
字符和整型可以互相赋值
char a=10;
int b='a';
字符型与进制数
由于char可以存放整型,所以可以声明和定义取值范围内的进制数
下面的例子中,就可以把char
想象成整型(但是要注意取值范围)
char a=0x32 // 这相当于将整型赋值给一个字符类型
printf("%c",a) // 2
printf("%d",a) // 50 这是因为由十六进制转换为的十进制 // 与python类别
// '2'.encode().hex() # 32
// int('32',16) # 50
// chr(50) # '2'
对于
char
类型的字符,(由于是窄字符,且只占一个字节,所以可以使用ASCII编码),(又由于ASCII编码的结果就是一个二进制数),可以利用转义的进制数进行表示
下面的例子,就可以把其当成字节对象(字节串对象),想当于python中的bytes对象,
char a='\x31' // a是字符数值1
char b='\x61' // b是字符achar *str1="\x31\x32\x33\x61\x62\x63" // 字符串"123abc"// 类别python
// '123abc'.encode().hex() # '313233616263'
putchar
输出字符(只能输出单个字符)
getchar
接收字符
char a=getchar();
printf("\"string\'") // "string'
对于中文字符应该使用宽字符的形式,并加上前缀,加上前缀后,所有的字符都变成了宽字符的存储形式
wchar_t
类型,在不同的编译器下对不同的语言使用不同的长度
#include
wchar_t a=L'中'
多字节字符,也叫做窄字符,变长字节存储方式
宽字符是固定字节存储方式
char
类型的窄字符,使用ASCII编码
char
类型的窄字符串,通常来讲是UTF8编码,(其实不同的编译器在不同的平台上编码方式不同)
wchar_t
类型的宽字符或宽字符串,使用UTF16或32编码
见文档
#include
wchar_t a=L'a'; // 要加一个L进行标注// 下面两个用于输出宽字符
putwchar(a);
wprintf(a);
双引号
在内存中占一块连续的内存
其实字符串数据,依靠的是数组和指针来简介的存储字符串
char s1[]="string1";
char *s2="string2"
char s1[]="string"
这种方法定义的字符串所在的内存既有读取权限又有写入权限(可变),可以用于输入与输出函数
[]
可以指明字符串的长度,如果不致命则根据字符串自动推算
声明字符串的时候,如果没有初始化,(由于无法自动推算长度,只能手动指定)char s[10]
char *s1="string"
这种方法定义的字符串所在的内存只有读取权限,没有写入权限(不可变),只能用于输出函数
puts
输出字符串
gets
接收字符串,与scanf
的区别见下
char s[10];
gets(s)
获得当前平台上的对象或类型的存储字节大小
printf("%lu",sizeof(int))
printf("%d",sizeof(instance))
数据类型存在强制转换
原则浮点数赋给整型:浮点数小数部分会被舍去(注意不是四舍五入)
整数赋值给浮点数,数值不变,但是会被存储到相应的浮点型变量中
强制转换
注意,强制转换是临时性的,并不会影响数值本身的数据类型和值
(类型说明符)(表达式)
(int)(x+y)
int a=10;
char b='a';
printf("%c\n",b+a); // 'k'
printf("%d\n",b+a); // 107
下面的例子中 由于都是
int
类型,所以即使是算出了float
值,也会变成int
类型,只不过在printf
变成了对应的格式
因此必须在先前就想好结果类型
int a=1;
int b=2;
float c;
c=a/b;
printf("%f",c); // 0.000000
%
取余运算,只能作用于整型,不能作用于浮点型注意:正负数,只根据
%
左边的数值符号决定!!!
++
自增--
自减num++; // 先运算后+1
num--; // 先运算后+1//例子
v=num++; // v=num;num+=1
num++; // 先运算后+1
++num; // 先+1后运算
逻辑运算符的结果 是 0 1
并不是bool类型
&&
– and
||
– or
!
– not
注:与python并不一样,python在逻辑运算上,对数值和bool类型的计算结果不一样
&
|
^
异或~
取反<<
左移>>
右移如果条件为真?则值为x:否则值为y
优先级:算术运算符>关系运算符>?:
结合性:从右到左
关系运算符的结果 是 0 1
并不是bool类型
注意,连续赋值应保证等号左边的变量、右边为赋值,并且变量已经了存储空间
定义时,连续赋值违反规定
// 下面是错误的!!!
// 赋值具有右结合性,这里1赋值给c,c赋值给b,b再赋值给a
// 但是这里相当于 b,c没有定义就赋值了,所以是错误的!!!
int a=b=c=1;// 下面是正确的
int a,b,c;
a=b=c=1;
注意结合性
同级别具有左结合性
具有右结合性的运算:单目运算符、赋值运算符(单目赋值和双目赋值)、三目运算符
! 大于 算数运算符 大于 关系运算符 大于 逻辑运算符中的&& 和 || 大于 赋值运算符
函数返回为空
函数参数为空,不接受任何参数,int main(void)
指针指向void:类型为void*
的指针 代表对象的地址,而不是类型
https://www.runoob.com/cprogramming/c-function-printf.html – 没看完
http://c.biancheng.net/view/1793.html?from=pdf_website_1_0 – 没看
输出格式
%[flag][width][.percision]type
width 控制输出宽度,数值不足宽度,空格补齐;数值或字符超过宽度,width不再起作用,按照数据本身宽度来输出;(例如%12f
默认保留6位小数,要输出12个字符宽度);width可以用于数值、字符、字符串
.precision 控制输出精度,小数位数大于precision,按照四舍五入输出;当小数位数不足precision后面补0
.precsion 与width不同,作用于整型的时候,不足宽度在左侧补0(作用于浮点数只控制小数位数);对于控制字符串输出,字符串长度大于.precision则被截断
总结,width要么补齐空格,要么不起作用;.precision对整型左侧补0或不起作用,对于浮点小数位补0或四舍五入,对于字符串被截断或不起作用
%d //十进制有符号整数
%u //十进制无符号整数
%f //float浮点数,默认保留六位小数,不足六位以0补齐,超过六位按四舍五入截断
%lf //double
%c //字符
%s //字符串
%p //指针的值
%e //指数形式的值
%x //十六进制 无符号%lu //32位无符号整数
...// printf的高级写法,用于区分不同的进制数字
print("a=%#hx",0x1d)
整型、短整型、长整型,分别有对应的printf输出形式
printf
中不能输出二进制数
需要某些转换函数
一个数字不管以何种进制来表示,都可以 以任意进制的形式输出,因为数字在内存中始终以二进制的形式存储
其他进制的数字在存储前都必须转换成二进制形式,而在输出前也需要反转
注意事项
有符号和无符号
当以有符号数的形式输出时,printf会读取数字所占用的内存,并把最高位作为符号位,把剩下的内存作为数值位,但是对于有符号的正数而言,最高位恰好是0,所以可以按照无符号数的形式读取,且不会有任何的数值影响
当以无符号的形式输出时,printf也会读取数字所占用的内存,并把所有的内存都作为数值位对待
因为通过格式控制符进行数值输出的时候,其实并不会检查定义的数值是有符号还是无符号数,只会按照格式控制符对数值进行特定的解释
"\\"
,%%
通常来说,printf执行结束后数据并没有直接输出到显示器上,而是放入了缓冲区,**注意:**不同的操作系统,对于printf和缓存机制是不同的
字符串类型,有两种定义方式,对应在内存中有两种 读取与写入权限
在输出时,要求字符串只需要有读取权限
puts
自动换行,printf
不是自动换行
从控制台读取数据
根据下面的例子,scanf会根据地址把读取到的数据写入内存
注意:多输出时的结果,除了空格外,必须严格输入
注意:超额输入的时候,不会出错,相当于位置参数(这是因为缓冲区的存在)
从键盘输入的数据并没有直接交给scanf,而是放入了缓冲区中,直到回车,scanf才到缓冲区中读取数据
输入可以不是指定的类型,但是会存在数值转换的风险
int a;
scanf("输入图像",&a)// 多输入
int a,b;
scanf("%d sb %d",&a,&b) // 输入的时候,必须是'1 sb 2',不然会出错// 超额输入
int a,b;
scanf("%d %d",&a,&b) // 输入的时候,必须是'1 2 3',不会出错,只会使用之前的内容// 连续多输入
int a,b;
int c,d;
scanf("%d %d",&a,&b);
printf("%d sb, %d\n",a,b);
scanf("%d %d",&c,&d);
printf("%d sb, %d",c,d);
// 输入的时候,可以是 1 2 3 4 ,也能正确输出 <-- 这是因为缓存区的存在
继续读取或读取失败
int main(){int a,b;int c,d;scanf("%d %d",&a,&b);printf("%d sb, %d\n",a,b);scanf("%d %d",&c,&d);printf("%d sb, %d",c,d);return 0;
}
// 输入 1 2 回车
// prinf()... 然后等待输入
// 输入 3 4 回车
// printf()... 结束//输入 1 2 a10 (第三个是一个不符合要求的数据)
// 直接结束,上下的 c,d 显示的系统默认的初始值int a=1;
int b=2;
scanf("%d %d",&a,&b);
printf("%d sb, %d\n",a,b);
// 输入 1 a10
// 不会出错,b输出的是初始值(和上面的例子一样)
字符串类型,有两种定义方式,对应在内存中有两种 读取与写入权限
在读取时,要求字符串有写入权限,因此只能用char s[]="string"
这种形式
scanf
在读取字符串的时候,在输入时,以空格当做输入结束标志
char s[30];scanf("%s",s) // 这里不需要取地址符&
gets
与scanf
同样具有缓冲区,区别在于gets
以回车作为字符串结束响应,而不是空格。scanf
是以空格作为结束响应,所以不能读取包含空格的字符串
位运算 是根据 内存中的二进制位进行运算的,而不是数据的二进制形式,
按位与运算,通常用来对某些位清0,或保留某些位
按位或运算可以用来将某些位置1,或保留某些位
按位异或可以用来将某些二进制位反转
单目运算符,具有右结合性
数据低位
1234中34就是低位
大端
数据的低位放在内存的高地址上
小端
数据的低位放在内存的低地址上
把各个二进制位全部左移若干位,高位丢弃,低位补0(小端模式下)(这里的高位和低位说的是数据高位和低位)
把各个二进制位全部右移若干位,低位丢弃,高位补0或1小端模式下)(这里的高位和低位说的是数据高位和低位)
如果数据的最高位是0,就补0,;高位是1,就补1
注意,
else
总是与其前面位置最近的if
配对
判断语句中,可以是数值、表达式、赋值表达式、变量等,只要是非0都可以
if (condition)
{...
}
else if (condition)
{...
}
else ()
{...
}//例子
if (a=b) // 把b的值赋值a,如果b为非0,则执行
{//TODO
}
switch-expression 必须是一个常量表达式,一个整型(整型表达式)或一个枚举类型,(其实字符常量也可以,因为字符和整型可以相互转换),(也就是说不能包含任何变量,浮点型也不行)
expression1/2 必须是与expression类型相同的数据,且必须是常量或字面量(字符也行,可以与整型相互转换)
expression 总结switch后面接的expression 可以是变量表达式(可以包括运算公式),但是必须是整型变量表达式
case 后面接的expression 必须是常量表达式(表达式的结果必须是整型就可以,但不能是变量)(可以包括运算公式)(不能是变量),必须与switch后的expression具有相同的类型
两种表达式不能是浮点型,但是可以是字符型
如果case不加break,控制流会继续后面的case,直到遇到break
当所有的case都不匹配的时候,有一个可选语句default(default不是必须的),default语句中的break不是必须的
swtich(expression)
{case expression1:...break;case expression2:...break;default:...
}
while(condition)
{...
}
int
会首先被执行一次,且只会被执行一次;这一步可以声明并初始化任何循环控制变量,也可以不写任何语句,只有一个分号即可
然后会判断condition,如果为真则执行循环主体,如果为假,则不执行循环主体,且控制流会跳转到紧接着for循环的下一条语句
执行完for循环主体后,控制流会跳回increment,该语句可以留空,只要在条件后有一个分号出现即可
条件再次被判断,如果为真,则执行循环,条件变为假的时候,for循环终止
for(int;condition;increment)
{...;
}
函数体至少会被执行一次
do
{...;
}while(codition);
C语言中硬件设备可以看做文件
打开文件–保存到一个FILE类型的结构体变量中;关闭文件就是释放结构体变量
都是二进制存储的,不过是采用特定的编码方式能够进行读取
linux :
\n
程序中的数据会原封不动的写入文本文件
windows:\r\n
作为文本文件的换行符如果以文本方式打开文件,当读取文件时,程序会将文件中所有
\r\n
转换成一个字符\n
(如果文本文件中有连续的两个字符\r\n
,则程序会丢弃前面的\r
,只读\n
)
当写入文件时,程序会将\n
转换成\r\n
写入
文件位置指针,指向当前读写到的位置,也就是读写到第几个字节
rewind(fp)
将文件中的位置指针重新定位到文件开头
部分函数再读取出错时也会返回EOF,有些情况下无法判断文件读取结束和是读取出错,feof() ferror()
fseek
移动文件指针到任意位置
fseek
一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错
一般搭配fread\fwrite使用
int fseek(FILE* fp,long offset,int origin)
// offset 移动的字节数,offser 为正,向后移动;offset为负时,向前移动
// origin 为起始位置,表示从何处开始计算偏移量
// // 文件开头 0;当前位置 1;文件末尾 2
feof
判断文件内部指针是否指向了文件末尾
feof(FILE *fp)
ferror
判断文件操作是否出错
ferror(FILE *fp)
if (ferror(fp))
{puts("出错")
}
else
{puts("读取完毕")
}
fopen
/fclose
#include
FILE *fp=fopen("filename","openmode");// 打开错误就返回一个空指针NULL
FILE *fp;
if (fp=fopen("filename","openmode")==NULL) "ERROR..."// 正常关闭的时候返回值为0,非0表示有错误发生
fclose(fp);
int fgetc(FILE *fp)
// 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF(EOF在stdio.h中定义的宏,通常是一个负值)
int fputc(FILE *fp)
char ch;
FILE *fp=fopen("filename","filemode");
ch=fgetc(fp)
FILE *fp=NULL;
char ch;if ((fp=fopen("F:\\c-learning\\file.txt","rt"))==NULL)
{puts("failed to open file");exit(0);
}
while ((ch=fgetc(fp))!=EOF)
{putchar(ch);
}
putchar('\n');if (ferror(fp))
{puts("出错")
}
else
{puts("读取完毕")
}fclose(fp);
char ch='a';
fputc(ch,fp);// 知道遇到回车结束
while ((ch=getchar())!='\n')
{fputc(ch,fp);
}
读写一个字符串或一个数据块
char* fgets*(char *str,int n,FILE *fp)
str为字符数组,读取成功时返回字符数组首地址
int fputs(char *str,FILE *fp)
向指定的文件写入字符串,写入成功返回非负数,失败返回EOF
读取到的字符串会在末尾自动添加\0,n个字符也包括\0。实际值读取到了n-1个字符
在读取n-1个字符之前如果出现了 换行 或者读到了文件末尾,则读取结束。即不管n的值多大,fgets()最多只能读取一行数据,不能跨行
C语言中没有按行读取文件的函数,只能将n设置的足够大,每次读取到一行数据
fgets() 遇到换行符时 会将换行符一起存入到当前字符串 !!!
// 读取100个字符
#define N 101
char str[N];
FILE *fp=fopen("filename","filemode");
fgets(str,N,fp);//例子
int main()
{FILE *fplchar str[N+1];if ((fp=fopen("filename","openmode"))==NULL){puts("failed to open file");exit(0);}while (fgets(str,N,fp)!=NULL){printf("%s\n",str);}fclose(fp);return 0;
}
if ((fp=fopen("F:\\c-learning\\file.txt","at+"))==NULL)
{puts("failed to open file");exit(0);
}
char *str2="hello\nqt\n";
fputs(str2,fp);fclose(fp);
以数据块的形式读写文件,
注意,使用fread/fwrite时应该以 二进制 的形式打开
其实就是多个字节数据:一个字节、一个字符串、多行数据等
// 读写成功,返回读写成功的块数count
size_t fread(void* ptr,size_t size,size_t count,FILE *fp) // 读取文件
size_t fwrite(void* ptr,size_t,size,size_t couont FILE *fp) // 写入文件
// ptr 内存区块的指针,可以是数组、变量、结构体等,用于存放读取到的数据
// fwrite 的ptr 用于存放写入的数据
// size 表示 *每个数据块* 的字节数
// count 表示要读写的 *数据块的块数*
// fp 表示文件指针
// 每次读写size*count个字节的数据// 读写成功,返回读写成功的块数count,如果返回值小于count
// fwrite 发生了写入错误,可以用ferror()函数检测
// fread 可能读到了文件末尾或发生错误,用ferror() 或feof()检测
int main()
{int N=5;int a[5]={1,2,3};int b[5];int i,size=sizeof(int);FILE *fp;if ((fp=fopen("F:\\c-learning\\file2.txt","rb+"))==NULL){puts("file to open");exit(0);}// size 每个块的字节数; N 为块数fwrite(a,size,N,fp); // 把a写入到fprewind(fp);fread(b,size,N,fp); // 把fp读到bfor (int i=0;iprintf("%d\t",b[i]);}printf("\n");fclose(fp);return 0;
}
进阶-读写结构体
#include
#include
#include #define N 2struct 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;strcpy(pa[0].name,"zhangsan");strcpy(pa[1].name,"lisi");for (int i=0;ipa[i].num=10;pa[i].age=20;pa[i].score=60.0;}if ((fp=fopen("./file.txt","wb+"))==NULL){puts("fail to open file\n");exit(0);}fwrite(boya,sizeof(struct stu),N,fp); // 写入fprewind(fp);fread(boyb,sizeof(struct stu),N,fp); // 从fp读入结构// fread(&someCertainStructInstance,sizeof()...)for (int i=0;iprintf("%d\n",pa[i].num);printf("%d\n",pa[i].age);printf("%f\n",pa[i].score);}fclose(fp);return 0;
}
scanf\printf
把字符串输出到标准输入输出设备上(显示器)
fscanf
把格式化字符串输出到执行文件
``fprintf` 把文件的内容输出到指定的数据
int fscanf(FILE *fp,char *format); // 返回参数列表中被成功赋值的参数个数
int fprintf(FILE *fp,char *format); // 返回成功写入的字符的个数,失败则返回负数FILE *fp;
int i,j;
char *str,ch;
fscanf(fp,"%d,%s",&i,str); // 读取文件
fprintf(fp,"%d %c",j,ch); // 写入文件
注意:除非对于文本格式,否则
fopen
一定要以二进制的形式打开
开辟一个缓冲区,不断从源文件中读取内容到缓冲区,每读取完一次就将缓冲区中的内容写入到新建的文件,直到把原文件的内容读取完
通常来讲,缓冲区的数据时没有结束标志的,如果缓冲区填充不满,–> 一般借助的就是fread 记录每次读取到的字节数
size_t fread(void* ptr,size_t size,size_t count,FILE *fp)
// 如果 size =1 ,则返回的就是读取的字节数,
int copyFile(char *fileRead,char *fileWrite);
int main()
{char fileRead[100]; // 要复制的文件名char fileWrite[100]; // 复制后的文件名strcpy(fileRead,"file1.wmv");strcpy(fileWrite,"file2.mp4");if (copyFile(fileRead,fileWrite)){printf("copy done ...");}else{printf("copy failed...");}return 0;
}int copyFile(char *fileRead,char *fileWrite)
{FILE *fpRead;FILE *fpWrite;// 设置缓冲区大小int bufferLen=1024*4;// 开辟缓冲区char *buff=(char*)malloc(bufferLen);// 实际读取的字节数int readCount;if ((fpRead=fopen(fileRead,"rb"))==NULL || (fpWrite=fopen(fileWrite,"wb"))==NULL){printf("connot open file");exit(1);}// 不断从fileRead读取内容,放在缓冲区,再将缓冲区的内容写入filewrite// readcount 表示实际是多少个字节while ((readCount=fread(buff,1,bufferLen,fpRead))>0){// 下面两种写法都可以 !!!fwrite(buff,readCount,1,fpWrite); // fwrite(buff,1,readCount,fpWrite);}free(buff); //手动释放动态内存fclose(fpRead);fclose(fpWrite);return 1;
}
long int ftell(FILE* fp); //获得文件内部指针(位置指针)距离文件开头的字节数
long fsize(FILE *fp);int main()
{long size=0;FILE *fp=NULL;char filename[30]="file1.wmv";if ((fp=fopen(filename,"rb"))==NULL){printf("failed open...");exit(1);}printf("%ld\n",fsize(fp));return 0;
}long fsize(FILE *fp)
{long n;// 当前位置// fpos_t 保存文件的内部指针fpos_t fpos; // typedef long long fpos_t// 获取当前位置// fgetpos 获得文件内部指针,fsetpos 设置文件内部指针fgetpos(fp,&fpos); // 设置到文件末尾fseek(fp,0,SEEK_END);// 获得文件内部指针(位置指针)距离文件开头的字节数n=ftell(fp);// 恢复之前的位置fsetpos(fp,&fpos);return n;
}
#include
exit(1); // 异常退出,返回给操作系统
exit(0); // 正常退出