美文网首页
C语言入门

C语言入门

作者: hgzzz | 来源:发表于2019-12-02 21:33 被阅读0次

    gcc的简单使用

    gcc 源自GNU

    1. gcc -E hello.c -o hello.i
      • 预处理.c文件,处理文件包含,宏定义,条件编译
    2. gcc -S hello.i -o hello.s
      • 编译,将预处理过的文件编译成汇编文件。过程包括:词法分析,语法分析,语意分析,代码生成等。
    3. gcc -c hello.s - o hello.o
      • 汇编,生成目标文件,此时是二进制代码。目标文件没有链接动态库,还不是可执行文件。
      • 静态库生成(ar -rc libxxx.a xxx1.o xxx2.o xxx3.o),静态库以.a后缀结尾
      • 动态库的生成(gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so),动态库以.so后缀结尾
    4. 链接:目标文件必须链接过后才可以执行
    • 静态链接:gcc -o hello hello.o libxxx.a
    • 动态链接:gcc -o hello hello.o libxxx.so
    • lld.hello.exe:在linux中可以通过shell脚本 lld 查看可执行模块的dependency

    基本语法

    1. system函数:int system(const char * string);
      • 在库<stdlib.h>中,作用是开辟一个新的进程执行(调用fork()),来执行参数字符串所代表的命令。
      #include <stdio.h>
      #include <stdlib.h>
      // gcc 中有这个库
      #include <windows.h> 
      
      int main() 
      {
        printf("before\n");
        // 系统调用
        system("dir");
        // 调用外部程序
        system("hello.exe");
        printf("after\n");
        // 使用 windows提供的库函数,调用windows操作系统的功能
        WinExec("calc", SW_NORMAL);
        return 0;
      }
      
      • 宏定义,会在预编译时展开
      // 定义一个宏 main.c
      #define NUM 100
      int main(int argc, char const *argv[])
      {
        int a;
        // 预处理会展开宏
        a = NUM; 
        return 0;
      }
      
      // 使用 `gcc -E`  预编译后 main.i
      # 1 "main.c"
      # 1 "<built-in>"
      # 1 "<command-line>"
      # 1 "main.c"
      int main(int argc, char const *argv[])
      {
        int a;
        100 = 10;
        a = 100;
        return 0;
      }
      
    2. 进制表示
      • 0x:十六进制,0:八进制
      • 输出时,%d:有符号十进制,%u无符号十进制输出,%o:无符号八进制,%x:无符号十六进制,输出时,以格式化输出的类型来决定输出的类型,输出时都是以四字节输出。在以%u,%o,%x输出时,如果原数据最高位是1,则高位补1。
      char b = 0x81;
      printf("%x\n", b); // 输出ffffff81
      
      • 计算机在存储时,都是以二进制补码形式存储,且每种类型的第一位为补码,注意在以%d输出时的进制转换问题。
      char b = 0x81;
      printf("%x\n", b); //输出 -127
      
      // 1000 0001 char 类型是8位存储,第一位为符号位
      // 原码为 1111 1110 + 1 = 1111 1111 = -127
      
      • 如果以小容量类型存储大容量类型的数据,超出部分被舍弃。以大容量数存储小容量数据,如果最高位是1,则在前面全部补1补齐位数。
    3. 数据类型:每种数据类型都有一定的存储大小,如果超过了存储空间大小,多出的位数则会舍弃,所以在给一种类型赋值或计算时,应注意该数据类型的取值范围,如果越界,可能产生意料之外的结果。
      • 整型
        • char、unsigned char:占一个字节。'a'为字符常量,打印格式为%c"a"为字符串(字符数组),打印格式为%s,每个字符串结尾,编译器都会自动添加一个结束标志位 \0,两者不一样。-128 到 127 或 0 到 255。
        • short、unsigned short类型,存储整数,2字节
        • int、unsigned int类型,存储整数,4字节
        • long、unsigned long,4字节
      • 浮点型
        • float,4字节,精度是六位小数
        • double,8字节,精度15位小数
        • long double,16字节,精度19位小数
      • 数组
        • 声明
          int arr[2] = {1, 2}
        • 数组越界:编译时不会出错,但是当程序运行时,如果用到了存储越界数组项这块空间时,会报错。
        • 数组名指向数组的首地址。c语言里面没有获取数组长度的方法,sizeof(arr) / sizeof(arr[0])可以获取数组长度。
        • 多维数组:int arr[2][3] = { { 1, 2, 3 },{ 4, 5, 6 } }
        • 字符数组和字符串:字符串以\0 或 0结尾。字符串可以以%s格式输出,原因是字符串输出指针需要结束符0
        #include <stdio.h>
        int main(int argc, char const *argv[])
        {
          char ch[] = { 'h', 'e', 'l', 'l', 'o' }; // 字符数组,长度是5
          char str2 = "hello" // 这种方式初始化,默认加 \0 结束符,所以长度是6
          char str[] = { 'h', 'e', 'l', 'l', 'o' '\0'}; // 字符串
          char str1[] = { 'h', 'e', 'l', 'l', 'o' 0}; // 字符串
          return 0;
        }
        
    4. 流程控制s
      • for(continue,break,goto)
      #include <stdio.h>
      int main(int argc, char const *argv[])
      {
        goto next; // 跳转到同一个作用域的标记处
        printf("zzzz1\n");
        printf("zzzz2\n");
        next:
          printf("123\n"); // 执行这里
        return 0;
      }
      
      • while
      • do...while
    5. 库函数
      • 随机数
      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      
      int main(int argc, char const *argv[])
      {
        /*
        1. srand函数是随机数发生器的初始化函数。
        2. 原型:void srand(unsigned int seed),如果传入参数相同,则产生随机数也相同。
        3. srand和rand()配合使用产生伪随机数序列。
        */
        srand((unsigned int) time(NULL));
      
        int i = 0;
        int num;
        for(i = 0; i < 5; i++) 
        {
          // rand()函数返回一个随机数
          num = rand();
          printf("%d\n", num);
        }
        return 0;
      }
      
      • 字符串处理函数
        • 每个字符串常量都是一个地址,指向字符串的首元素地址。字符串存储在_data的文字常量区,相同的字符串拥有相同地址。
        • gets(char []):获取一个键盘输入的字符串,以回车结束,和 scanf() 不同的是它可以读取空格,这个函数不安全,已经弃用。
        • fgets(char *, size, *stream);:可以指定读取长度,换行符也会读取。
        • strlen(s1):返回字符串s1的长度。和sizeof()不同的是,strlen()不包括结束符\0的长度,而且遇到\0结束。
        • strcpy(dst, src):字符串拷贝,以首元素开始,遇到\0结束。
        • strncpy(dst, src, length):字符串拷贝,可以拷贝\0,但是不能拷贝\0后面的数据。
        • strcmp(s1, s2):如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。依次比较每个字符的ASCII码。
        • strncmp(s1, s2, n):可以指定比较长度n
        • strcat(s1, s2),字符串拼接。
        • sprintf(buf, "%d %d %d", a, b, c),将数据(a,b,c)以指定格式填充到buf中。
        #include <stdio.h>
        #include <string.h>
        
        int main(int argc, char const *argv[])
        {         
          int a, b , c;
          char buf[100];
          a = 1, b = 2, c = 3;
          sprintf(buf, "%d %d %d", a, b, c);
          printf("buf = %s\n", buf); // buf = 1 2 3
          return 0;
        }
        
        • sscanf(buf, "%d %d %d", a, b, c):将buf中的数据以指定格式提取到(a,b,c)中。注意格式要严格一致。
        #include <stdio.h>
        #include <string.h>
        
        int main(int argc, char const *argv[])
        {         
          int a, b , c;
          char buf[] = "a = 1, b = 2, c = 3";
          sscanf(buf, "a = %d, b = %d, c = %d", &a, &b, &c);
          printf("a = %d b = %d c =  %d\n", a,b,c); // a = 1 b = 2 c =  3
          return 0;
        }
        
        • strchr(s1, ch):返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
        • strstr(s1, s2):返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
        • char *strtok(char *str, const char *delim):该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针,该函数会破坏原字符串,在匹配的位置加上\0。第二次调用时,第一个参数写NULL =>char *strtok(NULL, const char *delim),切割的还是第一次调用被破坏的字符串。
    6. 函数
      • 库函数,使用头文件引入,然后使用。
      • 自定义函数。
      #include <stdio.h>
      // 无参 无返回值
      void hello()
      {
        printf("hello func\n");
      }
      // 含参无返回值
      void add(int a, int b){
        printf("%d\n", a + b);
      }
      // 含参有返回值
      int multiply(int a, int b){
        return a * b;
      }
      int main(int argc, char const *argv[])
      {
        hello();
        add(1, 2);
        printf("2 * 3 = %d\n", multiply(2, 3));
        return 0;
      }
      
      • 函数的声明和定义: 在main函数中调用其他函数时,只会往前去找函数的定义,如果没有定义就找函数的声明, 如果声明也没有,c编译器会报警告,c++会报错。所以在调用函数之前,一定要进行函数的声明,声明时形参名可以不写。
      #include <stdio.h>
      // 这里是函数的声明
      int func(int a, int b);
      int main(int argc, char const *argv[])
      {
        func(1, 2);
        return 0;
      }
      // 这里是函数的定义
      int func(int a, int b) {
        return a + b;
      }
      
      • 封装库函数。在.c文件中编写自定义函数,在.h文件中写函数声明,在用到这些函数时,先将.h文件包含进来,(用双引号引入),将这些c文件一起打包编译。
      • .h文件为了防止被多次包含,可以在第一行加入。
      // 1. 条件编译
      #ifndef MY_STRLEN
      #define MY_STRLEN
      extern int my_strlen(char arr[]);
      #endif 
      // 2. 或者在第一行加入
      #pragma once
      

    指针

    1. 指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。
      • 指针也是一种数据类型。
      • 指针指向谁,就把谁的地址赋值给指针
      • 直接操作指针变量本身没有意义,需要操作*p---指针所指向的内存。
      • 指针的大小是由编译器决定的。
      #include <stdio.h> 
      int main(int argc, char const *argv[])
      {
        int *p;
        int a = 10;
        p = &a;
        printf("%p %p\n", p, &a); // 0061FF28 0061FF28
        *p = 10;
        printf("%d %d\n", a, *p); // 10 10
        return 0;
      }
      
    2. 野指针:指针保存了一个非法地址。(只有定义变量后系统分配的地址才是合法的),操作野指针时不会操作,但是在操作野指针指向的内存时,由于系统在该进程中未对改地址授权,会报一个段错误。
    3. 空指针:给指针变量赋值为NULL
    int *p = NULL;
    // 相当于是
    int *p;
    p = NULL;
    
    1. 多级指针:指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
      int a = 10;
      int *p = &a;
      int **q = &p;
      *p = 20; 
      **q = 30;
      // a= 30 *p = 30 **q = 30 
      printf("a= %d *p = %d **q = %d \n ", a, *p, **q);
      return 0;
    }
    
    1. 指针操作方式
    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
      int *p;
      int a = 10;
      p = &a;
      // a = 10 *p = 10 *(p+0) = 10 p[0] = 10
      printf("a = %d *p = %d *(p+0) = %d p[0] = %d\n", a, *p, *(p+0), p[0]);
      return 0;
    }
    
    1. void类型指针
    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
      int a = 10;
      /* 
        1. 不能用 void 定义变量,因为无法确定变量要分配的内存大小
        2. 但是可以用 void 关键字定义指针,因为指针的大小是固定的,根据编译器来确定。
        3. 在使用 void 关键字定义的指针是,需要进行类型适配。 
            * 理由是地址指向的都是这个数据的首地址(int 4 字节的首地址),操作整个4
              字节的数据,但是 void 类型没有大小无法确定操作多大空间。
      */ 
      void *p;
      p = &a;
      //printf("*p = %d\n", *p); // error: invalid use of void expression
      printf("*p = %d\n", *(int *)p); // 把 void 类型的指针转换成 int 类型的指针 
      return 0;
    }
    
    1. 指针的步长,指针的加减法根据指针的步长来进行加减。指针的步长由指针的类型决定,比如int类型的指针步长是4。
    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
      int a = 10;
      int *p = &a;
      printf("p = %p P+1 = %p\n", p, p+1); // p = 0061FF28 P+1 = 0061FF2C
      return 0;
    }
    
    1. 指针与数组
      • 数组存储的是指针类型的数据,每个数据是一个指针。
        int *p[3];
      • 形参中的数组:形参中的数组不是数组,是一个指针。
      func(int a[100]){} // 形参大小是一个指针的大小
      func(int a[]){} // 形参大小是一个指针的大小
      func(int *a){} // 形参大小是一个指针的大小
      
      • 数组名是一个指针,指向数组第一个元素的地址,可以利用指针步长的特性来遍历数组。
    2. 指针作为函数返回值:注意,不能返回一个局部变量的地址,局部变量在函数调用完成时会自动销毁。如果再用一个指针去接收这个地址会产生一个野指针。
    #include <stdio.h>
    
    int a; // 全局变量 在 bss区(未初始化全局变量)
    
    int *func(){
      return &a;
    }
    
    int main(int argc, char const *argv[])
    {
     *func() = 100;
      printf("a = %d\n", a);
      return 0;
    }
    

    作用域

    1. 局部作用域
      • {}内定义的变量只能在该局部作用域内使用,在代码执行到这个变量的定义时,编译器才会为这个变量分配空间。
      • 在不同的作用域中可以定义同名的变量。
      #include <stdio.h>
      
      int main(int argc, char const *argv[])
      {
        int a = 1;
      
        if(1) {
          int a = 2;
          printf("%d\n", a); // 2
        }
        return 0;
      }
      
      • static关键字声明的静态局部变量在程序编译的时候就会分配地址空间(_data区),不能用局部变量初始化,静态局部变量在程序退出时销毁。static初始化的局部变量只会初始化一次,但是可以赋值多次。
      #include <stdio.h>
      void func(){
        static int a = 1;
        a++;
        printf("%d\n", a);
      }
      int main(int argc, char const *argv[])
      {
        func(); // 2
        func(); // 3
        func(); // 4
        return 0;
      }
      
    2. 全局作用域
      • 全局变量,在编译时分配地址空间,如果没有初始化,默认值为0,在程序(进程)结束时销毁,提前使用(定义前使用)需要先声明,可以声明多次。
      #include <stdio.h>
      void func(){
        extern int a; // 在这之前未定义,需要声明
        printf("func = %d\n", a);
      }
      
      int a = 10;
      
      void func1(){
        printf("func1 = %d\n", a);
      }
      
      void func2(){
        int a = 20;
        printf("func2 = %d\n", a);
      }
      
      int main(int argc, char const *argv[])
      {
        func();
        func1();
        func2();
        return 0;
      }
      
      • static全局变量(包括函数),和普通全局变量的区别就是作用域不同,只在本文件内可用。

    内存分配

    1. 在程序执行前,有几个分区的内存已经确定,可以在linux使用size命令查看。

      • text(代码区):一般只读,函数。
      • data:已初始化的数据,全局变量,static变量,文字常量区(只读)。
      • bss:未初始化数据,全局变量,static变量
        目标文件段大小
    2. 在程序运行时除了加载上述已确定的内存外,还加载包括堆区内存和栈区内存。

      • 栈内存(stack):存放普通局部变量,自动管理内存。


        查看栈空间
      • 堆内存(heap):手动申请空间,手动释放或程序结束系统释放,使用函数malloc(int size)申请堆内存。
      #include <stdio.h>
      #include <stdlib.h>
      
      int main(int argc, char const *argv[])
      {
        int *p;
        p = (int *)malloc(sizeof(int));
        if(NULL == p){
          return -1; // 空间分配失败
        }
        *p = 100;
        printf("%d\n", *p); // 100
        printf("%p\n", p); // 00FF2A48
        if(NULL != p){
          // 释放 p 所指向的内存, p 中存储的地址信息不变,这个地址不能再被使用。
          free(p); 
          p = NULL;
        }
        //*p = 20; // err
        return 0;
      }
      }
      

    结构体

    1. 定义和声明
    #include <stdio.h>
    #include <string.h>
    
    struct Student
    {
      char name[50];
      int age;
    };
    
    struct Teacher // 定义并声明
    {
      char name[50]; 
      int age;
    }t1, t2 = { "hgz",18 };
    
    struct // 定义匿名结构体并声明
    {
      char name[50];
      int age;
    }w1 = { "hgz",18 }, w2;
    
    
    int main(int argc, char const *argv[])
    {
      struct Student stu1 = { "hgz",18 };
    
      struct Student stu2;
      strcpy(stu2.name, "hgz");
      stu2.age = 18;
    
      struct Student stu3;
      struct Student *p;
      p = &stu3;
      strcpy(p->name, "hgz");
      p->age = 18;
    
      printf("%s\n", "ok");
      return 0;
    }
    
    1. 结构体数组
    struct Student stu[2] = {
      {"hgz", 18},
      {"zzz", 19}
    };
    
    1. 结构体直接作为参数传递是值传递,如果需要修改原结构体,需要用地址传递(参数为结构体指针)。
    #include <stdio.h>
    
    struct Student
    {
      int age;
    };
    
    void func(struct Student *p){
      p->age = 20;
    }
    
    int main(int argc, char const *argv[])
    {
      struct Student s1 = {18};
      func(&s1);
      printf("%d\n", s1.age);
      return 0;
    }
    
    1. 结构体中的指针。(要先给地址再使用,可以给文字常量区字符串地址,栈地址或者堆区地址)
    #include <stdio.h>
    #include <string.h>
    
    struct Student
    {
      char *name;
      int age;
    };
    
    
    int main(int argc, char const *argv[])
    {
      // 相当于 s1.name = "zzz"; 将data区文字常量的字符串的首地址赋值给指针
      struct Student s1 = {"zzz", 18}; 
      printf("s1 = %s %d\n", s1.name, s1.age);
    
      struct Student s2;
      // strcpy(s2.name, "hgz"); // err 野指针 s2.name 还没有地址
      char name[100];
      s2.name = name; // 分配栈区空间
      strcpy(s2.name, "hgz");
      s2.age = 20;
      printf("s2 = %s %d\n", s2.name, s2.age);
    
      struct Student s3;
      s3.name = (char *)malloc(strlen("hgzzz")); // 分配堆区空间
      strcpy(s3.name, "hgzzz");
      s3.age = 22;
      printf("s3 = %s %d\n", s3.name, s3.age);
      if(NULL != s3.name){ // 注意使用堆区地址时,使用完要释放
        free(s3.name);
        s3.name = NULL;
      }
      return 0;
    }
    

    共用体

    1. 共用体所有成员公用一块内存地址,内存大小为最大成员内存大小。所有成员都指向首地址。
    #include <stdio.h>
    
    union Test{
      unsigned int a;
      unsigned short b;
      unsigned char c;
    };
    
    int main(int argc, char const *argv[])
    {
      union Test t1;
    
      // &t1.a = 0061FF2C &t1.b = 0061FF2C &t1.c = 0061FF2C 共用体成员指向同一块地址头
      printf("&t1.a = %p &t1.b = %p &t1.c = %p\n", &t1.a, &t1.b, &t1.c);
    
      t1.a = 0x44332211;
      // t1.a = 44332211 t1.b = 2211 t1.c = 11 共用体公用一块内存
      printf("t1.a = %x t1.b = %x t1.c = %x\n", t1.a, t1.b, t1.c);
    
      t1.c = 0xaa;
      // t1.a = 443322aa t1.b = 22aa t1.c = aa 修改共用体中的成员可能会影响其他成员
      printf("t1.a = %x t1.b = %x t1.c = %x\n", t1.a, t1.b, t1.c);
    
      return 0;
    }
    

    枚举类型

    1. 定义
    #include <stdio.h>
    
    enum{ // 不初始化赋值默认为 0 开始递增
      pink, red, yellow
    };
    
    int main(int argc, char const *argv[])
    {
      printf("%d\n", pink == 0 ); // 1
      printf("%d\n", red == 1 ); // 1
      printf("%d\n", yellow == 2 ); // 1
      return 0;
    }
    
    1. typedef关键字:给一个已存在的类型取一个别名。
    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
      typedef int int32; // 当前使用的是32位gcc编译器
    
      typedef struct Student
      {
        int32 age; // typedef替换发生在编译阶段 宏定义是在预编译阶段
      }Student;
    
      Student s1 = {18};
      printf("%d\n", s1.age);
    
      return 0;
    }
    

    文件操作

    1. FILE 结构体:我们在进行文件操作时要用到这个结构体,在我们调用fopen(const char * filename, const char * mode)时,会返回一个指向堆区的指针(这个指针指向结构体的地址,和文件没有直接关系)。这个函数会初始化结构体成员,这些成员和文件有着联系,当我们在进行文件操作时,实际上是这些成员在操作。
    typedef struct{
      short level; // 缓存区‘满’或者‘空’的程度
      unsigned flags; // 文件状态标志
      char fd; // 文件描述
      unsigned char hold; // 如无缓冲区不读取字符
      short bsize; // 缓冲区大小
      unsigned char *buffer; // 数据缓冲区位置
      unsigned ar; // 指针,在文件中的指向位置
      unsigned istemp; // 临时文件指示器
      short token; // 用于有效性检查
    }FILE;
    
    1. 使用方式
    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
      // 第二个参数为 w 表示以写操作打开文件,如果文件不存在,创建文件,
      // 如果存在,删除内容并打开
      FILE *fp = NULL;
      fp = fopen("b.txt", "w");
    
      if(NULL == fp){
        perror("fopen");
        return -1;
      }
    
      // fputs(int a, FILE *stream)
      char a = 'a';
      while(a <= 'z'){
        fputc(a, fp);
        a++;
      }
    
      // 关闭文件
      fclose(fp);
      fp = NULL;
    
      return 0;
    }
    
    1. 标准文件输出
    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
      printf("%s\n", "zzzz"); // 标准输出(屏幕输出) zzzz
    
      fclose(stdout); // 关闭标准输出文件指针
      printf("%s\n", "hgz"); // 无输出
      perror("abc"); // 打印函数调用失败的原因 abc : Bad file descriptor 
      return 0;
    }
    
    1. 按块大小读写文件fread() fwrite()
    #include <stdio.h>
    
    typedef struct Student
    {
      char name[50];
      int age;
    }Student;
    
    int main(int argc, char const *argv[])
    {
      Student stus[4] = {
        { "hgz1", 18 },
        { "hgz2", 19 },
        { "hgz3", 20 },
        { "hgz4", 21 }
      };
      FILE *fp = fopen("6.txt", "w");
      if(NULL == fp){
        perror("fopen");
        return -1;
      }
      int res = fwrite(stus, sizeof(stus), 1, fp);
      printf("%d\n", res);
    
      fclose(fp);
      fp = NULL;
      return 0;
    }
    
    #include <stdio.h>
    
    typedef struct Student
    {
      char name[50];
      int age;
    }Student;
    
    int main(int argc, char const *argv[])
    {
      FILE *fp = fopen("6.txt", "r");
    
      Student stus[10];
    
      int res = fread(stus, sizeof(Student), 4, fp);
    
      printf("res = %d\n", res);
    
      for(int i = 0; i < 4; i++){
        printf("%s %d\n", stus[i].name, stus[i].age);
      }
    
      fclose(fp);
    
      return 0;
    }
    

    fread() fwrite()实现文件拷贝命令

    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
      // 模拟linux cp 命令 -> cp a.txt b.txt 将a.txt 内容复制到b.txt中
    
      if(argc != 3){
        printf("%d\n", argc);
        printf("argument count error:a.exe src dst\n");
        return 0;
      }
      // 如果第二个参数是 r 在windows处理二进制文件会出问题
      FILE *srcfp = fopen(argv[1], "rb"); // 源文件
      FILE *dstfp = fopen(argv[2], "wb"); // 拷贝目的文件
    
      char buf[4*1024];
      int len; // 每次读取的长度
      while(1){
        len = fread(buf, 1, sizeof(buf), srcfp);
        printf("len = %d\n", len);
        if(len == 0){
          break;
        }
        fwrite(buf, 1, len, dstfp);
    
      };
    
      fclose(dstfp);
      fclose(srcfp);
    
      return 0;
    }
    
    1. 文件读写缓存区,在读写文件时,先把数据读写到缓冲区中。当缓存区满、调用(fclose()、fflush())等刷新缓存区函数、程序关闭时才从缓冲区读写文件。

    相关文章

      网友评论

          本文标题:C语言入门

          本文链接:https://www.haomeiwen.com/subject/irpgictx.html