C语言基础快查

作者: 薛定喵的鹅 | 来源:发表于2021-03-18 22:17 被阅读0次

    C令牌

    C程序又各种令牌组成,令牌可以是关键字、标识符、常量、字符串值,符号。

    • 分号: 语句结束符
    • 注释: //单行注释/* 局部或多行*/ 多行或者局部注释
    • 标识符:字母或下划线 _ 开头,后跟零个或多个字母、下划线和数字(0-9),区分大小写,不能是关键字
    • 关键字:C语言保留,不能作常/变量或其他标识符,比如:int、char、if 、else、while 、for 、case 、switch 等

    C数据类型

    类型决定内存占用空间以及布局

    类型 说明
    基本类型(算数类型) 浮点和整型
    枚举类型 (算数类型) 可以看做整型
    void类型 表示空
    派生类型 指针、数组、结构体、共用体、函数

    整型

    类型 占用内存 说明
    char 1字节 字符型(可能有符号或无符号)
    unsigned char 1字节 无符号字符型
    signed char 1字节 有符号字符型
    int 2或4字节(根据平台) 有符号整型
    unsigned int 2或4字节(根据平台) 无符号整型
    short 2字节 有符号短整型
    unsigned short 2字节 无符号短整型
    long 4或8字节(根据平台) 有符号长整型
    unsigned long 4或8字节(根据平台) 无符号长整型

    浮点型

    类型 占用内存 说明
    float 4字节 单精度浮点值,精度6位小数
    double 8字节 双精度浮点值,精度15位小数
    long double 16字节 19 位小数

    void 类型

    返回值位空、参数为空、void*表示任意类型的指针
    sizeof:可使用sizeof获取数据类型的占用字节的大小,这个是静态的,编译时就确定了。


    强制类型转换

    (type) expression

    算数类型隐式转换

    表达式存在不同类型的数值,编译器会把它们会被转换为表达式中出现的最高层次的类型。
    比如整型:


    算数类型隐式转换

    变量

    变量定义:可操作存储区域的名称。

    内存布局

    类型 占用内存
    无符号整型 均为原码*
    有符号整型 1个符号位,其他为补码*
    float 1位符号,8位指数,23位小数
    double 1位符号,11位指数,52位小数

    补充:

    • 原码:符号位加上真值的绝对值,即用第一个二进制位表示符号(正数该位为0,负数该位为1),其余位表示值。
    • 反码:正数的反码与其原码相同;负数的反码是对其原码逐位取反。
    • 补码: 正数的补码就是其本身;负数的补码是在其反码的基础上+1。

    变量定义:创建变量的存储类型和名称

    type variable_list;
    比如 int a, b, c;

    type variable1 = value, variable2 = value;
    =表示初始化,只定义不声明直接使用变量,变量的值是未定义的。

    变量声明

    告诉编译器有这个变量,并且指明变量的类型和名称。在编译时检查,是否有声明,链接时检查是否定义。
    extern type variable; 声明变量
    type variable; 定义就是声明一部分。

    局部变量被定义时,系统不会对其初始化,必须开发者对其初始化后再使用。定义全局变量时,系统会自动对其初始化,

    类型 初始值
    int 0
    char '\0'
    float 0
    double 0
    pointer NULL

    常量(字面量)

    常量定义:固定量,定义初始化后不能修改。

    整型常量: 1212、393U、298u、0xffe等。
    前缀:表示进制 ,0八进制,0x十六进制, 不带十进制 (不区分大小写)
    后缀: 表示类型, U无符号,L有符号 (不区分大小写)

    浮点常量:2.5、2.5f、231e-5L
    字符常量:'a'、'%'、 '\n'
    字符串常量:"hello"

    常量定义:

    const type variable = value;
    比如 const int a = 110;

    #define variable value
    比如 #define NAME "XiaoMing"


    存储类

    放置在类型前,表明变量或函数的声明周期和范围。

    auto:局部变量,只能修饰局部变量,默认不带

    {
       int integer;
       auto int auto_integer;
    }
    

    register:定义存储在寄存器中的变量,最大占用的位置是寄存器大小, 不能对它应用一元的 '&' 运算符(因为它没有内存位置)。

    {
       register int  miles;
    }
    

    static:静态变量或者全局变量,程序运行期间不销毁,定义的变量或者常量或者函数只能在同一个源码文件中访问

    static int value =10; 
    

    extern:声明一个全局变量的引用,对所有文件可见。


    运算符

    算术运算符:+、-、*、/、%、++、--

    • a/b :b不能是0
    • a%b: 必须都是整数
    • a++,a--:先运算,再返回运算后的值
    • ++a, --a: 先返回值,再运算

    关系运算符:返回的是布尔值真或假

    • ==、!=、>、< 、>=、 <=

    逻辑运算:返回的是布尔值真或假

    • &&、||、!

    位运算符: & 、|、^、~、<<、>>

    • &:按位与,只有都是1,才1,让更多的地方变成0。
    • |:按位或,只有都是0,才0,让更多的地方变为1。
    • ^:按位异或,一样的位置是0,不一样的是1。
    • ~:按位取反,1变成0,0变成1
    • <<:向左移动,右边补0
    • >>:向右移动,(正数左边补0,负数左边补1)

    如果整数乘除运算是2的幂,可以使用移位来实现

    赋值运算符: = 、+=、<<=等

    • +=: 先运算在赋值

    其他运算符

    • sizeof:返回变量占用内存的大小
    • &a:取变量的地址值
    • *p:表示地址p指向变量的值,解引用
    • ?: :条件表达式

    运算符优先级

    后缀 > 一元 > 乘除 > 加减 > 移位 > 相等判断 > & > ^ > | > && > || > ?: > 赋值 > ,
    比如: ++a++ 先a++ ,在运算++a
    比如: !a&&b||a !>&&>||


    数组

    连续存储的一系列相同类型的变量。

    数组定义

    类型 数组名[大小];
    int array[10];

    数组初始化

    int array[5] = {1, 2, 3, 4, 5};
    int array[] = {1, 2, 3, 4, 5}; 数组大小可省略,编译器自动推导。
    int array[5] = {1}; 部分初始化,array[0] ==1, 其他为0。

    数组访问:

    下标随机访问:array[3] = 0;


    多维数组

    数组的数组。

    多维数组定义:

    类型 数组名[大小][大小]···;
    int array[5][5][5];

    二维数组

    类型 数组名[x][y];
    int array[5][5]; x 表示行, y表示列。

    int a[3][4] 表示如下:

    ~ ~ ~ ~
    a[0][0] a[0][1] a[0][2] a[0][3]
    a[1][0] a[1][1] a[1][2] a[1][3]
    a[2][0] a[2][1] a[2][2] a[2][3]

    初始化

    下面的初始化是等价的

    int array[3][4] = {{1,2,3,4}, {2,2,3,4}, {3,2,3,4}, {4,2,3,4}};
    
    int array[3][4] = {1,2,3,4,2,2,3,4,3,2,3,4,4,2,3,4};
    

    二维数组下标访问:

    array[2][3] = 5;
    

    指针

    指针变量

    这个变量的值是另一个变量的地址;

    *类型 指针名 = &变量名;

    • 指向变量的数据类型必须和指针的数据类型一致。
    • 指针什么都不指向,指针变量赋值为NULL , int *ptr = NULL;
    • 指针解引用 *ptrptr->value,表示指向变量的值,

    指针的算数运算: ++、--、+、-

    • +:指针+1,表示指向下一个同等数据类型的变量的位置,比如 int *ptr = &value, ptr+1 表示下一个int位置,向后偏移4个位置。
    • -:指针-1,向前偏移一个位置。
    • ptr1-ptr2:表示两个指针偏移位置。

    指针的比较运算: ==、< 、 >

    指针可以比较大小,相等

    数组与指针

    数组名既是数组的指针又是第一个数组元素的指针。
    数组名是一个返回第一个数组元素的常量指针。

    int array[39] = {0};
    int *p = array;
    

    那么
    p == array == &array[0]
    p + 20 == &array[20]
    *(p+n) == array[n]

    指针数组

    int *ptr[256]; 是个数组,里面的元素是指向int变量的指针。

    指针的指针:

    int **ptr_int_ptr = &ptr; ptr_int_ptr 指向一个int型的指针。

    函数指针

    函数的函数名表示函数的地址,是一个常量,不能修改。
    函数指针是一个变量,可以把函数地址赋值给函数指针,指向一个函数。

    typedef int (*fun_ptr) (int, int); 
    
    int add(int left, int right){
        return left + right;
    }
    
    fun_ptr add_func = add;
    if (add_func) add_func(1, 2);
    

    字符串

    '\0'(null) 结尾的字符数组。

    char string[5] = {'1', ‘9', '0', '5', '\0'};
    char string[] = "1905"; "1905" 表示字符串字面量
    char *string = "1905";

    常用标准库函数

    函数声明 功能
    strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
    strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。
    strlen(s1); 返回字符串 s1 的长度。
    strcmp(s1, s2);                    
    如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
    strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
    strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。

    条件语句

    if (条件判断){语句;}
    if (条件判断){语句;} else{ 语句;}
    if (条件判断){语句;} else if{ 语句;} else if ··· else{语句}
    switch(变量){case 常量: 语句;··· default: 语句}: case语句不加break会穿透。


    循环语句

    while(条件判断){语句;} 条件真,执行语句
    for(变量定义赋值;条件判断;每次循环完执行的语句){ 语句;}
    do{语句}while(条件) 先执行一次,再判断

    • break:跳出循环,
    • continue:跳出本次循环

    枚举

    可以为每个变量指定值,第一个值不指定值默认为0,后面的值是前面的值+1,可以指定不同值

    enum STATE{
        PLAY = 1,
        PAUSE,
        STOP
    };
    enum STATE state;
    
    enum STATE{
        PLAY = 1,
        PAUSE,
        STOP
    }state;
    state = PLAY;
    
    typedef enum _STATE{
        PLAY = 1,
        PAUSE,
        STOP
    }STATE;
    STATE state = PLAY;
    
    typedef enum{
        PLAY = 1,
        PAUSE,
        STOP
    }STATE;
    STATE state = PLAY;
    

    结构体

    结构体用来存储不同类型的数据项。

    结构体声明

    struct tag {
    member-list
    member-list
    member-list
    ...
    } variable-list ;
    tag、member-list、variable-list 至少出现2个。

    struct Player
    {
        int state;
        char *file_name;
    };
    

    定义声明结构体变量需要带上 struct : struct Player player;

    typedef struct _Player
    {
        int state;
        char *file_name;
    }Player;
    

    使用typedef定义类型,Player前面不需要使用struct:Player player;

    struct NODE
    {
        char string[100];
        struct NODE *next_node;  
    };
    

    可以使用指向自己类型的指针

    结构体初始化

    Player player = {1, "a.mp4"};
    

    可以指定结构体成员名初始化:

    Player player = {.state=1, .file_name="a.mp4"};
    

    直接使用memset

    Player player;
    memset(&player, 0, sizeof(Player));
    

    访问结构成员

    Player player;
    Player *pPlayer = &player;
    player.state;
    pPlayer->state = 1;
    (*pPlayer).state = 1;
    

    *pPlayer.state 是不正确的,因为 运算符.*的优先级要高。


    位域

    位域的语句格式如下:
    struct 位域结构名
    {
    类型说明符 位域名: 位域长度;
    类型说明符 位域名: 位域长度;
    类型说明符 空空空 : 位域长度;
    ...
    };

    struct bs{
        int a:8;
        int b:2;
        int c:6;
    }data;
    

    声明 data 为 bs 变量,占两个字节。a 占 8 位, b 占 2 位, c 占 6 位。
    这样使用位域变量data.a = 8; data.b = 3;

    struct bs{
        unsigned a:4;
        unsigned  :4;    /* 空域 */
        unsigned b:4; 
        unsigned c:4
    }
    

    位域名可以为空,表示空域


    共用体

    • union在相同的内存位置存储不同的数据类型,定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。
    • union的大小是成员变量中内存占用最大的决定的(当然需要考虑内存对齐)。
    • union 使用下标访问。

    union的语句格式如下:
    union [union tag]
    {
    member definition;
    member definition;
    ...
    member definition;
    } [one or more union variables];

    比如:

    union Data
    {
       int i;
       float f;
       char  str[20];
    } data;
    
    data.i = 9;
    

    函数

    一组一起可以执行的语句。

    函数定义

    返回值 函数名称(参数类型 参数名, 参数类型 参数名,···){
    语句;
    return 返回值;
    }

    函数声明

    extern 返回值 函数名称(参数类型 参数名, 参数类型 参数名,···);

    函数调用

    带返回值:变量 = 函数名(参数,参数);
    不带返回值:函数名(参数,参数);

    参数形参:函数定义时规定的参数。运行进入函数时被创建,退出函数时被销毁。
    实参:函数调用时外面传入的参数。

    可变参数

    int func(int num, ... ){
        va_list valist;
        va_start(valist, num);
        for (i = 0; i < num; i++) {
            int value = va_arg(valist, int);
         }
         va_end(valist);
    }
    

    va_start 得到第一个可变参数地址。
    va_arg 获取可变参数的当前参数,返回指定类型并将指针指向下一参数。
    va_end 置空可变参数列表,释放内存。

    数组参数

    int index(int array[], int i);
    *int index(int array, int i);
    int index(int array[5], int i); 这个5其实没啥作用。

    返回数组:只能返回指针,无法返回数组大小,可以利用out参数传递。


    作用域规则

    • 局部变量:使用{}里面创建的变量,只在变量所在的{}里面生效,如果同名会屏蔽作用域外面的变量,优先使用它
    • 全局变量:在函数外部定义的变量,整个程序运行周期有效。
    • 形式参数:函数内有效,和局部变量。

    内存管理

    c语言使用如下方法动态分配释放内存,分配的内存在堆上,使用完成后需要手动释放。使用前需要引入#include <stdlib.h>头文件

    • void *calloc(int num, int size); 动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了num*size 个字节长度的内存空间,并且每个字节的值都是0
    • void free(void *address); 释放 address 所指向的内存块,释放的是动态分配的内存空间。
    • void *malloc(int num); 在堆区分配一块指定大小的内存空间,这块内存空间在函数执行完成后不会被初始化,它们的值是未知的
    • void *realloc(void *address, int newsize); 该函数重新分配内存,把内存变为newsize字节,可能会返回新的地址

    相关文章

      网友评论

        本文标题:C语言基础快查

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