导读: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>提供宏、类型以及支持多线程的函数。下面是宏、类型和枚举常量的摘要总结:
宏:
- thread_local,ONCE_FLAG,TSS_DTOR_ITERATIONScnd_tthrd_t,tss_t,mtx_t,tss_dtor_t,thrd_start_t,once_flag。
通过枚举常量:
- mtx_init:mtx_plain,mtx_recursive,mtx_timed。
线程枚举常量:
- thrd_timedout,thrd_success,thrd_busy,thrd_error,thrd_nomem。
条件变量的函数:
- call_once(once_flag*flag,void(*func)(void));
- cnd_broadcast(cnd_t*cond);
- cnd_destroy(cnd_t*cond);
- cnd_init(cnd_t*cond);
- cnd_signal(cnd_t*cond);
- cnd_timedwait(cnd_t*restrictcond,mtx_t*restrictmtx,conststructtimespec*restrictts);
- cnd_wait(cnd_t*cond,mtx_t*mtx);
互斥函数:
- voidmtx_destroy(mtx_t*mtx);
- intmtx_init(mtx_t*mtx,inttype);
- intmtx_lock(mtx_t*mtx);
- intmtx_timedlock(mtx_t*restrictmtx;
- conststructtimespec*restrictts);
- intmtx_trylock(mtx_t*mtx);
- intmtx_unlock(mtx_t*mtx);
线程函数:
- intthrd_create(thrd_t*thr,thrd_start_tfunc,void*arg);
- thrd_tthrd_current(void);
- intthrd_detach(thrd_tthr);
- intthrd_equal(thrd_tthr0,thrd_tthr1);
- noreturnvoidthrd_exit(intres);
- intthrd_join(thrd_tthr,int*res);
- intthrd_sleep(conststructtimespec*duration,structtimespec*remaining);
- voidthrd_yield(void);
特定于线程的存储功能:
- inttss_create(tss_t*key,tss_dtor_tdtor);
- voidtss_delete(tss_tkey);
- void*tss_get(tss_tkey);
- inttss_set(tss_tkey,void*val);
这些标准库函数可能更适合作为易用的API的基础而不是开发平台来使用。例如,使用这些低级库的函数时,很容易造成数据竞争,多个进程会不同步地对同一个位置的数据进行操作。C(和C++)标准允许任何行为,即使会发生争用同一个变量x,哪怕会导致严重的后果。例如,多字节值x可能被一个线程修改部分字节,而另一个线程又会修改别的部分(值撕裂),或者产生一些其它的副作用。
下面一个简单的示例程序,它包含一个数据竞争,其中64位的整数x会同时被两个线程改动。
- #include<threads.h>
- #include<stdio.h>
- #defineN100000
- charbuf1[N][99]={0},buf2[N][99]={0};
- longlongold1,old2,limit=N;
- longlongx=0;
- staticvoiddo1(){
- longlongo1,o2,n1;
- for(longlongi1=1;i1<limit;++i1){
- old1=x,x=i1;
- o1=old1;o2=old2;
- if(o1>0){
- if(o1!=i1-1)
- sprintf(buf1[i1],"thread1:o1=%7lld,i1=%7lld,o2=%7lld",
- o1,i1,o2);
- }else{
- n1=x,x=i1;
- if(n1<0&&n1>o1)
- sprintf(buf1[i1],"thread1:o1=%7lld,i1=%7lld,n1=%7lld",
- o1,i1,n1);
- }
- }
- }
- staticvoiddo2(){
- longlongo1,o2,n2;
- for(longlongi2=-1;i2>-limit;--i2){
- old2=x,x=i2;
- o1=old1;o2=old2;
- if(o2<0){
- if(o2!=i2+1)
- sprintf(buf2[-i2],"thread2:o2=%7lld,i2=%7lld,o1=%7lld",
- o2,i2,o1);
- }else{
- n2=x,x=i2;
- if(n2>0&&n2<o2)
- sprintf(buf2[-i2],"thread2:o2=%7lld,i2=%7lld,n2=%7lld",
- o2,i2,n2);
- }
- }
- }
- intmain(intargc,char*argv[])
- {
- thrd_tthr1;
- thrd_tthr2;
- thrd_create(&thr1,do1,0);
- thrd_create(&thr2,do2,0);
- thrd_join(&thr2,0);
- thrd_join(&thr1,0);
- for(longlongi=0;i<limit;++i){
- if(buf1[i][0]!='\0')
- printf("%s\n",buf1[i]);
- if(buf2[i][0]!='\0')
- printf("%s\n",buf2[i]);
- }
- return0;
- }
如果你的应用已经符合C11的标准,并且将它在一个32位的机器编译过了(在64位机器里long long类型会占用两倍以上的存储周期),你将会看到数据竞争的结果,得到像下面乱码一样的输出:
- thread2: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的那行语句:
- #include<stdatomic.h>
- atomic_llongx=ATOMIC_VAR_INIT(0);
通过使用这样的atomic变量,代码在运行中不会出现任何数据竞争的问题。
注意关键字
C委员会并不希望在用户命名空间里创建新的关键字,每个新的C版本都一直在避免出现不兼容之前版本C程序的问题。相比之下,C++委员会(WG21)更喜欢创造新的关键字,例如:C++11定义一个新的thread_local关键字来指定一个线程的本地静态存储,C11使用_Thread_local来代替,在C11新的头文件<threads.h>中有一个宏定义来提供normal-looking name:
- #definethread_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++定义宏中编译。
- #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的变更可以参考维基百科
(本文为CSDN编译整理,未经允许不得转载。如需转载请联系market@csdn.net)
相关文章:
ISO发布C语言标准新版本
C语言中史上最愚蠢的Bug
如何创建比C语言更快的编程语言?
分享到:
相关推荐
C11标准,全称为ISO/IEC 9899:2011,是国际标准化组织(ISO)和国际电工委员会(IEC)共同发布的C语言编程语言的最新规范。这个标准引入了新的特性,提高了程序的安全性和效率,并对原有的语法和库函数进行了修订。 ...
C语言规范之C11标准是C语言的最新官方版本,由国际标准化组织(ISO)在2011年发布,因此得名C11。这个标准是对C99的更新,引入了一系列新特性,旨在增强C语言的功能性和安全性。在深入探讨C11标准之前,我们首先需要...
国际标准化组织(ISO)和国际电工委员会(IEC)负责维护该标准,并鼓励收到该草案的人员提交他们的评论和相关专利通知,同时提供支持文件。 从之前草案N1539的版本中,C11标准所作的更改在文档右侧的边缘处用...
每个标准的发布都伴随着技术委员会的修订和解释文档,例如"n1124"是C99标准的一个版本,包含了技术修订1(TC1)和技术修订2(TC2)。"The rationale for the C99 standard"提供了制定C99标准时的考虑和决策过程,这...
This International Standard specifies the form and establishes the interpretation of programs expressed in the programming language C. Its purpose is to promote portability
本书基于C语言的最新标准ISO/IEC 9899:2011-C11标准,力求全面地介绍这门计算机语言的各个方面:词法元素、类型、声明、表达式、语句等,全书内容按概念和术语分类组织,示例丰富,查阅方便,适合具有一定C语言基础...
C99和C11标准的发布为C语言带来了许多现代化的特性,这些特性不仅提高了编程的灵活性和效率,也使得C语言能够更好地适应新的编程需求。通过理解和掌握这些新特性,开发者可以编写出更加高效、可维护的C代码。
C11标准及C语言标准(和部分非标准)函数手册
2011年12月8日,ISO正式公布C语言新的国际标准草案:ISO/IEC 9899:2011,即C11 这是最新的C语言参考手册
C语言编程魔法书:基于C11标准,AZW3格式下载。 C语言编程魔法书:基于C11标准,AZW3格式下载。
根据提供的文件内容,可以生成关于C11标准文档的知识点,主要包括了C语言标准的介绍、C语言及其标准库的规范、对C程序的环境、执行库内容以及对C程序可移植性、可靠性、可维护性和效率执行的促进。 1. C11标准介绍...
这个压缩包包含了C语言的三个重要版本——C89/C90,C99,C11的标准文档,这些文档是理解C语言规范、编写标准合规代码的关键参考资料。 1. **C89/C90标准**:这是C语言的第一个官方标准,由国际标准化组织(ISO)在...
从技术上讲,C11标准与之前的草案(N1539)相比,新标准在右侧边缘用“diffmarks”标记了变化,用“*”标记删除的文本,用“‘”标记新增或改动的文本。通过这种方式,C11标准对代码的编写和程序的执行提出了更为...
ISO/IEC 9899:2011标准,又称C11标准,是ISO(国际标准化组织)和IEC(国际电工委员会)联合制定的C语言的最新标准,正式发布于2011年。C11标准是对1999年发布的C99标准的进一步更新,引入了一些新的功能和改进,以...
C99是C语言的第三个官方标准,由国际标准化组织(ISO)和国际电工委员会(IEC)于1999年发布。这个标准对C89(也称为ANSI C)进行了许多更新和扩展,主要包含以下关键点: 1. **类型安全**:引入了`stdint.h`头文件...
语法扩展篇(第17~19章)讲述了GCC与Clang编译器对C语言的扩展,以及C语言的设计理念、未来C语言新添与排除特性。需要对底层交互与开发深入了解的读者大有裨益。 项目实践篇(第20~21章),UTF-8与UTF-16编码程序...
C85、C95、C99、C11和C17是C语言的不同版本标准,这些标准定义了语言的语法、特性以及程序员应遵循的规则。下面我们将分别探讨这些版本的主要特点和更新内容。 1. C85(通常指的是C89): 这是C语言的第一个正式...
2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC) 旗下的C语言标准委员会(ISO/IEC JTC1/SC22/WG14)正式发布了C11标准 。 C11标准的最终定稿的草案是免费开放的,为N1570 ,但是正式标准文件需要198...
C99和C11是两个关键的C语言国际标准版本,分别发布于1999年和2011年,由国际标准化组织ISO(International Organization for Standardization)和IEC(International Electrotechnical Commission)联合制定。...
C89/C90是第一个国际标准,由国际标准化组织(ISO)和国际电工委员会(IEC)在1989年发布,1990年正式生效。这个标准奠定了C语言的基础,包括基本数据类型、控制结构、函数、指针、预处理器等核心概念。C99是重要的...