| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 4494 人关注过本帖
标题:[轉供參考] C11: 致力于更安全编程的新C标准
只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
结帖率:100%
收藏
 问题点数:0 回复次数:5 
[轉供參考] C11: 致力于更安全编程的新C标准
C11: 致力于更安全编程的新C标准

2014-09-13 16:05:38


本文简要翻译自http://blog. 转载请注明出处.

C11标准化了许多当前主流编译器已经实现了的特性,同时定义了更加适合多线程的内存模型. 换句话说C11是一个更好的C.

C99标准中的问题

C99标准带来了许多新特性,包括:
• 变长数组(Variable length arrays)
• 指定初始化(Designated initializers)
• 泛型数学库
• 新的数据类型: long long, _Complex, _Bool
• restrict指针
• 变量混合声明
• 内联函数
• 单行风格的注释

尽管如此,C99标准依然没能取得巨大成功,即使在今天,兼容C99的实现也是个挑战.
C99标准到底何去何从? 它的一些强制特性被证明在某些平台上很难实现,其它特性也受到了广泛的质疑或被认为是实验性质的以至于某些厂商甚至建议C程序员转向使用C++.

C99不受欢迎, 政治也在其中扮演了角色. 最起码, 在20世纪90年代, C和C++两个委员会之间缺乏合作是个公开的秘密. 好消息是今天这两个委员会之间的合作更好了, C11得以避免C99标准上的设计失误.

新的标准, 新的希望?

C的安全一直是令人关心的问题. 其不安全的特性总是恶意代码的温床, 例如不检查边界的字符串操作函数和不检查参数的文件I/O函数.

C11使用一组更安全的标准函数来解决这些问题, 这组新函数意在替换传统的不安全的函数(尽管后者在C11里依然可用). 此外, C11还包含Unicode支持, 遵循IEC 60559的浮点运算以及IEC 60559的复数运算, 内存对齐工具集, 匿名结构和联合, _Noreturn 函数修饰符, 以及最重要的 – 多线程支持. 没错,我说的就是m-word! (Yes, I said the m-word)

让我们更进一步看看其中一些特性以及其它特性.

多线程

对于普通C程序员来说,C11的最大变化就是对多线程的支持进行了标准化. C当然已经支持多线程且有数十个年头了. 然而, 所有当前流行的C线程库都有很多非标准的扩展,因此变得不可移植.

新的C11头文件<threads.h>声明了创建和管理线程, 信号, 条件变量的函数, 以及_Atomic类型限定符. 另一个新的头文件<stdatomic.h>声明了不可中断对象访问工具. 最后, C11引入一个新的存储类修饰符_Thread_local (等价于C++11 thread_local). 声明为_Thread_local的变量不在多线程之间共享. 确切的说, 每个线程持有变量单独的拷贝.


作为一个内幕, 如果你在正在找一个人为笨拙的关键字_Thread_local负责的话, 就找我吧. 在21世纪早期, 当C++标准委员会致力于多线程支持的时候, 最初的线程本地存储的提议是使用关键字__thread , 由于不能清楚的表达该关键字的意图,因此我认为这既不安全也不易懂 (毕竟, __thread并不创建线程!), 并且还可能碰巧和旧的使用__thread修饰用户声明的标识符的代码冲突. 我建议把__thread改为thread_local并且被接受了. 此后thread_local渗透进了其它编程语言, 包括C11. 礼物和恐吓邮件都是受欢迎的!

C11另一个线程相关的特性是quick_exit()函数, 该函数可以让你在exit()函数不起作用时终止程序, 例如当线程的cooperative cancellation是不可能的时候. quick_exit()函数确保使用at_quick_exit()注册的函数以它们注册顺序的相反顺序被调用. 然后at_quick_exit()调用_Exit(), 同exit()相比该函数不会刷新进程文件描述符.

匿名structs和union

匿名struct或者union既没有标签名也没有类型定义名. 它对嵌套的组合有用, 例如, 结构体的联合成员. 下面的C11代码声明了一个带有匿名union的成员并直接访问了union中的数据成员:



点击(此处)折叠或打开

1. struct T//C++,C11
 
2. {

3.     intm;

4.     union//anonymous

5.     {

6.         char*index;

7.         intkey;

8.     };

9. };

10. struct T t;

11. t.key=1300;//access the union's member directly

