Loading...

C语言基础(五)——文件和可变数组

C语言基础(五)——文件和可变数组
  iwehdio的博客园:https://www.cnblogs.com/iwehdio/

导图

1、文件

  • 格式化的输入输出:格式字符串。

    • 输出:printf 。%[flags][width][.prec][hlL]type
      • flags:-表示左对齐,+表示前边有正负号,0表示0填充,空格边数正数留空。默认为右对齐、空格填充。
      • width / prec:为数字n时表示最小字符数为n,为*时表示下一个参数是字符数,为.n时表示小数点后的位数是n,为.*时表示下一个参数是小数点后的位数。
        • 示例:printf("%*d", 6, 123),表示显示6位字符。
      • hIL:类型修饰。hh 表示单个字节,h 表示 short,l 表示 long,ll 表示 long long,L 表示 long double。
      • type:数据类型。i 或 d 表示 int,u 表示 unsigned int,o 表示八进制,x 表示 十六进制,f 表示 float,c 表示 char,s 表示字符串,p 表示指针, n 表示读入/写出的个数。
        • 示例:printf("%dty%n", 12345, &num),表示到 %n 时输出了几个字符,在这里是 7 。
      • printf 的返回值是输出的字符数。
    • 输入: scanf 。%[flag]type
      • flag:* 表示跳过,为数字表示最大字符数,hh 表示 char,h 表示 short,l 表示 long / double,ll 表示 long long,L 表示 long double。
        • 示例:scanf("%*d%d", &num),这样输入:123 456,num中存储的是456。
      • scanf 的返回值是读入的项目数。
    • 在要求严格的程序中,应该判断每次调用 scan f或 printf 的返回值,从而了解程序运行中是否存在问题。
  • 文件输入输出:

    • FILE* fopen(const char * restrict path, const char* restrict mode):打开文件。
      • path:文件路径。
      • mode:文件的打开方式。
        • r:打开只读。
        • r+:打开读写,从文件头开始。
        • w:打开只写。如果不存在则新建,如果存在则清空。
        • w+:打开读写。如果不存在则新建,如果存在则清空。
        • a:打开追加。如果不存在则新建,如果存在则从文件尾开始。
        • ..x:只新建,如果文件已存在则不能打开。如 wx 。
    • int fclose(FILE *stream):关闭文件。
    • fscanf(FILE*...):读入文件。
    • ``fprint(FILE* ..)`:写入文件。
  • 打开文件的标准代码示例:

    FILE* fp = fopen("file", 'r');
    if(fp){
        int num;
        fscanf(fp, "%d", &num);
        printf("%d", num);
        fclose(fp);
    }
    
  • 二进制文件:

    • 其实所有的文件最终都是二进制的。
    • 文件无非是用最简单的方式可以读写的文件。
    • 而二进制文件是需要专门的程序来读写的文件。
    • 文本文件的输入输出是格式化,可能经过转码。
    • 文本与二进制文件:
      • 文本的优势是方便人类读写,而且跨平台。
      • 文本的缺点是程序输入输出要经过格式化,开销大。
      • 二进制的缺点是人类读写困难,而且不跨平台。包括int的大小不一致,大小端的问题等。
      • 二进制的优点是程序读写快。
  • 二进制读写:

    • size_t fread(void* restrict ptr,size_t size,size_t nitems,FILE* restrict stream):读入数据。第一个参数是指向所要读的内存的指针,第二个是内存的大小(每个结构单元的大小),第三个是要读入几个这样大的内存,第四个是文件指针。
      • 因为二进制文件的读写一般都是通过对一个结构变量的操作来进行的,于是 nitem 就是用来说明这次读写几个结构变量。
    • size t fwrite(const void* restrict ptr,size_t size,size_t nitems,FILE*restrict stream):写出数据。参数列表与读入数据相同。
    • FILE指针是最后一个参数,返回的是成功读写的字节数。
  • 在文件中定位:

    • long ftel(FILE*stream):获取现在所在的位置。

    • int fseek(FILE*stream,long offset,int whence):定位到文件中的某个位置。第一个参数是文件指针。

      • offset:从 whence 定位到位置,再进行偏置。
  • whence:从哪里开始。SEEK-SET表示从头开始,SEEK-CUR表示从当前位置开始,SEEK END表示从尾开始(倒过来)。

2、位运算

  • 按位与:&

    • 如果 x 的第 i 位和 y 的第 i 位都是1,那么按位与结果的第 i 位也是 1,否则为0。
    • 按位与常用于两种应用:
      • 让某一位或某些位为0,例:x & 0xFE
      • 取一个数中的一段,例:x & 0xFF
  • 按位或:|

    • 如果 x 的第 i 位或 y 的第 i 位是1,那么按位与结果的第 i 位也是 1,否则为0。
    • 按或常用于两种应用:
      • 使得一位或几个位为1,例:x | 0x01
      • 把两个数拼起来,例:0x00FF | 0xFF00
  • 按位取反:~。把 1 位变成 0,把 0 位变成 1。

  • 按位异或:^

    • 如果 x 的第 i 位和 y 的第 i 位相等,那么按位异或结果的第 i 位也是 0,否则为 1。
    • 对一个变量用一个值异或两次,会得到原来的变量。
    • 两个相同变量的异或为 0。
  • 左移:i << j

    • i 中所有的位向左移动 j 个位置,而右边填入0。
    • 所有小于int的类型,移位以int的方式来做,结果是int。
    • 左移 j 位等价于等于 i *= 2^j
  • 右移:i >> j

    • i 中所有的位向右移动 j 个位置。
    • 所有小于int的类型,移位以int的方式来做,结果是int。
    • 对于unsigned的类型,左边填入0。
    • 对于signed的类型,左边填入(除符号位以外的)原来的最高位(保持符号不变)。
    • 右移 j 位等价于等于 i /= 2^j
  • 位段:把一个 int 的若干位组合成一个结构。

    • 定义:

      struct uu {
          unsigned int leading : 3;
          unsigned int flag1 : 1;
          unsigned int flag2 : 1;
          int trailing : 11;
      }
      
    • 可以直接用位段的成员名称来访问比移位、与、或方便。

    • 编译器会安排其中的位的排列,不具有可移植性。

    • 当所需的位超过一个int时会采用多个int。

3、可变数组

  • 实现:

    #include<stdlib.h>
    #include<stdio.h>
    //每次增大的容量
    #define BLOCK_SIZE 20
    //结构体
    typedef struct changearray
    {
        int *array;
        int size;
    } Array;
    //函数原型
    Array array_creat(int init_size);
    void array_free(Array* a);
    int array_size(const Array* a);
    int* array_at(Array* a, int index);
    void array_inflate(Array* a, int more_size);
    
    //主程序测试
    int main(int argc, char const* argv[]){
        Array a = array_creat(20);
        printf("%d\n", array_size(&a));
        *array_at(&a, 0) = 12;
        printf("%d\n", *array_at(&a, 0));
        int number = 0;
        int cnt = 0;
        while(number != -1){
            scanf("%d", &number);
            if(number != -1){
                *array_at(&a, cnt++) = number;
            }
        }
        printf("%d\n", cnt);
        printf("%d\n", a.size);
        int index = 0;
        while(index < cnt){
            printf("%d\t", *array_at(&a, index++));
        }
    
        return 0;
    }
    
    //创建,动态申请内存
    Array array_creat(int init_size){
        Array a;
        a.size = init_size;
        a.array = (int*)malloc(sizeof(int) * a.size);
        return a;
    }
    //释放内存
    void array_free(Array* a){
        free(a->array);
        a->array = NULL;
        a->size = 0;
    }
    //得到数组大小
    int array_size(const Array* a){
        return a->size;
    }
    //按索引值得到指向某个数组元素的指针
    int* array_at(Array* a, int index){
        if(index >= a->size){
            array_inflate(a, (index/BLOCK_SIZE+1)*BLOCK_SIZE-a->size);
        }
        return &(a->array[index]);
    }
    //数组扩容
    void array_inflate(Array* a, int more_size){
        int* p = (int*)malloc(sizeof(int)*more_size);
        int i;
        for(i=0; i<a->size; i++){
            p[i] = a->array[i];
        }
        free(a);
        a->array = p;
        a->size += more_size;
    }
    

参考:C语言程序设计(浙江大学):https://www.bilibili.com/video/av15267247
iwehdio的博客园:https://www.cnblogs.com/iwehdio
posted @ 2020-03-10 15:57  iwehdio  阅读(333)  评论(0编辑  收藏  举报