C11标准委员会成员解读C语言新标准

导读:C语言国际标准新的新草案之前已经公布,新标准提高了对C++的兼容性,并将新的特性增加到C语言中。此外支持多线程的功能也受到了开发者的关注,基于ISO/IEC TR 19769:2004规范下支持Unicode,提供更多用于查询浮点数类型特性的宏定义和静态声明功能。根据草案规定,最新发布的标准草案修订了许多特性,支持当前的编译器。(背景:C编程语言的标准化委员会(ISO/IEC JTC1/SC22/WG14)已完成了C标准的主要修订,该标准的早期版本于1999年完成,俗称为“C99”。新标准在去年年底完成,也被称为“C11”。)

本文作者Tom Plum是Plum Hall Inc.的技术工程副总监,也是C11和C++11标准委员会的成员,他在这篇文章里从语言集的atomic操作和线程原语开始探讨了C语言的新特性,在文章末尾还讨论了C11与C++兼容性问题。此外,在本文和它的姊妹篇里,作者还描述了C11的新功能、并发性、安全性和易用性等话题。

并发

C11的标准化了可能运行在多核平台上的多线程程序的语义,使用atomic变量让线程间通信更轻量。

头文件<threads.h>提供宏、类型以及支持多线程的函数。下面是宏、类型和枚举常量的摘要总结:

    宏:

  1. thread_local, ONCE_FLAG, TSS_DTOR_ITERATIONS cnd_t thrd_t, tss_t, mtx_t, tss_dtor_t, thrd_start_t, once_flag。 

    通过枚举常量:

  1. mtx_init: mtx_plain, mtx_recursive, mtx_timed。 

    线程枚举常量:

  1. thrd_timedout, thrd_success, thrd_busy, thrd_error, thrd_nomem。 

    条件变量的函数:

  1. call_once(once_flag *flag, void (*func)(void)); 
  2. cnd_broadcast(cnd_t *cond); 
  3. cnd_destroy(cnd_t *cond); 
  4. cnd_init(cnd_t *cond); 
  5. cnd_signal(cnd_t *cond); 
  6. cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mtx, const struct timespec *restrict ts); 
  7. cnd_wait(cnd_t *cond, mtx_t *mtx); 

    互斥函数:

  1. void mtx_destroy(mtx_t *mtx); 
  2. int mtx_init(mtx_t *mtx, int type); 
  3. int mtx_lock(mtx_t *mtx); 
  4. int mtx_timedlock(mtx_t *restrict mtx; 
  5. const struct timespec *restrict ts); 
  6. int mtx_trylock(mtx_t *mtx); 
  7. int mtx_unlock(mtx_t *mtx); 

    线程函数:

  1. int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); 
  2. thrd_t thrd_current(void); 
  3. int thrd_detach(thrd_t thr); 
  4. int thrd_equal(thrd_t thr0, thrd_t thr1); 
  5. noreturn void thrd_exit(int res); 
  6. int thrd_join(thrd_t thr, int *res); 
  7. int thrd_sleep(const struct timespec *duration, struct timespec *remaining); 
  8. void thrd_yield(void); 

    特定于线程的存储功能:

  1. int tss_create(tss_t *key, tss_dtor_t dtor); 
  2. void tss_delete(tss_t key); 
  3. void *tss_get(tss_t key); 
  4. int tss_set(tss_t key, void *val); 

这些标准库函数可能更适合作为易用的API的基础而不是开发平台来使用。例如,使用这些低级库的函数时,很容易造成数据竞争,多个进程会不同步地对同一个位置的数据进行操作。C(和C++)标准允许任何行为,即使会发生争用同一个变量x,哪怕会导致严重的后果。例如,多字节值x可能被一个线程修改部分字节,而另一个线程又会修改别的部分(值撕裂),或者产生一些其它的副作用。