泛型函数

C11依然不支持模板, 但是它支持通过一种基于宏的方法定义“泛型”函数. 新的关键字_Generic用于声明一个泛型表达式, 其可以转换成类型相关的“特化”版本

在下面的例子中, 泛型的立方根计算宏cbrt(X)根据实际参数X的类型判定特化为cbrtl(long double), cbrtf(float) 以及默认的 cbrt(double):




点击(此处)折叠或打开

1. //C11 only
 
2. #define cbrt(X)_Generic((X),long double:cbrtl,

3.                               default:cbrt,

4.                               float:cbrtf)(X)

它是如何工作的呢? 首先参数X 转换为函数参数的特定类型.然后编译器选择匹配的cbrt()变体: 如果X是long double类型为cbrtl(), float类型为cbrtf(), 其它为cbrt().

内存对齐控制

同C++11相似, C11引入了探查以及强制变量和类型的内存对齐工具. _Alignas关键字指明了类型或者对象的对齐请求. alignof 操作符报告它的操作数是否是对齐的. 最后, 函数aligned_alloc()





点击(此处)折叠或打开

1. void*aligned_alloc(size_t algn,size_t size);

分配size字节以algn字节对齐的内存,并返回指向所分配内存的指针.

C11的对齐特性是在新的头文件中<stdalign.h>声明的.

_Noreturn函数修饰符

_Noreturn 声明的函数不会返回. 引入此新的函数修饰符有两个目的:
• 消除编译器对没有return的函数的警告.  
• 允许某种只针对不返回函数的优化.





点击(此处)折叠或打开

1. _Noreturn void func();//C11,func never returns

Unicode支持

Unicode标准定义了三种编码格式: UTF-8, UTF-16和UTF-32. 每个都有优点和缺点. 目前, 程序员使用char编码UTF-8, unsigned short或者wchar_t编码UTF-16, unsigned long或者wchar_t编码UTF-32. C11 摒弃了这些hack表示法, 引入了两种新的具有平台独立宽度的数据类型: char16_t和char32_t, 分别用于UTF-16 以及UTF-32(UTF-8像以前一样使用char). C11 也提供u 和 U前缀来表示Unicode字符串, u8前缀表示UTF-8编码的文字. 最后, Unicode转换函数在头文件<uchar.h>中声明.

静态断言

不像#if和#error 预处理指令, 静态断言在后面的翻译阶段当知道表达式的类型的时候被评估的. 因此, 静态断言能够让你捕获预理阶段检测不到的错误.

范围检查的函数

技术报告24731-1现在是C11的不可或缺的一部分, 其定义了标准C库字符串操作函数的边界检查版本. 边界检查版本在原始函数名称后加上_s后缀.

举例来说, strcat()和strncpy() 边界检查版本分别是strcat_s()和strncpy_s(). 大多数边界检查函数都带有一个额外的参数指示了所需处理的缓冲区大小. 其中的许多函数同时也执行额外的运行期检查来检测不同的运行期异常.

让我们来看下两个众所周知的字符串操作函数:





点击(此处)折叠或打开

1. //C11,safe version of strcat
 
2. errno_t strcat_s(char*restrict s1,rsize_t s1max,constchar*restrict s2);

strcat_s()拷贝不超过s1max字节到s1. 第二个函数strcpy_s()要求s1max应该大于s2 的长度(更准确的说, s1max应该大于strnlen_s(s2, s1max)) 以防越界写操作:





点击(此处)折叠或打开

1. //C11,safe version of strcpy
 
2. errno_t strcpy_s(char*restrict s1,rsize_t s1max,constchar*restrict s2);

最初, 所有支持边界检查的库都是由微软Visual C++团队开发的. C11实现了一份近似但不完全一致的版本.



移除gets()函数

gets() (在<stdio.h>中声明) 函数从标准输入读取一行并存入调用者提供的缓冲区中. gets()函数不清楚这个缓冲区的实际大小. 恶意程序和攻击者常常利用这个漏洞进行缓冲区溢出攻击. 因此, 在C99标准中gets()函数就被声明为不赞成的. C11干脆整个移除了它并用更安全的版本gets_s()函数取代了它:





点击(此处)折叠或打开

1. char*gets_s(char*restrict buffer,size_t nch);

gets_s()最多从标准输入读取nch个字符.

新fopen()接口

