C11 标准特性研究
Posted 喜欢兰花山丘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C11 标准特性研究相关的知识,希望对你有一定的参考价值。
前言 - 需要点开头
C11标准是C语言标准的第三版(2011年由ISO/IEC发布),前一个标准版本是C99标准。
相比C99,C11有哪些变化呢!!所有的测试全部基于能够和标准贴合的特性平台. 但是绝大部
分来源于 GCC. 这里不妨教大家源码安装最新的GCC吧。
a. 首先去 GNU GCC官网下载最新的 GCC 源码
GCC : https://gcc.gnu.org/
下载最新源码, 安装过程中可能提示下面这句话
configure: error: Building GCC requires GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+.
说白了缺少上面 GMP,MPFR,MPC 三个组件。 那么开始下载
GMP : ftp://ftp.gnu.org/gnu/gmp/
MPFR: http://www.mpfr.org/mpfr-current/
MPC : ftp://ftp.gnu.org/gnu/mpc/
b. 开始挨个解压安装 GMP → MPFR → MPC → GCC
开始执行命令跑起来。
cd gmp-6.1.2/
mkdir gmp-6.1.2-build
cd gmp-6.1.2-build
../configure
我们如果出现
checking for suitable m4... configure: error: No usable m4 in $PATH or /usr/5bin (see config.log for reasons).
不用怕,那就继续安装 m4
cd m4-1.4.18 mkdir m4-1.4.18-build cd m4-1.4.18-build ../configure make sudo make install
那继续安装 GMP。
cd ../../gmp-6.1.2 /gmp-6.1.2-build ../configure make sudo make install
随后就是 MPFR
cd ../../mpfr-3.1.6 mkdir mpfr-3.1.6-build cd mpfr-3.1.6-build ../configure make sudo make install
然后就是 MPC
cd ../../mpc-1.0.3 mkdir mpc-1.0.3-build cd mpc-1.0.3-build ../configure make sudo make install
最后还是回到我们的 gcc
cd ../../gcc-7.2.0 mkdir gcc-7.2.0-build cd gcc-7.2.0-build ../configure
又是不好意思,提示下面错误信息
configure: error: I suspect your system does not have 32-bit development libraries (libc and headers).
If you have them, rerun configure with --enable-multilib. If you do not have them,
and want to build a 64-bit-only compiler, rerun configure with –disable-multilib.
继续
../configure --enable-multilib make
不好意思又来了
checking dynamic linker characteristics... configure: error: Link tests are not allowed after GCC_NO_EXECUTABLES. Makefile:11923: recipe for target \'configure-stage1-zlib\' failed make[2]: *** [configure-stage1-zlib] Error 1 make[2]: Leaving directory \'/home/wangzhi/桌面/gcc-7.2.0/gcc-7.2.0-build\' Makefile:23803: recipe for target \'stage1-bubble\' failed make[1]: *** [stage1-bubble] Error 2 make[1]: Leaving directory \'/home/wangzhi/桌面/gcc-7.2.0/gcc-7.2.0-build\' Makefile:933: recipe for target \'all\' failed make: *** [all] Error 2
没关系我们继续搞,存在首次安装GCC不彻底污染问题,清理后继续安装
make distclean ../configure –enable-multilib make
还是不行更换思路, 走插件全安装
sudo apt-get install gawk sudo apt-get install gcc-multilib sudo apt-get install binutils sudo apt-get install lzip make distclean ../configure make sudo make install
见过漫长的等待,下面就是见证历史奇迹的时候了。
到这里关于 GCC 升级到最新版本问题以及搞定。
正文 - C11标准特性研究
1、对齐处理
alignof(T)返回T的对齐方式,aligned_alloc()以指定字节和对齐方式分配内存,头文件<stdalign.h>
定义了这些内容。我们首先看看 stdalign.h 中定义
/* ISO C1X: 7.15 Alignment <stdalign.h>. */ #ifndef _STDALIGN_H #define _STDALIGN_H #ifndef __cplusplus #define alignas _Alignas #define alignof _Alignof #define __alignas_is_defined 1 #define __alignof_is_defined 1 #endif #endif /* stdalign.h */
alignas 设置内存的对其方式, alignof 返回内存的对其方式。
Aligned.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdalign.h> #define _INT_NAME (128) struct names { int len; char name[]; }; struct people { int id; alignas(struct names) char name[sizeof(struct names) + _INT_NAME]; }; static void test_aligned(void) { printf("sizeof(struct people) = %zu.\\n", sizeof(struct people)); // 控制内存布局 struct people pe = { 1 }; struct names * name = (struct names *)pe.name; name->len = _INT_NAME; strcpy(name->name, "你好吗?"); printf("people len = %d, name = %s.\\n", pe.id, name->name); // 测试内存对其 printf("alignof(struct people) = %zu.\\n", alignof(struct people)); // 接着控制内存布局 alignas(struct names) char xname[sizeof(struct names) + _INT_NAME]; struct names * xna = (struct names *)xname; strcpy(xna->name, "我还行!"); // // 另一种内存申请, 一种演示, malloc已经够额 // aligned_alloc 相比 malloc 多了第一个参数, 这个参数必须是2的幂 // 在特定嵌入式平台会使用 // void * ptr = aligned_alloc(alignof(struct names), _INT_NAME); if (NULL == ptr) exit(EXIT_FAILURE); free(ptr); }
2、 _Noreturn
_Noreturn是个函数修饰符,位置在函数返回类型的前面,声明函数无返回值,
有点类似于gcc的__attribute__((noreturn)),后者在声明语句尾部。
#include <stdio.h> #include <stdlib.h> _Noreturn static void _test(void) { puts("func _test C11 never returns"); abort(); } int main(int argc, char * argv[]) { _test(); }
3、 _Generic
_Generic支持轻量级范型编程,可以把一组具有不同类型而却有相同功能的函数抽象为一个接口。
#include <stdio.h> void sort_insert_int(int a[], int len); void sort_insert_float(float a[], int len); void sort_insert_double(double a[], int len); #define sort_insert(a, len) \\ _Generic(a, \\ int * : sort_insert_int, \\ float * : sort_insert_float, \\ double * : sort_insert_double)(a, len) // // file : generic.c // test : C11 泛型用法 // int main(int argc, char * argv[]) { int a[] = { 1, 2, 5, 3, 4, 11, 23, 34, 33, 55, 11, 12 }; int i, len = sizeof a / sizeof (*a); sort_insert(a, len); for (i = 0; i < len; ++i) printf("%2d ", a[i]); putchar(\'\\n\'); return 0; } #define sort_insert_definition(T) \\ void \\ sort_insert_##T (T a[], int len) { \\ int i, j; \\ for (i = 1; i < len; ++i) { \\ T key = a[j = i]; \\ while (j > 0 && a[j - 1] < key) { \\ a[j] = a[j - 1]; \\ --j; \\ } \\ a[j] = key; \\ } \\ } sort_insert_definition(int) sort_insert_definition(float) sort_insert_definition(double)
最终输出结果如下
4、 _Static_assert()
_Static_assert(),静态断言,在编译时刻进行,断言表达式必须是在编译时期可以计算的表达式,
而普通的assert()在运行时刻断言。
#include <stdio.h> int main(void) { printf("C version : %ld.\\n", __STDC_VERSION__); _Static_assert(__STDC_VERSION__ < 201112L, "It is c11 version"); return 0; }
其实本质等同于, 真的有点鸡肋
#if __STDC_VERSION__ >= 201112L # error "It is c11 version" #endif
5、安全版本的几个函数
gets_s()取代了gets(),原因是后者这个I/O函数的实际缓冲区大小不确定,
以至于发生常见的缓冲区溢出攻击,类似的函数还有其它的。
_Success_(return != 0) _ACRTIMP char* __cdecl gets_s( _Out_writes_z_(_Size) char* _Buffer, _In_ rsize_t _Size );
目前在 VS 中有这个函数实现. C11 废弃了 gets, 这里是最接近的 api, 相比 fgets 它不会记录最后一个 \'\\n\'.
并且会在最后一个字符添加 \'\\0\'. 其中 rsize_t 和 size_t 类型是一样的, 但是
#if __STDC_WANT_SECURE_LIB__ typedef size_t rsize_t; #endif #if __STDC_WANT_SECURE_LIB__ #ifndef RSIZE_MAX #define RSIZE_MAX (SIZE_MAX >> 1) #endif #endif
也就是 gets_s 第二参数合法区间就是 [1, RSIZE_MAX], 否则它会什么都不做.
6、 fopen() 新模式
fopen() 增加了新的创建、打开模式“x”,在文件锁中比较常用。类似 POSIX 中的
O_CREAT | O_EXCL. 文件已存在或者无法创建(一般是路径不正确)都会导致 fopen
失败。文件以操作系统支持的独占模式打开。可惜的是当前 CL or GCC 都没有提供支持.
主要原因是 glibc 没有提供支持!
7、匿名结构体、联合体。
例如下面这样, 直接 struct cjson::vs 这种访问. 一种语法层面优化.
struct cjson { struct cjson * next; struct cjson * child; unsigned char type; char * key; union { char * vs; double vd; }; };
8、多线程
头文件<threads.h>定义了创建和管理线程的函数,新的存储类修饰符_Thread_local限定了变
量不能在多线程之间共享。只能等待 glibc 去支持, 单纯而言可以将 pthread 引入标准线程库.
_Thread_local 等价于线程 pthread_key_t 的私有变量, 不是特别适合不推荐使用.
9、 _Atomic类型修饰符和头文件<stdatomic.h>。
原子操作也算是 C11 看着 C++11 急眼了, 直接引入的类型. 把编译器提供的特性纳入标准中.
同样支持的很一般般. 但是可以一用. 展示一种最简单的自旋锁写法:
include <stdatomic.h> // 标记类型, init lock atomic_flag flag = ATOMIC_FLAG_INIT; // 尝试设置占用(原子操作), try lock atomic_flag_test_and_set(&flag); // 释放(原子操作), unlock atomic_flag_clear(&flag);
10、改进的Unicode支持和头文件<uchar.h>。
提供了utf-8和 utf-16, utf-32 字符之间转换. 其中 uchar.h 在 winds 一种实现如下:
// // uchar.h // // Copyright (c) Microsoft Corporation. All rights reserved. // #pragma once #define _UCHAR #include <corecrt.h> _CRT_BEGIN_C_HEADER #define __STDC_UTF_16__ #define __STDC_UTF_32__ typedef unsigned short _Char16_t; typedef unsigned int _Char32_t; #if !defined __cplusplus || (defined _MSC_VER && _MSC_VER < 1900) typedef unsigned short char16_t; typedef unsigned int char32_t; #endif _Check_return_ _ACRTIMP size_t __cdecl mbrtoc16(_Out_opt_ char16_t *_Pc16, _In_reads_or_z_opt_(_N) const char *_S, _In_ size_t _N, _Inout_ mbstate_t *_Ps); _Check_return_ _ACRTIMP size_t __cdecl c16rtomb(_Out_writes_opt_(6) char *_S, _In_ char16_t _C16, _Inout_ mbstate_t *_Ps); _Check_return_ _ACRTIMP size_t __cdecl mbrtoc32(_Out_opt_ char32_t *_Pc32, _In_reads_or_z_opt_(_N) const char *_S, _In_ size_t _N, _Inout_ mbstate_t *_Ps); _Check_return_ _ACRTIMP size_t __cdecl c32rtomb(_Out_writes_opt_(6) char *_S, _In_ char32_t _C32, _Inout_ mbstate_t *_Ps); _CRT_END_C_HEADER /* * Copyright (c) 1992-2013 by P.J. Plauger. ALL RIGHTS RESERVED. * Consult your license regarding permissions and restrictions. V6.40:0009 */
使用起来也很简单.
#include <stdio.h> #include <uchar.h> #include <locale.h> #include <string.h> // // uchar test // int main(int argc, char * argv[]) { size_t i, len; const char * str = u8"z\\u00df\\u6c34\\U0001F34C"; // 或 u8"zß水以上是关于C11 标准特性研究的主要内容,如果未能解决你的问题,请参考以下文章