下面一个简单的示例程序,它包含一个数据竞争,其中64位的整数x会同时被两个线程改动。

  1. #include <threads.h> 
  2. #include <stdio.h> 
  3. #define N 100000 
  4. char buf1[N][99]={0}, buf2[N][99]={0}; 
  5. long long old1, old2, limit=N; 
  6. long long x = 0; 
  7.   
  8. static void do1()  { 
  9.    long long o1, o2, n1; 
  10.    for (long long i1 = 1; i1 < limit; ++i1) { 
  11.       old1 = x, x = i1; 
  12.       o1 = old1;  o2 = old2; 
  13.       if (o1 > 0) { // x was set by this thread 
  14.          if (o1 != i1-1) 
  15.             sprintf(buf1[i1], "thread 1: o1=%7lld, i1=%7lld, o2=%7lld"
  16.                      o1, i1, o2); 
  17.       } else {      // x was set by the other thread 
  18.          n1 = x, x = i1; 
  19.          if (n1 < 0 && n1 > o1) 
  20.             sprintf(buf1[i1], "thread 1: o1=%7lld, i1=%7lld, n1=%7lld"
  21.                      o1, i1, n1); 
  22.       }         
  23.    } 
  24.   
  25. static void do2()  { 
  26.    long long o1, o2, n2; 
  27.    for (long long i2 = -1; i2 > -limit; --i2) { 
  28.       old2 = x, x = i2; 
  29.       o1 = old1; o2 = old2; 
  30.       if (o2 < 0) { // x was set by this thread 
  31.          if (o2 != i2+1) 
  32.             sprintf(buf2[-i2], "thread 2: o2=%7lld, i2=%7lld, o1=%7lld"
  33.                      o2, i2, o1); 
  34.       } else {      // x was set by the other thread 
  35.          n2 = x, x = i2; 
  36.          if (n2 > 0 && n2 < o2) 
  37.             sprintf(buf2[-i2], "thread 2: o2=%7lld, i2=%7lld, n2=%7lld"
  38.                      o2, i2, n2); 
  39.       } 
  40.    } 
  41.   
  42. int main(int argc, char *argv[]) 
  43.    thrd_t thr1; 
  44.    thrd_t thr2; 
  45.    thrd_create(&thr1, do1, 0); 
  46.    thrd_create(&thr2, do2, 0); 
  47.    thrd_join(&thr2, 0); 
  48.    thrd_join(&thr1, 0); 
  49.    for (long long i = 0; i < limit; ++i) { 
  50.       if (buf1[i][0] != '\0'
  51.          printf("%s\n", buf1[i]); 
  52.       if (buf2[i][0] != '\0'
  53.          printf("%s\n", buf2[i]); 
  54.    } 
  55.    return 0; 
  56. }  

如果你的应用已经符合C11的标准,并且将它在一个32位的机器编译过了(在64位机器里long long类型会占用两倍以上的存储周期),你将会看到数据竞争的结果,得到像下面乱码一样的输出:

  1. thread 2: o2=-4294947504, i2=    -21, o1=  19792 

传统的解决方案是通过创建一个锁来解决数据竞争。然而,用atomic数据有时会更高效。加载和存储atomic类型循序渐进、始终如一。特别是如果线程1存储了一个值名为x的atomic类型变量,线程2读取该值时则可以看到之前在线程1中运行的所有其它存储(即使是非atomic对象)。(C11和C++11标准还提供其他内存一致性的模型,虽然专家提醒要远离它们。)

新的头文件<stdatomic.h>提供了很多命名类和函数来操作atomic数据的大集。例如,atomic_llong就是一个为操作atomic long long整数的类型。所有的整数类都提供了相似的命名。该标准包括一个ATOMIC_VAR_INIT(n)宏,用来初始化atomic整数,如下:

之前的数据竞争的例子可以通过定义x为一个atomic_llong类型的变量解决。简单地改变一下上述例子中声明x的那行语句:

  1. #include <stdatomic.h> 
  2. atomic_llong x = ATOMIC_VAR_INIT(0); 

通过使用这样的atomic变量,代码在运行中不会出现任何数据竞争的问题。

注意关键字

C委员会并不希望在用户命名空间里创建新的关键字,每个新的C版本都一直在避免出现不兼容之前版本C程序的问题。相比之下,C++委员会(WG21)更喜欢创造新的关键字,例如:C++11定义一个新的thread_local关键字来指定一个线程的本地静态存储,C11使用_Thread_local来代替,在C11新的头文件<threads.h>中有一个宏定义来提供normal-looking name:

  1. #define   thread_local    _Thread_local 

下面我将假设你已经引入例如适当的头文件,所以我会显示normal-looking name。

thread_local存储类

新thread_local存储类为每个线程提供一个单独的静态存储,并且在线程运行之前初始化。但有没有保障措施来防止你捕获一个thread_local变量的地址,并把它传递给其他线程,然后明确实现(即,不便携/不可移植),每个线程在thread_local都有它自己的errno存储副本。

线程可选

C11已指定为多种特色为可选功能,例如:如果它明确实现了一个名为 _ _STDC_NO_THREADS_ _ 的宏,就不会提供一个头名为<threads.h>的头文件,也不可能在其中定义任何函数。

设计准则

作为通则,WG21委托Bjarne Stroustrup整体设计和进化的责任,不明白的话可以在网上搜索“camel is a horse designed by committee”。然而,有一个WG14和WG21同样遵循的设计原则:不要给任何系统编程语言比我们(C/C++)更高效的机会!

一些与会者(称他们为“A组”)认为atomic数据仍将是一个很少使用的专业性功能,但其他人(称他们为“B组”)认为,atomic数据将成为一个重要的功能,至少会在一种系统编程语言中被应用。

在过去的几十年里,很多更高级别的语言都是基于C语言创建的(Java,C#,ObjectiveC,当然,也包括C++)和C++子集或超集(如D和嵌入式C++)。许多参与WG14和WG21的公司已经决定使用这些语言作为自己的应用的编程语言(称他们为“C组”)。这些公司之所以选择C++作为他们的上层应用程序的语言,通常是因为C足够稳定(或者说是因为WG21控制其标准化),而选择其他语言的公司(称他们为“D组”)通常认为C是他们所使用的高级语言非常重要的基石。

有了这些背景,我可以得出一个C11中atomic进化的理由。C++11中对atomic的设计充分运用了模板。比如atomic<T>是获得任何一种类型T的简单且常用的方法。即使T是一个类或结构,那么无论T*指向哪儿,atomic<T*>都会保存类型信息,但是,几年来,C语言的设计依然只用了几十种命名类型(如上所示的atomic_llong)。这样做的主要优点在于:它不需要编译器做任何改变。它能够实现一个仅库的解决方案,会在最低水平调用系统依赖的原生函数。然而命名类型的解决方案干挠了为C结构或者T*指针创建atomic(无论多么小)。主要因为B组和D组的意见,WG14决定要求C11编译器为任何类型的T识别atomicT。

后来也有一个WG14内部的关于编译器识别atomicT语法的争论。一种使用atomic-parenthesis的解决方案因为和C++良好的兼容性而被推动。Let _Atomic(T)成为了指定atomicT的语法。同样的源程序能够简单地在C++定义宏中编译。

  1. #define _Atomic(T)    atomic<T> 

另一种相反的观点认为应该创建新的类限制符(类似于C99的_Complex的解决方案);使用"atomic-space"语法,atomic T类型应该被写为“_Atomic T”。使用这种语法的程序型的程序无法立刻被当作C++来编译。(无兼容性宏,本质上看起来像atomic-parenthesis的方法)。

获取C11标准

新标准可以在webstore.ansi.org查看(或者搜索"ISO/IEC 9899:2011")。现在已经有了PDF文档,但是需要支付$285(和C++2011一样)才可以使用。一旦这些标准被ANSI采纳为美国国家标准,价格将下降至$30左右。

你可以定期检查此页面的部分,我及时检查草案的状态,如果您填写了这个Web表单,将会在C11和C++11被ANSI采用为标准时获得电子邮件通知。也可以通过tplum@plumhall.com联系我,非常感谢来自Pete Becker(C++2011标准的项目编辑)的建设性建议。

原文链接:drdobbs.com

更多关于C11的变更可以参考维基百科


相关文章:

ISO发布C语言标准新版本

C语言中史上最愚蠢的Bug

如何创建比C语言更快的编程语言?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450700.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何将拷贝过来的数据 *.ibd 文件生效

1.将拷贝的数据文件 "qqq.idb"放在自己的数据库中. 一般存放在 mysql/ data/ databasename 下 2. "qqq.idb" 改个名字-->"qqq--.idb", 主要是避免冲突&#xff01; 3.执行 create table qqq(...) 语句&#xff0c;此时除了会生成一个 qqq…

windows 下启动zookeeper的zkServer.cmd服务闪退

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 解决方案把conf目录下的默认zoo_sample.cfg的文件名字改成zoo.cfg 即可&#xff1a; 以上转自&#xff1a;http://blog.csdn.net/qq4960…

设计模式常见面试真题详解

文章目录1. 设计模式1.1 说一说设计模式的六大原则1.2 说一下六大原则中的开闭原则1.3 手写一个单例模式1.4 手写一个线程安全的单例模式1.5 说一说你对工厂模式的理解1.6 简单工厂模式和抽象工厂模式有什么区别&#xff1f;1.7 如何实现工厂模式&#xff1f;1.8 说一说你策略模…

Android内存优化之内存泄漏

内存泄漏 内存泄漏一般有以下几种情况&#xff1a;单例、静态变量、Handler、匿名内部类、资源使用未关闭 单例导致的内存泄漏 单例的情况主要是因为单例的生命周期比较长&#xff0c;如果引用的一些资源&#xff08;比如Context、图片等&#xff09;没有做特殊处理&#xff0c…

cmd - 使用curl命令的注意点

前言 最近在cmd中使用curl命令来测试rest api&#xff0c;发现有不少问题&#xff0c;这里记录一下。 在cmd中使用curl命令的注意事项 json不能由单引号包括起来json数据里的双引号要用反斜杠\转义json数据里不能带有空格如果想要在json数据里使用空格则必须用双引号将整个json…

指针常见定义

再给出常用的C变量的定义方式&#xff1a;a) 一个整型数&#xff08;An integer&#xff09; b) 一个指向整型数的指针&#xff08;A pointer to an integer&#xff09; c) 一个指向指针的的指针&#xff0c;它指向的指针是指向一个整型数&#xff08;A pointer to a pointer …

场景应用题目常见面试真题详解

文章目录1. 场景应用1.1 微信红包相关问题1.2 秒杀系统相关问题1.3 扫码登录流程1.4 如何实现单点登录&#xff1f;1.5 如何设计一个本地缓存&#xff1f;1. 场景应用 1.1 微信红包相关问题 参考答案 概况&#xff1a;2014年微信红包使用数据库硬抗整个流量&#xff0c;2015…

后Kubernetes时代的微服务

\本文要点\\当前微服务架构依然是最流行的分布式系统架构风格。Kubernetes和云原生运动已大规模地重新定义了应用设计和开发中的一些方面。\\t在云原生平台上&#xff0c;服务仅具备可观测性是不够的。更基本的先决条件是使用检查健康、响应信号、声明资源消耗等手段实现微服务…

Dynamics CRM On-Premise V9安装手记

下载地址&#xff1a; https://download.microsoft.com/download/A/D/D/ADDD6898-4EFA-46FA-80B6-6FE9A3CDED63/CRM9.0-Server-CHS-amd64.exe 安装支持Windows 2016 及SQL Server 2016 SP2以上版本 我想安装了All in one的&#xff0c;就想着用最新的SQLServer 2017&#xff0c…

金山网络CEO傅盛:简约之美

摘要&#xff1a;金山网络CEO傅盛带来了主题为《简约之美》的精彩演讲。他表示由于时代的变迁&#xff0c;红海的竞争&#xff0c;项目的需求等原因&#xff0c;若想项目取得成功&#xff0c;唯有简单才是王道&#xff0c;唯有简单定位才能深入人心。那么&#xff0c;如何做到简…

zookeeper安装和使用 windows环境

简介 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;…

计算机网络常见面试真题详解

文章目录1. 计算机网络1.1 请介绍七层网络体系结构。1.2 请介绍五层网络体系结构。1.3 了解网络编程协议吗&#xff1f;客户端发送给服务器的请求&#xff0c;怎么确定具体的协议&#xff1f;1.4 TCP、HTTP、FTP分别属于哪一层&#xff1f;1.5 讲一下TCP/IP协议。1.6 说一说你对…

2018.09.14python学习第四天part2

流程控制之while循环 1.什么是循环&#xff1f;&#xff08;what&#xff09; 循环是指重复做某一件事 2.为何要有循环&#xff1f;&#xff08;why&#xff09; 为了让计算机能像人一样重复去做某一件事 3.如何使用循环&#xff1f;&#xff08;how&#xff09; #语法一&#…

git操作指令合集

1.下载完git&#xff0c;需要输入用户名和邮箱 git config --global user.name "Your Name" git config --global user.email "emailexample.com" 注意git config命令的--global参数&#xff0c;用了这个参数&#xff0c;表示这台电脑上所有的GIt仓库都会使…

C++回调函数(callback)的使用

什么是回调函数(callback) 模块A有一个函数foo&#xff0c;它向模块B传递foo的地址&#xff0c;然后在B里面发生某种事件&#xff08;event&#xff09;时&#xff0c;通过从A里面传递过来的foo的地址调用foo&#xff0c;通知A发生了什么事情&#xff0c;让A作出相应反应。 那么…

Hibernate JPA中@Transient、@JsonIgnoreProperties、@JsonIgnore、@JsonFormat、@JsonSerialize等注解解释

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1、Transient Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性&#xff1b; 如果一个属性并非数据库表的字段…

可爱的rem

前端开发中&#xff0c;移动端的开发可以说是举足轻重了&#xff0c;可是又面临着不同设备尺寸和分辨率的尴尬点。今天[2018-09-16]台风山竹登陆广东&#xff0c;来势汹汹&#xff0c;外出是不可能的了&#xff0c;那就宅着写写这篇小文章吧...原文请戳这里-谈谈rem单位 超长的…

kafka直连方式消费多个topic

一个消费者组可以消费多个topic&#xff0c;以前写过一篇一个消费者消费一个topic的&#xff0c;这次的是一个消费者组通过直连方式消费多个topic,做了小测试&#xff0c;结果是正确的&#xff0c;通过查看zookeeper的客户端&#xff0c;zookeeper记录了偏移量 package day04 /…

100个经典的C语言算法

100个经典的C算法 C语言的学习要从基础开始&#xff0c;这里是100个经典的算法 题目&#xff1a;古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔 子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;…

MySQL常见面试题目详解

文章目录1. SQL1.1 介绍一下数据库分页1.2 介绍一下SQL中的聚合函数1.3 表跟表是怎么关联的&#xff1f;1.4 说一说你对外连接的了解1.5 说一说数据库的左连接和右连接1.6 SQL中怎么将行转成列&#xff1f;1.7 谈谈你对SQL注入的理解1.8 将一张表的部分数据更新到另一张表&…