广泛使用的文件I/O函数fopen()在C11中得到了增强.fopen()现在支持新的独占的创建-打开模式(“...x“). 新模式的行为就像POSIX中的O_CREAT|O_EXCL, 通常用于锁文件. “...x” 族模式包括下面几个选项:
• wx 以独占访问创建用于写入的文本文件.
• wbx 以独占访问创建用于写入的二进制文件.
• w+x 以独占访问创建用于更新的文本文件.
• w+bx或wb+x 以独占访问创建用于更新的二进制文件.

使用上面任何一种独占模式打开已存在或者不能被创建的文件都会失败. 否则, 文件会以独占(不共享)访问模式被创建 . 此外, 更安全的fopen()版本fopen_s()也是可用的.

结论

C11试图修复C99中令人沮丧的特性. 它使C99的一些强制特性(变长数组, 复合类型等等) 变成了可选项, 引入了在不同的实现中已经可用的新特性. 并非不太重要的是, C11的设计者们和C++标准委员会紧密合作以确保尽可能保持两种语言的兼容性. 机遇是良好的, 不像它的前任, C11将受到广发的欢迎. 作为奖励, 用C11写的软件将在安全漏洞和恶意软件的攻击面前更加健壮.

See also:
• Understanding The Open Web Stack
• 7 Reasons that Rexx Still Matters
• 14 Ways to Contribute to Open Source without Being a Programming Genius or a Rock Star

Danny Kalev 是通过以色列系统分析师协会认证的系统分析师, 并且是专攻C++的软件工程师. Kalev 写了多本C++的书籍,同时给不同的软件开发者站点投搞C++文章. 他是C++标准委员会的成员, 还获得了通用语言学的硕士学位.

原始鏈接:http://blog.
搜索更多相关主题的帖子: Complex 编译器 多线程 模型 数学 
2015-02-03 23:41
Aliali
Rank: 2
等 级:论坛游民
威 望:1
帖 子:40
专家分:36
注 册:2015-1-31
收藏
得分:0 
2015-02-03 23:48
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
曾經有不少人不屑vc的_s安全類型函數,說那是微軟的私貨,死活要用“標準”的原始版本,但C11卻引入這類東西,不知他們怎麽說。還有那個變長數組(VLA)也是一樣,我當時就不以爲然,可以參考我另外轉過來的資料。

[ 本帖最后由 TonyDeng 于 2015-2-4 10:24 编辑 ]

授人以渔,不授人以鱼。
2015-02-03 23:50
rjsp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:528
帖 子:9030
专家分:54050
注 册:2011-1-18
收藏
得分:0 
回复 3楼 TonyDeng
因为这次 _s 类函数就是微软的代表提交的,遗憾地说,C标准需要微软。

第二,VC的 _s 类函数仍然还是私货。
a. 微软_s 和 标准_s 的定义和行为不一样。(名字可能不一样,参数可能不一样,行为可能不一样)
b . 微软_s 是对 非_s 的替代(编译报错),标准_s 是对 非_s 的补充(编译不报错)

手机打字
2015-02-04 12:02
rjsp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:528
帖 子:9030
专家分:54050
注 册:2011-1-18
收藏
得分:0 
C标准当初的失误是,努力和cpp产生分歧,以抑制cpp。结果大家看到了。
第二,冒进,得罪了linux/gun那伙人;看不起微软。
可谓是,举目四顾无朋友。

非s 的问题是,得在设计时保证缓冲不溢出。
所以它是 正确但不安全的;
微软s 的问题是,运行时保证不溢出但无法判断数据的完整性。
所以它是 安全但不正确;
标准s 修改了微软的提稿,(部分)通过返回值来判断数据的完整性(当然不仅仅是这样)。
理论上它是最完美的,但要想取得标准n,就得先解决掉两帮人对结尾应不应该加'\0'的分歧(秦始皇在就好了);要想取得标准非s,就得说服令一帮更激进者(要求C内置字符串类型)。
另外,目前的修改版还太粗糙(c标准看不起微软 + c标准需要微软)。
2015-02-04 13:19
rjsp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:528
帖 子:9030
专家分:54050
注 册:2011-1-18
收藏
得分:0 
错别字更正: gnu, 取代
2015-02-04 13:22
快速回复:[轉供參考] C11: 致力于更安全编程的新C标准
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.027482 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved