SDP(0):Streaming-Data-Processor – Data Processing with Akka-Stream

概述 – C语言老了

 
 再有两天就进来2018了,想想依旧要未雨绸缪一下过年的工作方向。回忆当年上马攻读函数式编程时的根本目标是想设计一套标准API給这多少个习惯了OOP形式开发商业使用软件的程序员们,使他们能用一种恍若传统数据库软件编程的措施来促成多线程,并行运算,分布式的多寡处理应用程序,前提是这种编程格局不需要对函数式编程语言、多线程软件编程以及集群环境下的分布式软件编程模式有很高的经历要求。后边试着公布了一个按照scalaz-stream-fs2的数码处理工具开源项目。该品种为主落实了多线程的数据库数据并行处理,能充裕利用域内服务器的多核CPU环境以streaming,non-blocking情势加强数据处理效用。最近刚形成了对任何akka套装(suite)的打听,感觉akka是一套精美的分布式编程工具:一是actor情势提供了多种多线程编程情势,再不怕akka-cluster能轻松地实现集群式的分布式编程,而集群环境转变只需要调整安排文件,无需改变代码。akka-stream是一套功用尤为完整和强大的streaming工具库,那么只要以akka-stream为根基,设计一套能在集群环境里展开分布式多线程并行数据处理的开源编程工具应该可以是2018的首要任务。同样,用户仍可以够遵照他们熟知的数据库应用编程模式轻松实现分布式多线程并行数据处理程序的开发。

  近日而言(二零一七年三月12日) C语言中有 32 + 5 + 7 = 44 个至关首要字. 具体如下
O(∩_∩)O哈哈~

 
 我把一般中小集团的IT系统分成两大一部分:一是实时的数量收集(输入)部分,二是批量数额抽取、分析、处理局部。为了让传统中小型集团IT软件编程职员能支付服务器集群环境上数据平台(如云端数据平台)运行的软件系统,我打算通过这个DSP(Streaming-Data-Processor)项目来落实地方提到的第二片段。第一部分可以用CQRS(Command-Query-Responsibility-Separation)即读写分离架构和事件记录(event-sourcing)格局来贯彻一种高效迅速响应、安全稳定运转的多少收集系统。这有些我会在做到SDP项目后以akka-persistence为着力,通过akka-http,AMQP如RabitMQ等技巧来兑现。

-> C89关键字

 
按一般的scala和akka的编程情势编写多线程分布式数据库管理软件时一是要依据akka代码情势,使用scala编程语言的一对较深的语法;二是急需涉及异步Async调用,集群Cluster节点任务布置及Streaming对外集成actor运算格局的底细,用户需要拥有一定的scala,akka使用经验。再接下来就需要按业务流程把各业务环节分解成不依靠顺序的效用模块,然后把那一个分拆出来的功用分派给集群中不同的节点上去运算处理。而对于SDP用户来说,具备最基本的scala知识,无需了解akka、actor、threads、cluster,只要遵照SDP自定义的作业处理流格局就可以编写多线程分布式数据处理程序了。下边我就用部分文字及伪代码来叙述一下SDP的结构和功效:

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

完全来说SDP是由一或五个Stream组成的;每个Stream就代表一段程序。一段完整的主次Stream是由流元素源Source、处理节点Process-Node(Flow)及数量输出终点Sink多少个环节组成,下边是一个第一名的程序框架:

-> C99新增关键字

  def load(qry: Query): PRG[R,M] = ???
  def process1: PRG[R,M] = ???
  def process2: PRG[R,M] = ???
  def recursiveProcess(prg: PRG[R,M]): PRG[R,M] = ???
  def results: PRG = ???

  load(qryOrders).process1.process2.recursiveProcess(subprogram).results.run
_Bool _Complex _Imaginary inline restrict

