• C语言-基础


    0. 参考

    https://www.runoob.com/cprogramming/c-tutorial.html

    1. 关键字

    https://blog.csdn.net/L_fengzifei/article/details/127921678!!!

    1.1 auto

    动态类型
    所有局部变量 默认为auto类型
    只能在函数内使用,只能修饰局部变量

    块作用域、空链接

    int func()
    {
    	// 下面两种写法都表示的是局部变量
    	int mount;
    	auto int month;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    typedef

    https://blog.csdn.net/weixin_45743799/article/details/104532763
    https://blog.csdn.net/weixin_44948467/article/details/114452324

    typdef 与 #define

    #define是由预处理器来处理的,在预处理阶段,只是简单的字符序列替换
    typedef是由编译器完成的,在编译阶段,给原来的数据类型起别名,可以作为一种新的数据类型进行使用

    typedef int INGEGER;
    INTEGER a,b; //等价于 int a,b;
    
    typedef int* PTR_INT
    PTR_INT p1,p2
    
    • 1
    • 2
    • 3
    • 4
    • 5

    typedef与数组

    arr数组的类型是char [20]

    typedef char ARRAY[20]; // ARRAY 是char [20]的别名
    ARRAY arr; // 等价于char arr[20];
    
    • 1
    • 2

    typedef与结构体

    typedef strcut Stu{
        char name[10];
        int age;
    }STU; // STU 是struct stu的别名
    STU stu1,stu2; // 等价于struct stu stu1,stu2;   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    typdef struct Stu{
        char name[10];
        int age;
    }STU,*PSTU;
    
    // version2
    typdef struct Stu{
        char name[10];
        int age;
    }; 
    typedef struct Stu STU;
    typedef struct Stu* PSTU;
    
    STU stu1; // 等价于 struct Stu su1;
    PSTU pstu2; // 等价于 struct Stu *pstu2
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结构体数组

    #define MAXSIZE 10
    
    typedef struct
    {
        int data;
        int next; // 下一个元素的数组下表
    }SLinkList[MAXSIZE];
    
    
    int main()
    {
        SLinkList arr; // struct arr[MAXSIZE];
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    typedef与指针

    typedef int (*PTR_ARR)[4]; //二维数组 PTR_ARR 是int *[4]的别名
    PTR_ARR p1,p2; //等价于int (*p1)[4],(*p2)[4];
    
    • 1
    • 2

    typedef 函数指针

    typedef int (*PTR_TO_FUNC)(int, int);
    PTR_TO_FUNC pfunc;
    
    // 例子
    typedef int (*PTR_TO_FUNC)(int, int);
    int max(int a, int b){
        return a>b ? a : b;
    }
    PTR_TO_FUNC pfunc = max;
    printf("max: %d\n", (*pfunc)(10, 20));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.3 const

    整个作用域值不被改变,常量,(建议使用大写名进行)
    使用const关键字 必须进行初始化

    const 修饰局部变量,在栈区创建
    const 修饰全局变量,在常量区创建

    const 不能用于switch-case语句,实际上const 修饰之后变成的是只读变量,不是实际的常量

    不要将const修饰的数据 赋值给非const修饰的数据,不然会报错 !!! 不完全对 !!! – 见下面的const和非const
    可以将非const修饰的数据,赋值给const修饰的数据

    const int Var=10;
    
    // const int var;
    // var=20; // 不能进行这种初始化
    
    // var=20; // 不能改变
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    进阶

    int getNum()
    {
    	return 100;
    }
    
    int main()
    {
    	int n=10;
    	const int M1=getNum(); // 运行时初始化
    	const int M2=n; // 运行时初始化
    	const int M3=30; // 编译时初始化
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.3.0 const 与 非const

    https://blog.csdn.net/souching/article/details/6796705
    https://blog.csdn.net/zxc024000/article/details/78624188

    const某些情况下可以赋值给非const
    本质上是值拷贝(),与地址拷贝的区别

    int a=10;
    const int &b=a;
    int c=b; // 正确
    
    printf("%#p\n",&a); // 0x61fe14
    printf("%#p\n",&b); // 0x61fe14
    printf("%#p\n",&c); // 0x61fe110
    
    a=20;
    cout<<b<<endl; // 20
    cout<<c<<endl; // 10
    
    const Student & topval(const Student &s) const;
    Student s=s1.topval(s2); // 正确
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    1.3.1 const 与数组

    数组不允许被修改,数组中的元素都被当成常量

    const int a[10]; 
    
    • 1

    1.3.2 const 与 指针

    可以修饰指针变量本身,或指针指向的数据

    不要将const修饰的数据 赋值给非const修饰的数据,不然会报错!!! 不完全对 !!!)(要看是值传递还是地址传递)

    const 离(指针)变量名远,修饰的是指针指向的数据
    const 离(指针)变量名近,修饰的是指针变量本身

    const int *p; // 指针指向的值不能修改,指针可以指向其他数据
    int const *p; // 指针指向的值不能修改,指针可以指向其他数据
    int* const p; // 指针变量本身不能修改(不能指向其他数据)
    
    // 下面的方法 表示指针和指向的数据都是只读的
    const int* const p4
    int const * const p5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // 不同通过指针修改数组,但是可以通过数组名本身进行修改!!!
    int arr[2]={0};
    const int *p=arr;
    
    // p[0]=1; // ERROR 错误
    arr[0]=10; // 正确
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.3.3 const 与函数参数

    const 通常用在函数形参中,如果形参是一个指针,为了防止在函数内部修改指针指向的数据,可以用const来限制

    // 限制内部函数对字符串str进行修改
    size_t strnchr(const char* str,char ch){
        int i,n=0,len=strlen(str);
    
        for (i=0;i<len;i++){
            if (str[i]==ch){
                n++;
            }
        }
        return n;
    }
    
    int main(){
        char* str="http"; //hello
        char ch='t';
    
        int n=strnchr(str,ch);
    
        printf("%d\n",n);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注意:上面针对的是指针传递,如果是值传递,本来实参传形参的时候就是深拷贝操作,所以不需要也不要加const

    // 下面两种都要禁止
    // void func(const int a);
    // void func(const  a); // 用户定义的类型
    
    • 1
    • 2
    • 3

    1.4 static

    https://www.runoob.com/w3cnote/cpp-static-usage.html – 还没看

    静态变量
    无论是全局的的还是局部的,都存储在静态数据区
    静态数据区数据在程序启动时就会初始化,直到程序结束
    对于代码块中的静态局部变量,即使代码块执行结束,也不会销毁
    静态存储区的变量只能,且只需要初始化一次,由于不销毁,所以多次调用都是有效的

    int sum(int n)
    {
        static int reuslt=0;
        result+=n;
        return result;
    }
    
    int main()
    {
        for (int i=0;i<100;i++)
        {
            result=sum(i);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    static也可以修饰全局变量,当修饰全局变量的时候,会使变量的作用域限制在声明它的文件中。全局声明的一个static变量或方法可以被任何函数或方法调用,只是必须与被修改的变量或方法在同一个文件中

    static修饰静态函数,表示该函数只能在当前源文件中被调用

    1.4.1 static/const/多文件

    // s.h
    /*
    	如果下面的变量不加static 在file1.c和file2.c的引用过程中会出现重复定义
    	如果加上static 则两个变量分别变成了file1.c 和 file2.c中的(私有)变量,不会造成重复定义的问题
    */
    static const int a=1;
    static const char* names[2]={"li","wang"};
    
    // file1.c
    #include "s.h"
    
    // file2.c
    #include "s.h"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    1.5 extern

    extern dataType var 相当于引用声明,表明声明不是一个定义,告诉编译器该变量的真正定义在别的地方,可以当前文件,也可以是别的文件
    extern 关键字修饰的表达式,的变量,不会分配内存空间,他只是用来引用一个已经存在的外部定义
    不能利用extern进行变量定义extern int a=1; 这种是错误的

    用于提供一个全局变量的引用

    跨文件调用的时候,要加上extern,虽然有些情况下不写extern仍然可以执行(函数不用extern可以,变量不可以)(因为对于函数声明的extern 加不加都是等价的

    定义与声明

    // main.cpp
    int a=1; // 既是声明也是定义
    int b; // 既是声明也是定义
    extern int c; // 只是声明,c在其他文件中被定义 
    
    • 1
    • 2
    • 3
    • 4

    简单用法

    提供一个全局变量/函数的引用,全局变量对所有程序文件都是可见的
    使用extern无法初始化(定义)变量,会把变量名指向一个之前定义过的存储位置
    其实就是 A文件中定义了一个可以在B文件中使用的全局变量或函数,那么在B文件中就可以使用extern来得到A文件中定义的变量或函数的引用(用在在另一个文件中声明一个全局变量或函数)
    进阶:A/B文件可以存在相互的使用extern

    // A.c
    #include 
    int count;
    extern void write_extern();
    
    int main()
    {
    	count=5;
    	write_extern();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    // B.c
    #include 
    extern int count;
    void write_extern(void)
    {
    	printf("count is %d\n",count)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    简单用法2

    时刻注意先声明后定义
    可多次声明,但是不可重复定义

    // 在一个文件内
    // 例子1
    int a; // 之所以下面可以使用extern 是因为此时a相当于一个外部链接变量(外部链接的静态)
    	   // 当在函数内不写extern的时候,那么根据作用域进行判断使用局部变量a或全局变量a
    
    int main() // or some function
    {
    	extern int a; // 使用的是全局变量的a
    	// int a; // 重新定义了一个局部变量a ; 如果extern int a 和int a 同时存在于一个作用域块内,则ERROR重复定义
    }
    
    // 例子2
    int a=10; 
    
    int main() // or some function
    {
    	extern int a; // 使用的是全局变量的a
    	// int a; // 重新定义了一个局部变量a ; 如果extern int a 和int a 同时存在于一个作用域块内,则ERROR重复定义
    }
    
    // 例子3
    // 值得注意的是:下面的例子是正确的!!!
    int main()
    {
    	extern int a;
    }
    int a=10;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    进阶用法
    https://blog.csdn.net/m0_46606290/article/details/119973574
    https://blog.csdn.net/rammuschow/article/details/107993479

    /*test.h*/
    extern int a; // 引用声明,引用了一个已经存在真正定义的变量,该变量先通过.c文件#include 然后进行了定义!!!
    
    • 1
    • 2
    /*test.c*/
    #include "test.h"
    int a=10;
    
    • 1
    • 2
    • 3
    /*main.c*/
    #include 
    #include "test.h" // 会链接到test.c文件
    
    int main()
    {
    	printf("%d\n",a); // 10 使用的是test.c中的a 具有外部链接的变量
    	
    	int a=2000; // 这个是具有空链接的变量:块作用域、局部变量,这个不会造成重复定义
    	printf("%d\n",a); // 2000 就近原则,使用的是局部变量的a 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    进阶用法2

    /*test.h*/
    void func(); // 函数声明
    
    • 1
    • 2
    /*test.c*/
    #include 
    #include "test.h"
    
    extern int times; // 引用声明,可以引用主文件中的全局变量
    
    // 函数定义
    void func()
    {	
    	int i=0;
    	    int i=0;
        for ( i;i<times;i++)
        {
            printf("hello world\n");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    /*main.c*/
    #include 
    #include "test.h"
    
    // 定义全局变量
    int times=10; // test.c中extern 引用声明
    
    int main()
    {
    	loop(); // 调用的是test.h 中连接test.c的函数
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.5.1 extern / #include

    https://www.runoob.com/w3cnote/extern-head-h-different.html

    1.6 register – 还要看

    定义存储在寄存器中而不是RAM(内存)中的局部变量,意味着变量的最大只存等于寄存器的大小(一个字节),且不能也对它应用一元运算符???
    通常变量存储在内存中
    寄存器变量被存储在CPU的寄存器中,寄存器变量比普通变量具有更对的访问和读写速度
    无法获得寄存器变量的地址
    寄存器只用于需要快速访问的变量,如计数器等(定义了register,并不意味着变量将被存储在寄存器中,只是意味着变量可能存储在寄存器中,这取决于硬件和实现的限制
    一般不能定义double类型

    int func()
    {
    	register int miles;
    }
    
    void func(register int n);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.7 volatile – 没看懂???

    通常用于 硬件地址和与其他并行运行的程序共享的数据(例如:一个地址中可能保存着当前的时钟事件,不管程序做些什么,该地址的值都会随着时间而改变。另一种情况是一个地址被用来接收来自其他计算机的信息)
    语法同const

    防止编译器、寄存器过分优化
    一般用于嵌入式

    volatile int a;
    volatile int b;
    
    // 防止a和b,共享x???
    a=x;
    b=x; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    控制方法

    if-else

    注意,else总是与其前面位置最近if配对
    判断语句中,可以是数值、表达式、赋值表达式、变量等,只要是非0都可以

    if (condition)
    {
    	...
    }
    else if (condition)
    {
    	...
    }
    else ()
    {
    	...
    }
    
    //例子
    if (a=b) // 把b的值赋值a,如果b为非0,则执行
    {
    	//TODO
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    switch-case

    switch-expression 必须是一个常量表达式,一个整型(整型表达式)或一个枚举类型,(其实字符常量也可以,因为字符和整型可以相互转换),(也就是说不能包含任何变量,浮点型也不行
    expression1/2 必须是与expression类型相同的数据,且必须是常量或字面量(字符也行,可以与整型相互转换
    expression 总结

    switch后面接的expression 可以是变量表达式(可以包括运算公式),但是必须是整型变量表达式 --> (整型常量、整型变量、整型常量表达式、整型变量表达式、枚举类型)
    case 后面接的expression 必须是常量表达式表达式的结果必须是整型就可以,但不能是变量)(可以包括运算公式)(不能是变量),必须与switch后的expression具有相同的类型 (整型常量、整型常量表达式)
    两种表达式不能是浮点型但是可以是字符型
    const 后面接const 常量也不行,因为const int val 是只读变量 不是常量

    如果case不加break,控制流会继续后面的case的语句(无论满不满足下一个case的条件),直到遇到break
    当所有的case都不匹配的时候,有一个可选语句default(default不是必须的),default语句中的break不是必须的

    swtich(expression)
    {
    	case expression1:
    		...
    		break;
    	case expression2:
    		...
    		break;
    	default:
    		...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    例子

    {
        char c='b'; // c存储在常量区
        scanf("%c",&c);
        switch (c)
        {
        case 'a':
            cout<<'a'<<endl;
            break;
        
        default:
            cout<<"not a"<<endl;
            break;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    #include 
     
    int main(){
        enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } day; // day是整型常量
        scanf("%d", &day);
        switch(day){
            case Mon: puts("Monday"); break;
            case Tues: puts("Tuesday"); break;
            case Wed: puts("Wednesday"); break;
            case Thurs: puts("Thursday"); break;
            case Fri: puts("Friday"); break;
            case Sat: puts("Saturday"); break;
            case Sun: puts("Sunday"); break;
            default: puts("Error!");
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    while

    while(condition)
    {
    	...
    }
    
    • 1
    • 2
    • 3
    • 4

    注意!!!

    #include 
    using namespace std;
    
    int main()
    {
        int a=10;
        while (a--)
        {
            cout<<a<<endl; // 第一次a已经是9了!!!
        }
    
        // for (;a>=0;a--)
        // {
        //     cout<
        // }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    for

    int会首先被执行一次,且只会被执行一次;这一步可以声明并初始化任何循环控制变量,也可以不写任何语句,只有一个分号即可
    然后会判断condition,如果为真则执行循环主体,如果为假,则不执行循环主体,且控制流会跳转到紧接着for循环的下一条语句
    执行完for循环主体后,控制流会跳回increment,该语句可以留空,只要在条件后有一个分号出现即
    条件再次被判断,如果为真,则执行循环,条件变为假的时候,for循环终止

    for(int;condition;increment)
    {
    	...;
    }
    
    • 1
    • 2
    • 3
    • 4

    do-while

    函数体至少会被执行一次

    do
    {
    	...;
    }while(codition);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    文件操作

    C语言中硬件设备可以看做文件
    打开文件–保存到一个FILE类型的结构体变量中;关闭文件就是释放结构体变量

    文本和二进制

    都是二进制存储的,不过是采用特定的编码方式能够进行读取

    换行符

    linux : \n程序中的数据会原封不动的写入文本文件
    windows: \r\n作为文本文件的换行符

    如果以文本方式打开文件,当读取文件时,程序会将文件中所有\r\n转换成一个字符\n(如果文本文件中有连续的两个字符\r\n,则程序会丢弃前面的\r,只读\n
    当写入文件时,程序会将\n转换成\r\n写入

    位置指针/EOF

    文件位置指针,指向当前读写到的位置,也就是读写到第几个字节

    • rewind(fp)

    将文件中的位置指针重新定位到文件开头

    部分函数再读取出错时也会返回EOF,有些情况下无法判断文件读取结束和是读取出错,feof() ferror()

    • fseek

    移动文件指针到任意位置
    fseek一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错
    一般搭配fread\fwrite使用

    int fseek(FILE* fp,long offset,int origin)
    // offset 移动的字节数,offser 为正,向后移动;offset为负时,向前移动
    // origin 为起始位置,表示从何处开始计算偏移量
    // // 文件开头 0;当前位置 1;文件末尾 2 
    
    • 1
    • 2
    • 3
    • 4
    • feof

    判断文件内部指针是否指向了文件末尾 feof(FILE *fp)

    • ferror

    判断文件操作是否出错 ferror(FILE *fp)

    if (ferror(fp))
    {
    	puts("出错")
    }
    else
    {
    	puts("读取完毕")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    fopen/fclose

    #include 
    FILE *fp=fopen("filename","openmode");
    
    // 打开错误就返回一个空指针NULL
    FILE *fp;
    if (fp=fopen("filename","openmode")==NULL) "ERROR..."
    
    // 正常关闭的时候返回值为0,非0表示有错误发生
    fclose(fp); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    fgetc/fputc

    int fgetc(FILE *fp) // 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF(EOF在stdio.h中定义的宏,通常是一个负值)
    int fputc(FILE *fp)

    char ch;
    FILE *fp=fopen("filename","filemode");
    ch=fgetc(fp)
    
    • 1
    • 2
    • 3
    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    char ch='a';
    fputc(ch,fp);
    
    // 知道遇到回车结束
    while ((ch=getchar())!='\n')
    {
    	fputc(ch,fp);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    fgets/fputs

    读写一个字符串或一个数据块
    char* fgets*(char *str,int n,FILE *fp) str为字符数组,读取成功时返回字符数组首地址
    int fputs(char *str,FILE *fp) 向指定的文件写入字符串,写入成功返回非负数,失败返回EOF (注意:fputs不自动添加\0)

    读取到的字符串会在末尾自动添加\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 *fpl
    	char 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    fread/fwrite

    以数据块的形式读写文件,
    注意,使用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()检测
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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写入到fp
        
        rewind(fp);
        fread(b,size,N,fp); // 把fp读到b
        
    
        for (int i=0;i<N;i++)
        {
            printf("%d\t",b[i]);
        }
        printf("\n");
        fclose(fp);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    进阶-读写结构体

    #include 
    #include 
    #include 
    
    #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;
    
        strcpy(pa[0].name,"zhangsan");
        strcpy(pa[1].name,"lisi");
        for (int i=0;i<N;i++)
        {
            pa[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); // 写入fp
        rewind(fp);
        fread(boyb,sizeof(struct stu),N,fp); // 从fp读入结构
        // fread(&someCertainStructInstance,sizeof()...)
    
        for (int i=0;i<N;i++)
        {
            printf("%d\n",pa[i].num);
            printf("%d\n",pa[i].age);
            printf("%f\n",pa[i].score);
        }
        
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    fprintf/fscanf – 有问题

    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); // 写入文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    文件复制

    注意:除非对于文本格式,否则fopen一定要以二进制的形式打开

    开辟一个缓冲区,不断从源文件中读取内容到缓冲区,每读取完一次就将缓冲区中的内容写入到新建的文件,直到把原文件的内容读取完
    通常来讲,缓冲区的数据时没有结束标志的,如果缓冲区填充不满,–> 一般借助的就是fread 记录每次读取到的字节数

    size_t fread(void* ptr,size_t size,size_t count,FILE *fp)
    // 如果 size =1 ,则返回的就是读取的字节数,
    
    • 1
    • 2
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    ftell

    long int ftell(FILE* fp); //获得文件内部指针(位置指针)距离文件开头的字节数
    
    • 1
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    exit

    #include
    exit(1); // 异常退出,返回给操作系统
    exit(0); // 正常退出 
    
    • 1
    • 2
    • 3
  • 相关阅读:
    为什么鸿蒙调用弹窗组件(CommonDialog )却不展示或闪退?
    Python包:包的概念、2种建立包方式、包的使用(代码 + 图文)
    一篇读懂 Linux 用户管理
    10.Oracle的同义词与序列
    2023版 STM32实战10 内部Flash读写
    C#进程调用FFmpeg操作音视频
    devops
    为什么我的k8s的节点状态都是notready呢
    怎么设置代理IP进行网络爬取呢?代理访问网络如何设置?
    【使用教程】在Ubuntu下PMM60系列一体化伺服电机通过PDO跑循环同步位置模式详解
  • 原文地址:https://blog.csdn.net/L_fengzifei/article/details/127921624