• C语言进阶语法及部分注意事项



    8进制与16进制

    0x00 计算机原理

    01 缓冲区(buffer)与缓存(cache)

    buffer是内存的一部分
    cache是CPU,磁盘等的一部分
    简单来说就是buffer偏重于写,而cache偏重于读。

    02 一个程序运行时的内存空间

    栈区(stack) 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等
    堆区(heap) 一般由程序员分配释放(malloc), 若程序员不释放,程序结束时可能由OS回收
    全局区(静态区)(static) 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,
    未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放
    文字常量区 一般常量就是放在这里的。不能被修改。 程序结束后由系统释放
    程序代码区 存放函数体的二进制代码

    0x01 输入专题

    01 scanf

    强烈推荐这篇博客,一定要去看看:scanf函数读取缓冲区数据的问题

    1. scanf(" ");如果双引号里面的空格可以跳过广义上的空格,等价于循环+getchar

    2. 正则表达式

      参考博客

      • []内是匹配的字符。

      • ^表示求反集,当遇到非集合内的字符时立即终止输入。

      • %表示选择,%* 表示跳过,其后一定要有新的%语句,否则无法读入。

      • %,%*后面的是条件,比如%ss是一个条件,表示任意字符。%3s又多了一个条件:只拷贝3个字符。 %[a-z]的条件稍微严格一些,输入的东西不但是字符,还得是一个小写字母的字符。
        ,也就是跳过满足条件的字符,

      • 注:%[a-b 0-9]能够读入空格。单个字符也可以直接写在中括号里面

    3. fgets(c, n, fp)gets 不同,第一、fgets 需要加最大输入长度n这个参量, 表示加了 \0 之后 fgets 能读入的最大长度。因此需要在读入的最末尾主动赋值为 \0 ,不必管是否有 \n,这样能保证数据没有多余的 \n

    4. 正文文件fscanf 等函数大都和标准I/O下的函数用法一样。除了 fgets

    02 sscanf

    本质上与scanf相同,只不过将目标字符串当作stdin

    同理还有sprintf

    0x02 文件专题

    01 fopen

    1. fopen 必须要搭配文件指针使用,如 fp = fopen( , ) 而且得配合 fscanf 等等,但是在关闭文件之可以同时对文件和标准输入输出进行操作。

    2. 文件打开一定要判断是否成功。如果不成功,可以使用 perror(const char *s) 它可将 上一个函数(一般是各个库函数) 发生错误的原因输出到标准设备(stderr)。参数s所指的字符串会先打印出,后面再加上错误原因字符串。

    02 文本方式打开与二进制方式打开

    C语言采用文本方式和二进制方式打开文件的区别分析

    i. 区别

    数据怎么在磁盘上写不是由文件打开方式决定的,而是由写函数决定的。数据怎么从磁盘上读也不是由文件打开方式决定的,而是由读函数决定的。
    上面说的数据怎么写是指:一种类型的变量是怎么存的?比如int 12,可以直接存12的二进制码(4个字节),也可以存字符‘1’,字符'2'.
    数据怎么读的是指:我要读一个int变量,是直接读sizeof(int)个字节,还是一个字符一个字符的读,直到读到的字符不是数字字符。

    这对应了两种函数

    ii. 如何读写

    1. fread 以二进制的方式读入,fscanf 以正文方式读入。
      fread 可以直接按几个字节为单位读,而 fscanf 则是把文章解释为字符串然后再读。

    2. fread 返回值是成功读入的以size为单位长度的项数

    iii. 判断末尾

    首先说明一点,ASCII表有256个字符。

    EOF是等于-1,但是 实际上文档末尾是没有EOF这个玩意的。EOF是读入函数的返回值。
    在以文本方式打开的文件中,返回值为int,不会与ASCII(char类型)冲突。故EOF有效。
    而在以二进制打开的文件中,要想读字符,就必须得用 sizeof(char) ,这样不可避免地就会出现 EOF 与ASCII冲突。此时就得用feof来判断。

    feof :若为末尾返回非零,若还没到就返回0。 返回非零的触发条件和 fgets等函数返回-1的条件相同

    03 fseek 和 ftell

    i ftell

    原型如下

    long ftell(FILE *stream);
    

    它的返回值为long类型,只有一个参数为文件指针

    ii fseek

    原型如下

    int fseek (FILE * stream, long int offset, int origin );
    
    1. 它中间的那个参数是long类型,因此中间那个参数可以这样写:22LL表示这个整数是long类型。
      需要注意的是,offset移动的量永远是字节数。 由于二进制文件和文本文件存储格式的区别 ,需要自己手动计算偏移量
    2. 第三个参数只有三个值SEEK_SET表示文件开头,SEEK_CUR表示当前指针位置,SEEK_END表示文件末尾
    3. 当offset是向文件尾方向偏移的时候,无论偏移量是否超出文件尾,fseek都是返回0,当偏移量超出文件尾的时候,文件指针是指向文件尾的。并不会返回偏移出错-1值。当offset是向文件头方向偏移的时候,如果offset没有超出文件头,fseek返回值为0.当offset超出文件头时,fseek返回出错-1值,文件指针不变还是处于原来的地址。

    0x03 变量专题

    01 extern

    extern a显式的说明了a的存储空间是在程序的其他地方分配的,在文件中其他位置或者其他文件中寻找a这个变量。

    02 const

    用const修饰的变量通常也叫常变量,因为这个变量有地址,有空间。只不过它的读写方式设定为了只读,这也就意味这只能通过赋初值的方式给它值。

    const(运行时概念) define(编译时概念)
    原理 常量声明 字符替换
    谁来编译? 编译器 预编译器
    空间 需要分配空间
    C与C++中const变量内存分配问题详解))
    不需要分配空间
    安全检查 没有数据类型的区别,没有类型安全检查 有类型区别,需要在编译阶段进行类型检查
    有无生命周期 有(当前函数) 无(全局)

    (注:const在C语言中与在C++中的内存分配方式不同。此处讨论C语言的情况)

    const修饰的全局变量,在常量区分配内存空间,不能通过变量地址来修改值;
    const修饰的局部变量在栈区分配内存空间,可以通过变量地址来修改值

    ii 与指针搭配

    const的位置可以发生改变,但有可能含义就发生了变化

    例如: const int * pint const * p等价,表示 *p 不能改,
        而 int * const p 则表示 p 不能改。

    如何记?
    * 是右结合运算符,它若先和p结合,那const修饰的就是*p,否则const修饰的就是p

    03 typedef

    typedef基本用法
    在一个定义 typedef <字符串> 中, <字符串> 中会出现一个未定义的类型名 Type_A
    当你写:Type_A object; 的时候,它的含义就是:用 object去替换 <字符串> 中的 Type_A

    例如:

    typedef int int_array[4];
    int_array object;
    

    实际上是:用 object 替换 字符串 "int int_array[4];" 中的 int_array 得到的结果: int object[4];

    又例如:

    typedef void (*PU)(int a, char b);
    PU pa;
    

    实际上就是用 pa 替换 void (*PU)(int a, char b); 中的 PU

    这也就是编译器处理tyepdef定义的原理。
    typedef可以看作是定义了一个新的类型,这个类型在解释的时候按照以上规则定义变量。

    typedef与结构体搭配也可以用上面的理论解释:

    使用typedef关键字定义结构体类型 定义结构体类型的同时定义结构体类型变量
    typedef struct student
    {
     int age;
     int height;
    }std;
    // std相当于struct student
    struct student
    {
     int age;
     int height;
    }std1,std2;
    //定义了student数据类型的结构体和std1、
    std2结构体变量

    04 char有符号位

    在x86平台上,默认有符号,但在arm平台上默认没符号。
    保险起见,用 signed char

    05 enum(枚举常量)

    定义类似于结构体
    相当于一次性定义多个常量。
    用它定义变量没用

    06 可变数组长度

    老师可能讲的是在C语言中不能用变量作为数组的长度,但实际上, 哪怕是c89的标准,在满足一定条件时也是能够用变量作为数组长度的。

    例如:

    int len = 3;
    char a[len];
    

    在语法上是没有问题的。

    尽管如此,我本人还是不推荐使用这种写法。

    1. 这样写无法在定义的时候初始化。char a[len] = {0}就是错的。
    2. 这样写只能作为局部变量,如果定义为全局变量就有问题。

    0x04 指针专题

    01

    1. 字符串常量有返回值,其返回值为首字母的地址

    2. 数组名,是指向它的第一个一级成员的指针
      数组名取地址,是指向整个数组的指针

    02 表述 *(p + 1)p[1] 等价

    本质是因为 [] 是下标运算符。 x[y] 等价于 *((x) + (y))

    03 函数指针

    为方便起见,C语言中可以将函数指针直接按函数调用的方式使用。比如func(u,v)(*func)(u,v) 等价

    最常见的例子:qsort里面的cmp函数。cmp函数的名字是你自己定的,qsort在用你写的cmp的时候先给它一个别名。
    这个过程就是函数A的地址通过一个函数指针的形参传递到另一个函数B内部,从而能够在内部调用。只要函数A格式与B里面的形参一样,A名字可以任何。
    大大增加了程序的扩展性。

    04 二维数组与二重指针

    见此博客
    具体原因是两者在内存中的分配不完全一样,二重指针更加分散一些
    所以 char **pchar p[2][3] 之间不能相互传递参数

    0x05 结构体专题

    结构体大小(sizeof)

    sizeof计算结构体大小

    没有成员的结构体占用的空间是1个字节。

    偏移量

    struct stru 
    {  
    		int a;  //start address is 0
    		char b;  //start address is 4
    		int c;  //start address is 8
    };
    

    偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。

    比如上面的结构体,第一个成员a的偏移量为0。第二个成员b的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员c的偏移量是第二个成员的偏移量应该是加上第二个成员的大小(4+1)。

    但在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:
    (1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
    (2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。

    0x06 函数专题

    01 qsort

    qsort在调用外部判断函数的时候,传入的参数是 数组元素的地址! 。在自己写cmp函数的时候尤其要注意。
    另外,指针在C语言中 指向的是被指元素的首地址

    示意图b

    cmp返回值大于0则按找大于排序,小于0则按照小于排序。通常直接写return a - b;

    一维的数组排序
    int a[1000]
    qsort(a,1000,sizeof(int),comp);
    
    int comp(const void *a,const void *b)
    {
      return *(int *)a-*(int *)b;
    } 
    -----------------------------------
    
    字符数组排序
    char a[1000][20];
    qsort(a,1000,sizeof(char)*20,comp);
    int comp(const void *a,const void *b)
    {
       return strcmp((char *)a,(char *)b);
    }
    -----------------------------------
    
    结构体排序
    typedef struct str
    {
       char str1[11];
       char str2[11];
    }str;
    
    str *strs;strs=(str *)malloc(sizeof(str)*10);
    int compare(const void *a,const void *b)
    {
      return strcmp(((str*)a)->str2,((str*)b)->str2);
    }
    qsort(strs,10,sizeof(str),compare); 
    
    

    02 strlen()

    返回值为无符号整形unsigned int。故如果参与减法运算会产生负数,需要强制类型转换

    0x07 宏定义专题(待整理)

    宏定义详解

  • 相关阅读:
    python截取视频中的某一段,保存为avi结尾的视频
    文章内容过长,将此内容转为pdf的方式(使用node)
    typescript常见问题集锦
    利用matplotlib中imshow()函数绘图
    如何查看Linux系统安装时间
    php包含那点事情[WOOYUN]
    中间人攻击利用框架bettercap测试
    java 递归方法
    java方法重载
    JVM 之栈结构
  • 原文地址:https://www.cnblogs.com/mitnick/p/15571453.html
Copyright © 2020-2023  润新知