从下面的言传身教中大家可以看来有着定义的函数都发生PRG[R,M]类型结果。其中R类型就是stream的因素,它流动贯穿了先后的具有环节。就像下水道网络运行规律一样:污水由源头Source流入终点Sink,在半路可能通过三个污水处理节点Node。每一个节点代表对管道中流淌污水处理的办法,包括分叉引流、并叉合流、添加化学物质、最终经过极端把拍卖过的水向外输出。在PRG中流动的R类型可能是数额如数据库表的一行,又或者是一条Sring类型的query如plain-sql,可以用JDBC来运行。cassandra的CQL也是String类型的。Slick,Quill,ScalikeJDBC和部分别样ORM的Query都足以生出plain-sql。

-> C11新增关键字

Source是一段程序的启幕部分。一般的话Source是透过运算Query发生一串数据行或者人工构建而成。Source也足以并行运算Query暴发,然后合并成一条无序的数据源,如下伪代码的门类:

_Alignas _Alignof _Atomic _Generic _Noreturn _Static_assert _Thread_local
  def load_par(qrys: Query*): PRG[R,M] = ???

 下边容我细细分析起实际用法.(存在平台差距, 有问题特别欢迎评论补充,
这就相当于一个重中之重字字典)

Process-Node是SDP最重点的一个组成部分,因为大部分用户定义的各类事务效能是在此处运算的。用户可以挑选对事情职能拓展拆分然后分担给不同的线程或不同的集群节点开展多线程并行或分布式的演算。SDP应该为用户程序提供多线程,并行式、分布式的运算函数。首先,运算用户程序后应发生R类型结果同时,作为一种reactive软件,必须确保完全消耗上一阶段暴发的有着R类型元素。下面是一个用户函数的花样:

 

  type UserFunc = R => R 

C89 32个关键**

除却fire-and-run类型的运算函数,SDP还应当提供针对性多线程或分布式程序的map-reduce式运算函数。伊始想法是:无论重回结果与否,分派任务都是由persistence-actor来推行的,这样能担保不会挂一漏万任何任务。如若完全任务急需在拥有分派任务重回运算结果后再统一开展深度运算时akka的actor音讯使得格局是最符合不过的了。具体情况可以参照我眼前关于cluster-sharding的博文。

1) char

Sink的第一效用实际上是保证完全消耗程序中爆发的富有因素,这是reactive类型程序的必须要求。

解释:

好了,不知不觉还有多少个刻钟就进来2017倒计时了。飞快凑合着在跨入2018事先把这篇发表出去,刚好是二零一九年的末尾一篇博文。祝各位在新的一年中行事生活顺利!

  阐明变量的时候用! char占1字节, 8bit. 多数序列(vs or
gcc)上是有记号的(arm 上无符号), 范围是[-128, 127]. 

 

在工程项目开发中推荐用 

 

#include <stdint.h>

int8_t   -> signed char 
uint8_t  -> unsigned char

 

聊天一点, 程序开发最常遭遇的就是自解释问题. 鸡生蛋, 蛋生鸡. 前面再分析
signed 和 unsigned

 

演示:

 

#include <stdio.h>

char c;
c = getchar();
rewind(stdin);
printf("c = %d, c = %c.\n", c);

 

2) short

解释:

  阐明变量的时候用! short 占2字节, 为无符号的. 默认自带signed.
范围[-2^15, 2^15 – 1] 2^15 = 32800.

推介应用 int16_t or uint16_t 类型.

演示:

short port = 8080;
printf("port = %d.\n", port);

 

3) int

解释:

  注脚变量的时候用! int 注解的变量, 占4字节, 有标志. 范围 [-2^31,
2^31-1].

推荐用 int32_t 和 uint32_t类型开发. 方便移植

演示:

int hoge = 24;
printf("hoge = %d.\n", hoge);

 

4) unsigned

解释:

  变量类型修饰符! 被修饰的变量就是无符号的.范围 >= 0.  unsigned
只好修饰整型的变量.

当然当您用这多少个修饰变量的时候. 再拔取 – 和 — 运算的时候一定要小心

演示:

unsigned int i = 0;          // 正确
unsigned short s = 0;        // 正确
unisgned float f = 0.11f;    // 错误

 

5) long

解释:

  讲明变量的时候用!长整型 x86上四字节, x64上8字节.
