• 回首C语言关键字(~回首向来萧瑟处~)


    开篇废话:

     本文意在回顾 C 语言中的关键字,整理文件发现当时做的这些笔记还是蛮用心的,有临摹

     前辈的足迹也有自己的理解和体会。时至今日2018已经跨过一半,对不起过去半年,今天

     拿这篇关键字开篇,开启自己的程序猿心路,主要记录一下自己遇到的问题和学习的经历,

     方便自己。如果能对别也有用那就更开心了,由于自己还很菜,理解和体会都很有限,如

     果你打开发现了错误还请不吝赐教,随便评论,不要客气,我都会很感激的。首篇废话就

     这么多吧,我可能废话比较多,哈哈,批评我吧~

    C 关键字

    /**
     *    到目前C语言中有32+5+7=44个关键字,具体如下:
     *    
     *    ->C89关键字
     *    
     *    char       short       int         unsigned
     *    long       float       double      struct 
     *    union      void        enum        signed
     *    const      volatile    typedef     auto
     *    register   static      extern      break   
     *    case       continue    default     do
     *    else       for         goto        if
     *    return     switch      while       sizeof
     *    
     *    ->C99新增关键字
     *    
     *    _Bool      _Complex[复杂的]    _Imaginary[虚构的]  inline  restrict[限定/约束]
     *    
     *
     *    ->C11新增关键字
     *    
     *    _Alignas       Alignof         _Atomic         _Generic    
     *    _Noreturn      _Static_assert  _Thread_local
     *
     **/


    C89   32个关键字解释

    /* 
        1) char
    解释:  
        声明变量的时候用,char 占1个字节8bit,多数系统上是有符号的(arm 上无符号)
        范围 [-128, 127]
        
        在工程项目中开发推荐用
        int8_t   -> singned char
        uint8_t  -> unsigned char
    */
        char c = 'p';
        
        
    /*
        2) short
    
    解释:
        声明变量的时候用,short占2个字节,为无符号的,默认自带signed
        范围[-2^15, 2^15-1]2^15 = 32800
    
    推荐使用 int16_t or uint16_t 类型
    */
        short port = 8080; 
        
        
    /*
        3) int
    解释:
        声明变量的时候用,int声明的变量占4个字节,有符号,范围[-2^31,2^31-1]2^31
        
    推荐使用:int32_t or uint32_t 类型开发,方便移植
    */
        int i = 0;
        
     
    /*
        4) unsigned 
    解释:
        变量类型修饰符,被修饰的变量就是无符号的,范围>=0,unsigned 只能修饰整型的变量
        当然你用这个修饰变量的时候,再使用++和--运算的时候一定要小心。
    */
        unsigned int i = 0;         //正确
        unsigned short s = 0;       //正确
        unsigned float f = 0.11f;   //错误
        
    
    /*
        5) long
    解释:
        声明变量的时候使用,长整型x86上4个字节,x64上8个字节,一定不比int字节数少
        c99之后出现long long 类型为8个字节
    */
        long l = 4;
        long long ll = 8;
    
        
    /*
        6) float
    解释:
        声明变量的时候用,4个字节,精度是6-7位,
        详细精度可以看:https://blog.csdn.net/dxy612/article/details/5518477
    */
        float f = -0.12f;   
    
        
    /*
        7) double
    解释:
        声明变量的时候用,8个字节,精度在15-16位左右,有的时候压缩内存用float代替
    */
        double d = 2e13;
    
        
    /*
        8) struct
    解释:
        定义结构体,这个关键字用法很广,是大头。C的重要的思路就是面向过程编程,
        撑起面向过程的大头就是结构体。
    */
        // 普通结构体
        struct node {
            int id;
            struct node *next;
        };
        struct node n = {1,NULL};
    
        // 匿名结构体
        struct {
            int id;
            char* name;
        } per = {2,"Tom"}; 
    
        
    /*
        9) union
    解释:
        定义共用体,用法很花哨,常在特殊库函数封装中用到,技巧很强
    */
        // 普通定义
        union type {
            char c;
            int i;
            float f;
        };
        union type t = {.f = 3.33f};
    
        // 匿名定义
        union {...} t = {...};
     
        //类型匿名定义
        struct cjson {
            struct cjson *next; //采用链表结构处理,放弃二叉树结构,优化内存
            
            unsigned char type; // 数据类型和方式定义,一个美好的愿望
            char *key;          // json内容那块的key名称
            union {
                char *vs;       // type == _CJSON_STRING, 是一个字符串
                double vd;      // type == _CJSON_NUMBER, 是一个num值((int)c->vd)转成int或bool
            };    
        }; 
    
        /*
            什么是大小端?
            
            Endian表示数据在存储器中的存放顺序,
            
            大端(Big Endian): 是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的
            高地址中,这样的存储模式有点儿类似把数据当做字符串顺序处理:地址由小向大增加,
            儿数据从高位往低位。
            
            小端(Little Endian): 是指数据的高字节保存在内存的高地址中,而数据的低字节保存在
            内存的低地址中,这种存储模式将地址的高低和数据位权有效的结合起来,高地址部分权值
            高,低地址部分权值低,和我们的逻辑方法一致。
            
            这两种模式,泥瓦匠记忆宫殿:“小端低低”。这样就知道小端的模式,反之大端的模式。
            
            https://www.cnblogs.com/Alandre/p/4878841.html
        */
    
        // 在来一种union用法,判断大小端,笔试题中长见
        inline bool
        sh_isbig(void) 
        {
            static union {
                unsigned short s;
                unsigned char c;
            }U = {1};   // 1 -> 0x0001;
            return U.c == 0;
        }
    
        
    /*
        10) void
    解释:
        这个关键字用法很多,也是用在函数声明中,或函数参数
    */
        // 函数声明
        extern void foo();
    
        // 函数参数约束
        extern void foo(void);  //函数参数为void表示函数是无参数的,否则是任意的
    
        // 万能类型定义,指针随便转
        void* vp = NULL;
    
        
    /*
        11) enum
    解释:
        枚举类型,C中枚举类型很简陋,相当于一种变相的INT宏常量,估计也许是INT宏
        常量和枚举并存的原因
        
        问题1
        有些面试题中会问你enum 和#define 的区别:
        1. #define 宏常量是在预编译阶段进行简单的替换;enum常量则是在编译的时候确定其值
        2. 一般在调试器里,可以调试枚举常量,但不是不能调试宏常量。
        3. 枚举可以一次定义大量相关的常量,而#define 宏一次只能定义一个。
        
        问题2
        1. 枚举能做的事,#define 宏能不能做到?如果能,那为什么还需要枚举?
        答:能,枚举可以自增1,这样不用每一个值都定义,而宏必须每个值都定义,
            而枚举是一个集合,代表一类值,像代码中的颜色归为一类方便使用,而#define不能形成集合
        
        2. sizeof(ColorVal)的值是多少?为什么?
            enum Color{
                GREEN = 1,
                RED,
                BLUE,
                GREEN_RED = 10,
                GREEN_BLUE
            }ColorVal;
            
            答:值为4,ColorVal一个枚举变量,而枚举变量代表一个整数。
        
        
        
    */
        //
        //    flag_e -全局操作基本行为返回的枚举,用于判断返回值状态的状态码
        //    >=0 标识Success状态,< 0 标识Error状态
        //    
        //    枚举变量完全可以等同于int变量使用,枚举值等同于宏INT常量使用,枚举的默认值
        //    是以1为单位从上向下递增。
        //
        typedef enum {
            Success_Exit    =2, //希望存在,设置之前已经存在了
            Success_Close   =1, //文件描述符读取关闭,读取完毕也返回这个
            Success_Base    =0, // 结果正确的返回宏
            
            Error_Base      =-1, //错误类型,所有错误都用它,在不清楚的情况下
            Error_Parm      =-2, //调用参数错误
            Error_Alloc     =-3, //内存分配错误
            Error_Fd        =-4, //文件打开失败
        } flag_e;
    
        
    /*
        12) signed
    解释:
        变量声明类型修饰符,有符号型,对比unsigned无符号型,变量声明默认基本都是
        singned ,所有多数别就省略了
    */
        signed int piyo = 0x12345678;
    
        
    /*
        13) const
    解释:
        const 修饰的变量表示是一个不可修改的量,和常量有点区别,
    */
        // 声明不可修改的量
        const int age = 24;
    
        // 修饰指针
        const int *pa = NULL;       //pa指向的值(*pa)是不能修改
        int *const pt = NULL;       //pt不能指向新的指针,pt指向的值(*pt)可以修改
        const int *const pc = NULL; //pc和pc指向的值(*pc)都不能修改 
         
     
    /*
        14) volatile
    解释:
        声明变量修饰符,可变的,当变量前面有这个修饰符,编译器不再从寄存器中取值
        直接内存读取写入,保证实时性,常在多线程代码中
    */
        // 具体轮询器
        struct srl {
            mq_t mq;            // 消息队列
            pthread_t th;       // 具体线程
            die_f run;          // 每个消息都会调用run(pop())
            volatile bool loop; // true表示还在继续  
        };
        // 以后再使用loop的时候,其他线程修改,当前线程也能正确的获取它的值
     
     
    /*
        15) typedef 
    解释:
        类型重定义修饰符,重新定义新的类型,给变量去别名
    */
        // 声明普通类型
        typedef void* list_t;
    
        // 声明不完全类型,头文件中不存在struct tree
        typedef struct tree * tree_t;
    
        // 重定义变量类型
        typedef unsigned long int ub4;  /* unsigned 4-byte quantities*/
        typedef unsigned      char ub1; /* unsigned 1-byte quantities*/
    
        // 定义一个函数指针
        typedef uint32_t (*crc_func)(uint32_t crc, const void *buf, size_t len);
        // 使用
            crc_func crc32c; //声明一个crc_func类型的变量,实际就是一个函数指针,指向函数的首地址
        
    
    /*
        16) auto
    解释:
        变量类型声明符,auto变量存放在动态存储区,随和声明周期{开始},结束而立即释放,存在在栈上
        
        默认变量都是auto的,基本是不写
    */
        //演示
        {
            // 生存期开始
            int a = 0;
            auto int p = 1;
            // 生存期结束
        }
    
        
    /*
        17) register
    解释:
        修饰变量,这个关键字请求编译器尽可能的将变量存放在CPU内部寄存器中而不是通过内存寻址访问
        以提高效率,注意是尽可能,不是绝对。
        
    实用register修饰的注意点
        虽然寄存器的速度非常快 ,但是实用register修饰符也要些限制的,register变量必须是能被cpu寄存器所能接受的类型
        意味着register变量必须是一个单个的值,并且其长度应小于或等于整型的长度,而且register变量
        可能不存在在内存中,所以不能用取地址运算符"&"来获取register变量的地址。
    */ 
        #include <limits.h>
        
        register int i = 0;
        while (i < INT_MAX) {
            ++i;
        }
    
        
    /*
        18) static 
    解释: 
        static用法很广,修饰变量,函数,从字面上看static 很安静,这个关键字在C++
        中做了扩展,在C语言中重要就前面提到的两个作用。
        
        1. 修饰变量
        变量又分为局部变量和全局变量,但它们都在内存的静态区。
        
        静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用extern声明也没有办法使用
        准确的说:作用域是从定义之处开始,到文件结尾处结束,
        
        静态局部变量:在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也
        用不了,由于被static修饰的变量总是存在内存的静态区,所有即使这个函数运行结束,这个
        静态变量的值也不会被销毁,函数下次使用时任然能用这个值。
        
        看下面一段代码:
        
        static int j;
        void fun1(void) {
            static int i = 0;
            i++;
        }
        
        void fun2(void) {
            j = 0;
            j++;
        }
        
        int main()
        {
            int k = 0;
            for(k = 0; k < 10; k++) {
                fun1();
                fun2();
            }
                
            return 0;
        }
        
        此时i和j的值分别是多少?
        我们来分析一下哈:
        首先毫无疑问,j 是个全局静态变量,调用一次fun2()后,它的值始终没有变,所有调用10次值还是1
        
        i这个变量是一个局部静态变量,值存放在内存的静态区,调用一次fun1()结束后它的值不会被销毁,
        函数下次调用的时候任然使用这个值,所有调用10次它的值一次为,1,2,3,4,5,6,7,8,9,10,11
     
        2. 修饰函数
        函数前面加static使得函数成为静态函数,但此处"static"的含义不是指存储方式,而是指对函数作用域
        仅局限于本文件(所有称内部函数)使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义
        的函数是否会与其他文件中的函数同名。
     
        // C99之后加的static新用法,编译器优化
        / static 只能修饰函数第一维,表示数组最小长度,方便编译器一下取出所有内存进行优化
    */
        int sum(int a[static 10]) { ... } 
     
     
    /*
        19) extern 
    解释:
        extern (外面的,外来的)可以置于变量或函数前,以表明变量或函数的定义在别的文件中
        下面代码用到的这些变量或函数是外来的,不是本文件中定义的,提示连接器遇到此变量
        和函数时在其他模块中解析/绑定此标识符。
    */
        
        // 声明引用全局变量
        extern int G_arg;
        
        // 声明引用全局函数,(主动声明,希望外部可以调用)
        extern int kill(int sig, int val);
      
        // 当然有时候extern不写,对于变量不写会出现重定义,对于函数时可以缺省的
     
     
    /*
        20) break
    解释:
        结束语句,主要用于循环的跳出,只能跳出到当前层级,也用于switch语句中
        跳出swithc嵌套
    */
        // 演示  for循环
        for(;;) {
            // 符合条件跳转
            if(n == 10)
                break;  // 跳出for循环
        }
    
        
    /*
        21) 22) 23) switch & case & default 
    解释:
        21)switch :条件分支语句,很复杂的if else if时候可以用switch
        22)case :   语句中分支语句,确定走哪个分支
        23)default:  switch 分支的默认分支,所有case都没有进入就到default分支
    */
        // 演示
        
        switch (errcode) {
    	case SSL_ERROR_ZERO_RETURN:
    		/* Possibly a clean shutdown. */
    		if (SSL_get_shutdown(bev_ssl->ssl) & SSL_RECEIVED_SHUTDOWN)
    			event = BEV_EVENT_EOF;
    		else
    			dirty_shutdown = 1;
    		break;
    	case SSL_ERROR_SYSCALL:
    		/* IO error; possibly a dirty shutdown. */
    		if ((ret == 0 || ret == -1) && ERR_peek_error() == 0)
    			dirty_shutdown = 1;
    		break;
    	case SSL_ERROR_SSL:
    		/* Protocol error. */
    		break;
    	case SSL_ERROR_WANT_X509_LOOKUP:
    		/* XXXX handle this. */
    		break;
    	case SSL_ERROR_NONE:
    	case SSL_ERROR_WANT_READ:
    	case SSL_ERROR_WANT_WRITE:
    	case SSL_ERROR_WANT_CONNECT:
    	case SSL_ERROR_WANT_ACCEPT:
    	default:
    		/* should be impossible; treat as normal error. */
    		event_warnx("BUG: Unexpected OpenSSL error code %d", errcode);
    		break;
    	}
        
     
    /*
        24) continue
    解释:
        跳过此次(本轮)循环,直接进行条件判断操作,进入下一轮循环,
        for和while循环有些区别,for会执行第三个后面的语句
    */
        // 演示
        for(int i = 0; i < 20; ++i) {
            if(i % 2 == 0)
                continue; // 满足if跳到 ++i,再到条件 i<20
        }
     
     
    /*
        25) do ... 26) while 27) for
    解释:
        C 语言中的三种循环
        25) do: do循环,先执行循环体,在执行条件判断,先保证循环体执行一遍在判断条件
        在一个菜单的程序设计的时候用do...while 比较好,首先给出选择。
        
        26) while: 循环,先判断while后的条件,只有条件真才执行里面的代码块,
        常用的还有就是一种死循环的写法 while(1)
        
        27) for: 循环,for循环很容易的控制循环次数,多用于事先知道循环次数的情况下
    */    
        // 演示
        /* do -while 循环 */
        do {
    		event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
    		xcount++;
    	} while (count != fired);
        
        /* while 循环 */
        while(1) {
            if('#' == GetInputChar()) 
                break;
        }
        
        /* for 循环 */
        for (i = 0; i < 25; i++) {
    		tv = run_once();
    		if (tv == NULL)
    			exit(1);
    		fprintf(stdout, "%ld\n",
    			tv->tv_sec * 1000000L + tv->tv_usec);
    	}    
     
     
    /*
        28) if ... 29) else
    解释:
        28) if: if分支语句,可以单独使用,可嵌套
        29) else: else分支,必须和if分支对应,和if分支条件相反
    */    
        n = recv(fd, (char*)&ch, sizeof(ch), 0);
    	if (n >= 0)
    		count += n;
    	else
    		failures++;
    	if (writes) {
    		if (widx >= num_pipes)
    			widx -= num_pipes;
    		n = send(pipes[2 * widx + 1], "e", 1, 0);
    		if (n != 1)
    			failures++;
    		writes--;
    		fired++;
    	}
    
        
    /*
        30) goto
    解释:
        关于goto这个关键字,褒贬很多,原因就在于它太自由,可以灵活的跳转,在结构化
        编程中它有了很多争议,如果不加以限制,它的自由跳转的确会破坏结构化设计的风格
        所有使用它一定要慎重,下面我们用一段真实代码展示它的魅力,
        
    */
        // 演示
        if (evbuffer_expand_fast_(buf, to_alloc, 2) < 0) {
    		goto done;
    	}
    
    	for (n = 0; n < n_vec; n++) {
    		/* XXX each 'add' call here does a bunch of setup that's
    		 * obviated by evbuffer_expand_fast_, and some cleanup that we
    		 * would like to do only once.  Instead we should just extract
    		 * the part of the code that's needed. */
    
    		if (evbuffer_add(buf, vec[n].iov_base, vec[n].iov_len) < 0) {
    			goto done;
    		}
    
    		res += vec[n].iov_len;
    	}
    
    done:
        EVBUFFER_UNLOCK(buf);
        return res;
    
        
    /*
        31) return 
    解释: 
        return 用来终止一个函数并返回其后面跟着的值
        
        使用return 不可返回指向“栈内存”的“指针”,因为该内存在函数体结束后就被自动释放了
    */
        // 演示
        
        #include <stdio.h>
        
        int main(int arg, char* argv[]) {
            ......
            
            return EXIT_SUCCESS;
        }
    
        
    /*
        32) sizeof 
    解释:
        也称为sizeof运算符,计算变量或类型的字节大小,它常被误认为是个函数
        面试中也经常有它出现,下面我们看看它的用法
    */
        // 演示 x86上
        int *p = NULL;
        sizeof(p) 的值是多少          ---> 4
        sizeof(*p)呢?                ---> 4
        
        int a[100];
        sizeof(a)的值是多少           ---> 400
        sizeof(a[100])的值            ---> 4
        sizeof(&a)的值                ---> 4
        sizeof(&a[0])的值             ---> 4
        
        int b[100];
        void func(int b[100]) {
            sizeof(b); // sizeof(b)的值 ---> 4
        }
    
        // 常用一种写法获取数组长度
        #define LEN(arr) (sizeof(arr) / sizeof(*(arr)))
        #define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
    

     到此 C89 的32个关键字都就介绍完了,对于 C99 和 C11的关键字,后续完善.....

    后记

    《意颓废》

    意颓废,人难寐。
    夜夜长街空买醉。
    路茫茫,断离肠。
    梦牵魂索,独守西窗,伤!伤!伤 。

    花儿碎,风流泪。
    曲悲弦断连心肺。
    恋成殇,心透凉。
    一朝白头,只为情狂,怆!怆!怆。

  • 相关阅读:
    JAVA实现DES加密实现详解
    CentOS 7安装Hadoop 3.0.0
    使用JAVA开发微信公众平台(一)——环境搭建与开发接入
    Oracle触发器用法实例详解
    负载均衡中使用 Redis 实现共享 Session
    在windows上部署使用Redis
    java 线程排查问题流程
    使用Fernflower 比较准确的反编译整个java项目
    Mysql用户本机登陆不成功的解决
    kali 系统的源
  • 原文地址:https://www.cnblogs.com/zhaoosheLBJ/p/9250912.html
Copyright © 2020-2023  润新知