一定不比int字节数少.  C99从此出现long long类型8字节.

演示:

    long l = 4;
    long long ll = l;
    printf("l = %ld, ll = %lld.\n", l, ll);

 

6) float

解释:

  阐明变量的时候用! 四字节. 精度是6-7位左右.  详细精度能够看 float与double的界定和精度

演示:

float f = -0.12f;        // 四字节
long float lf = 0;       // 八字节 等同于 double, 不推荐这么写

 

7) double

解释:

  阐明变量的时候用!风水节,精度在15-16位左右.部分时候压缩内存用float代替.

演示:

double d = 2e13;               // 8字节
long double ld = -0.99;        // x86也是8字节, 不推荐这么用
long long double lld = 99;     // 写法错误, 不支持

 

8) struct

解释:

  定义结构体, 这多少个重大字用法广泛, 是大头. c
的最紧要思路就是面向过程编程. 撑起面向过程的花边就是社团体.

struct 就是概念结构的东西, 可以看看下边演示

演示:

// 普通结构体定义
struct node {
    int id;
    struct node * next;   
};

struct node node = { 1, NULL };

// 匿名结构定义
struct {
   int id;
   char * name;   
} per = { 2, "王志" };

 

9) union

解释:

  定义公用体, 用法很花哨. 常在特殊库函数封装中用到.技巧性强

演示:

// 普通定义
union type {
    char c;
    int i;
    float f;
};

union type t = { .f = 3.33f };

// 匿名定义
union { ... } t = { .... };

// 类型匿名定义
struct cjson {
    struct cjson * next;     // 采用链表结构处理, 放弃二叉树结构, 优化内存
    struct cjson * child;    // type == ( _CJSON_ARRAY or _CJSON_OBJECT ) 那么 child 就不为空

    unsigned char type;      // 数据类型和方式定义, 一个美好的意愿
    char * key;       // json内容那块的 key名称     
    union {
        char * vs;    // type == _CJSON_STRING, 是一个字符串     
        double vd;    // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
    };
};

再来一种 union用法, 利用内存对齐. 

//12.0 判断是大端序还是小端序,大端序返回true
inline bool
sh_isbig(void) {
    static union {
        unsigned short _s;
        unsigned char _c;
    } _u = { 1 };
    return _u._c == 0;
}

再有很久以前利用union 实现内存字节对齐, 太多了. 每个重点字用法,
确实很多, 很意外.

语言, 

10) void

解释:

  那一个是空关键字. 用法很多. 也是自身最欢喜的重要字. 用在函数讲明中,
类型定义中.

演示:

// 函数声明
extern void foo();

// 函数参数约束
extern void foo(void);   // ()中加了void表示函数是无参的, 否则是任意的

// 万能类型定义, 指针随便转
void * arg = NULL;

 

11) enum

解释:

  枚举类型, C中枚举类型很简陋. 其实就一定于一种变相的INT宏常量.
推测这也许也是 INT宏常量和枚举并存的原因.

演示:

//
// flag_e - 全局操作基本行为返回的枚举, 用于判断返回值状态的状态码
// >= 0 标识 Success状态, < 0 标识 Error状态
//
typedef enum {
    Success_Exist    = +2,           //希望存在,设置之前已经存在了.
    Success_Close    = +1,           //文件描述符读取关闭, 读取完毕也会返回这个
    Success_Base     = +0,           //结果正确的返回宏

    Error_Base       = -1,           //错误基类型, 所有错误都可用它, 在不清楚的情况下
    Error_Param      = -2,           //调用的参数错误
    Error_Alloc      = -3,           //内存分配错误
    Error_Fd         = -4,           //文件打开失败
} flag_e;

枚举变量完全可以等效 int 变量使用, 枚举值等同于宏INT常量使用.
枚举的默认值是以1位单位从上向下递增.

 

12) signed

解释:

  变量表明类型修饰符. 有符号型, 相比 unsigned 无符号型.
变量阐明默认基本都是 signed, 所以多数人家就简单了.

演示:

signed int piyo = 0x1314520;
signed char * str = u8"你好吗";

本来了, 平日不需要着意加. 会令人嫌麻烦. O(∩_∩)O哈哈~

 

13) const

解释:

  const修饰的变量表示是个不得修改的量. 和常量有点区别. 能够简简单单认为
const type val 是个只读的.

演示:

// 声明不可修改的量
const int age = 24;

// 修饰指针
const int * pi = NULL;         // *pi 不能修改指向变量
int * const pt = NULL;         // pt 不能指向新的指针 
const int * const pc = NULL;   // *pc 和 pc 都不能动

实则在c中着力没有什么样改变不了的. 全是内存来回搞, 软件不行硬件~~

 

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;

 

16) auto

解释:

  变量类型表明符, auto变量存放在动态存储区,随着生命周期{最先}停止而登时释放.存放在栈上. 

默认变量都是auto的. 基本都是不写, 除非装逼!

演示:

{
    // 生存期开始
    int hoge = 0;
    auto int piyo = 1;
    // 生存期结束
}

毫无用生命周期截止的变量, 存在各类意外. 

 

17) register

解释:

  变量修饰符,只可以修饰整形变量.表示期望以此变量存放在CPU的寄存器上.现代编译器在打开优化时候,

可知肯定水准上默认启用register寄存器变量.

演示:

#include <limits.h>

register int i = 0;
while (i < INT_MAX) {

   ++i;
}

出于CPU寄存器是少数的, 有时候你不怕阐明的寄存器变量也说不定只是平凡变量.
printf(“&i = %p\n”, &i) 那种用法是非法.

寄存器变量不能够取地址.

 

18) static

解释:

  static 用法很广泛. 修饰变量, 表示变量存在于静态区, 基本就是全局区.
生存周期同系统生存周期.

static修饰的变量功能域只可以在当前文件范围内. 能够作为上层语言的private.
除了auto就是static.

static修饰函数表示目前函数是私有的,只可以在此时此刻文件中接纳.
更加详实的看演示部分.

演示:

// 修饰全局变量, 只对当前文件可见
static int _fd = 0;

// 修饰局部变量, 存储在全局区, 具有记忆功能
{
    static int _cnt = 0;
}

// 修饰函数, 函数只能在当前文件可见
static void * _run(void * arg) {
   ......
   return arg;
}

//
// C99之后加的static新用法, 编译器优化
// static 只能修饰函数第一维,表示数组最小长度, 方便编译器一下取出所有内存进行优化
//
int sum(int a[static 10]) { ... }

 

19) extern

解释:

  extern 关键字表示申明, 变量声明, 函数讲明.  奇葩的用法很多.

演示:

// 声明引用全局变量
extern int g_cnt;

// 声明引用全局函数
extern int kill(int sig, int val);

当然有时候extern不写, 对于变量不行会冒出重定义. 对于函数是可以缺省写法.
再扯一点

// extern 主动声明, 希望外部可以调用
extern int kill(int sig, int val);

// extern 缺省,不推荐外部调用
int kill(int sig, int val);

 

20) break

解释:

  停止语句. 重要用来循环的跳转, 只好跳转到当前层级. 也用于switch
语句中, 跳出switch嵌套.

演示:

for(;;) {
   // 符合条件跳转
   if(six == 6)    
       break;
}


// break 跳出while循环
int i = 0;
while(i < 6) {
   if(i == 3)
      break;
} 

break用法重要和循环一块使用, 还有do while. 但不得不跳转当前层循环. 

 

21) case

解释:

  switch 语句中分支语句. 确定走什么分支.

演示:

// case 普通用法 和 break成对出现
switch ((c = *++ptr)) {
case 'b': *nptr++ = '\b'; break;
case 'f': *nptr++ = '\f'; break;
case 'n': *nptr++ = '\n'; break;
case 'r': *nptr++ = '\r'; break;
case 't': *nptr++ = '\t'; break;
}

多扯一点, 对于case约等于标记点. switch
中值决定case跳转到啥地方.再从来往下实施, 遭受break再为止switch嵌套.

 

22) continue

解释:

  跳过此次循环. 直接开展标准判断操作. for 和 while 有些局别. for
会执行第多少个前边的语句.

演示:

// for 循环 continue
for(int i = 0; i < 20; ++i) {
    if(i % 2 == 0)
         continue;

     // 上面continue 调到 ++i -> i < 20 代码块
}

 

23) default

解释:

  switch 分支的默认分支, 如若case都没有进来这就进去default分支.
default 可以省略break. c 语法中可行.

演示:

uint32_t 
skynet_queryname(struct skynet_context * context, const char * name) {
    switch(name[0]) {
    case ':':
        return strtoul(name+1,NULL,16);
    case '.':
        return skynet_handle_findname(name + 1);
    default:
        skynet_error(context, "Don't support query global name %s",name);    
    }
    return 0;
}

 

24) do

解释:

  do 循环. 先实施循环体, 后再实践尺度判断.

演示:

register i = 0;
do {
    if(i % 2 == 0) 
           continue;

    printf("i = %d.\n", i);   

} while(++i < 10);

do while 循环有时候可以减去五次口径判断. 性能更好, 代码更长.

 

25) else

解释:

  else 是 if 的反分支. 具体看演示

演示:

#include <stdbool.h>

if(true) {
   puts("你好吗?");
}
else {
  puts("我们分手吧.");
}



// 附赠个else 语法
#if defined(__GNUC__)

// 定义了 __GNUC__ 环境, 就是gcc环境

#else

#error "NOT __GNUC__, NEED GCC!";

#enfif

 

26) for

解释:

  for 循环其实就是while循环的语法糖. 也有异军突起的地点.

演示:

for(int i = 0; i < 2; ++i) {
    if(i == 1)
       continue;
    if(i == 2)
       break;
}

等价于下面这个
int i = 0;
while(i < 2) {
  if(i == 1) {
     ++i;
     continue;
  }
  if(i == 2)
     break;  

  ++i;
}

// for 最好的写法, 在于死循环写法
for(;;) {
   // xxxx
}

for(;;) {  } 比 while(true) { } 写法好, 有一种不走条件判断的意图,
即使汇编代码是相同的.

 

27) goto

解释:

  goto 是本人第二喜欢的基本点字.  能够在现阶段函数内跳转. goto
可以代替所有循环.

演示:

__loop:
   // xxx 死循环用法
goto __loop;
__exitloop:

还有就是在工程开发中, goto 常用于复制的政工逻辑.

    if ((n = *tar) == '\0') // 判断下一个字符
        goto __err_ext;    

    if(cl % rl){ // 检测 , 号是个数是否正常
    __err_ext:
        SL_WARNING("now csv file is illegal! c = %d, n = %d, cl = %d, rl = %d."
            , c, n, cl, rl);
        return false;
    }

 

28) if

解释:

  if 分支语句. 用法太多了. 程序语句中拨出就是智能.

演示:

if(false) {
   puts("我想做个好人!");
}

 

29) return

解释:

  程序重临语句太多了. 用于函数重返中. 重临void 直接 return;

演示:

#include <stdlib.h>

int main(int argc, char * argv[]) {

   return EXIT_SUCCESS;
}

 

30) switch

解释: 

  条件分支语句. 很复杂的if else if 时候可以switch.

演示:

#include <unistd.h>

do {
    int rt = write(fd, buf, sizeof buf)
    if(rt < 0) {
       switch(errno) {
       case EINTER
           continue;
       default:
           perror("write error");
       }
    }
} while(rt > 0);

 

31) while

解释:

  循环语句, 有do while 和 while 语句两种.

演示:

#define _INT_CNT (10)

int i = -1;
while(++i < _INT_CNT) {
     // ...... 
}

 

32) sizeof

解释:

  这些首要字也称之为 sizeof 运算符. 统计变量或项目标字节大小.
这些重要字特别好用!

演示:

sizeof (main)   -> x86 上四字节

// 获取数组长度,只能是数组类型或""字符串常量,后者包含'\0'
#define LEN(arr) (sizeof(arr) / sizeof(*(arr))) 

到此地C89保留的要紧字基本解释完毕.

 

C99 5个新增关键**

33) _Bool

解释:

  bool类型变量, 等价于 unsigned char . 唯有0和1.

演示:

#include <stdbool.h>

bool flag = true;

// 或者直接用
_Bool flag = !0;

 

34) _Complex

解释:

  对于C99 标准定义, 存在 float _Complex, double _Complex, long
double _Complex 复数类型. 下边先演示gcc 中关于复数的用法.

演示:

#include <math.h>
#include <stdio.h>
#include <complex.h>

//
// 测试 c99 complex 复数
//
int main(int argc, char * argv[]) {

    float complex f = -1.0f + 1.0if;
    printf("The complex number is: %f + %fi\n",crealf(f), cimagf(f));

    double complex d = csqrt(4.0 + 4.0i);
    printf("d = %lf + %lfi\n", creal(d), cimag(d));

    return 0;
}

实际上在复数类型中, gcc标准落实

#define complex         _Complex

而在VS 中贯彻具体为

#ifndef _C_COMPLEX_T
    #define _C_COMPLEX_T
    typedef struct _C_double_complex
    {
        double _Val[2];
    } _C_double_complex;

    typedef struct _C_float_complex
    {
        float _Val[2];
    } _C_float_complex;

    typedef struct _C_ldouble_complex
    {
        long double _Val[2];
    } _C_ldouble_complex;
#endif

typedef _C_double_complex  _Dcomplex;
typedef _C_float_complex   _Fcomplex;
typedef _C_ldouble_complex _Lcomplex;

总的而言, 学习C 最好的阳台就是 *nix 平台上利用 Best new GCC.
当然除了科学总结会用到复数, 另外很少.

此地VS 和 GCC实现不一样. 用起来需要注意.

 

35) _Imaginary

解释:

  虚数类型. _Complex 复数类型的虚部. 例如 10.0i, 10.8if 等等. 
这多少个关键字在VS 上未曾兑现. 其实自己也以为没有必要.

和_Complex有重叠.

演示:

  那多少个首要字无法在代码中表示. 系统保留, 我们不可能使用.

 

36) inline

解释:

  内联函数,从C++中引入的概念. 就是将小函数直接嵌入到代码中.
C的代码损耗在于函数的出入栈. 假若能够推荐用内联函数

代替宏. 宏能不用就不用. 函数申明的时候绝不加inline 需要加extern,
定义的时候需要加inline.

演示:

/*
 * 对json字符串解析返回解析后的结果
 * jstr        : 待解析的字符串
 */
extern cjson_t cjson_newtstr(tstr_t str);


inline cjson_t 
cjson_newtstr(tstr_t str) {
    str->len = _cjson_mini(str->str);
    return _cjson_parse(str->str);
}


// 还有就是和static 一起使用
static inline int _sconf_acmp(tstr_t tstr, struct sconf * rnode) {
    return strcmp(tstr->str, rnode->key);
}

37) restrict

解释:

  这是很装逼的要害字用于编译器优化. 关键字restrict只用于限定指针;该重大字用于告知编译器,

拥有修改该指针所指向内容的操作全体都是基于(base
on)该指针的,即不存在其他进行修改操作的不二法门;

如此这般的结果是匡助编译器举行更好的代码优化,生成更有效能的汇编代码。

演示:

extern void *mempcpy (void *__restrict __dest,
                      const void *__restrict __src, size_t __n)
     __THROW __nonnull ((1, 2));

下边是摘自GCC 的 string.h中. 其实正式用法

// 简单演示用法, GCC 和 VS 都是 __restrict 推荐加在 * 后面
static void _strlove(char * __restrict dest) {
    *dest = '\0';
}

Pelles C 编译器可以全部辅助 restrict.

 

C11 7个新增关键字

38) _Alignas

解释:

  内存对齐的操作符. 需要和_Alignof匹配使用,
指定结构的对齐模式.
演示:

#ifndef __cplusplus

#define alignas _Alignas
#define alignof _Alignof

#define __alignas_is_defined 1
#define __alignof_is_defined 1

#endif

譬如一种用法

#include <stdio.h>
#include <stdalign.h>

struct per {
    int age;
    double secl;
    char sex;
};

int main(int argc, char * argv[]) {
    char c[100];
    alignas(struct per) struct per * per = (struct per *)&c;
    printf("per = %p, c = %p.\n", per, c); 

    return 0;
}

 

将c 数组以 struct per 对齐模式对齐重回回去.

 

39) _Alignof

解释:

  得到类型和变量的对齐模式.
演示:

printf("alignof(struct per) = %zd.\n", alignof(struct per));

 

40) _Atomic

解释:

  原子操作, 原子锁. gcc 很已经襄助. 详细用法可以参见 CAS
https://sanwen8.cn/p/18dZQie.html

讲的可以.
演示:

#include <stdio.h>
#include <stdatomic.h>

int main(int argc, char * argv[]) {

    _Atomic int hoge = ATOMIC_VAR_INIT(100);
    int piyo = atomic_load(&hoge);  
    printf("piyo = %d.\n", piyo);
    piyo += 2;
    atomic_store(&hoge, piyo);
    printf("hoge = %d.\n", hoge);

    return 0;
}

切实的实施结果, 你也懂就这样. 原子操作, 对于写出高效代码很重要.

 

41) _Generic

解释:

  这多少个相比较叼, C的泛函机制. 高级函数宏. 下边来个老套路用法
演示:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define ABS(x) \
        _Generic((x), int:abs, float:fabsf, double:fabs)(x)

//
// 测试 C11 语法
//
int main(int argc, char * argv[]) {

        int a = 1, b = 2, c = 3;

        _Generic(a + 0.1f, int:b, float:c, default:a)++;
        printf("a = %d, b = %d, c = %d\n", a, b, c); 

        printf("int abs: %d\n", ABS(-12));
        printf("float abs: %f\n", ABS(-12.04f));
        printf("double abs: %f\n", ABS(-13.09876));

        return EXIT_SUCCESS;
}

宏泛型真的很给力. 宏又能玩上天了.

   

42) _Noreturn

解释:

  修饰函数,相对不会有再次回到值. _Noreturn 表明的函数不会再次回到.
引入此新的函数修饰符有六个目标:

  • 铲除编译器对从未 return的函数的警告. 
  • 同意某种只针对不回去函数的优化.

演示:

_Noreturn void suicide(void) {
    abort(); // Actually, abort is _Noreturn as well
}

再扯一点, GCC中等同于 __attribute__((__noreturn__)),
在VC中一般效用是 __declspec(noreturn).

它不是说函数没有重返值,而是说假如你调了那多少个函数,它世代不会回来。一些函数是永远不会回去的,

譬如 abort或者 exit之类,调用它们就表示结束程序. 所以
warning就显示没有必要.

 

43) _Static_assert

解释:

  编译器期间断言, 当 #if #error 搞完毕(预编译)之后, 编译器断言.
assert是运作时断言.用的时候看现实的需求.
演示:

_Static_assert(__STDC_VERSION__ >= 201112L, "C11 support required");
// Guess I don't really need _Static_assert to tell me this :-(

 

44) _Thread_local

解释:

  到这里快扯完了, 其实C11专业是个很好的尝试. 为C引入了线程和原子操作.
各个安全特点补充. 可以说C强大了.

不过还远远不够, 因为尤其丑了. C11为C引入了线程 在
头文件<threads.h>中定义.但也允许编译可以不实现.

_Thread_local是新的储存类修饰符, 限定了变量无法在多线程之间共享。
演示:

_Thread_local static int i;
// Thread local isn't local!

语义上就是线程的村办变量.

   

后记 – 我们也不年轻了

  假使有题目欢迎补充, 关键字当字典用也是好的 哈哈啊

  确实男子汉 
http://music.163.com/\#/m/song?id=31421394&userid=16529894**
**

  语言 1

  你会心疼吗, 已经心痛了 /(ㄒoㄒ)/~~

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图