特点:
1、必须放在结构体最后一个成员,不能写元素个数,或者写 0 2、不占结构体空间,大小还是 16,d 不计算在内,多大都不算
2、一般思维使用: struct Node n = { 12, {3,4}, 2.5, {1,2,3,4,5,6} }; printf(“%d”, n.d[5]); 那么 d 就是 6 个元素,总 24 个字节。
3、上面太麻烦了,还得初始化,柔性数组设计初衷的使用方式如下: struct Node* pt = (struct Node*)malloc(sizeof(struct Node) + 10 * sizeof(float)); free(pt);
VAL:
变量表示长度的数组,即元素个数是变量,不是数组长度可变,数组从定义的那一刻起, 整个声明周期内,其大小都是固定不可变的
int n = 10; int a[n];//元素个数必须要是确定准确的值 int(*p)[n];不是数组,是数组指针,此时叫 VM,variably-modified types 要求,必须是自动存储类,即局部变量,并且声明时不可进行初始化
初始化指定元素
int a[10] = { [2] = 3,[5] = 6 }; //将下标为 2,5 的元素分别初始化为 3,6,其余为 0 int b[3][4] = {[2][1] = 3,[0][2] = 6}; //将下标为 21,02 的元素分别初始化为 3,6,其余为 0 int a[3] = {}; //c23 将支持,相当于 int a[3] = {0};
结构体:
struct Node { int a; double b; } struct Node no = {.,b=12.22};//在不初始化的成员那+"."
联合:
union Node { int a; double b; float c; }; union Node a = { .b = 12.3}; //只能初始化 1 个成员
restrict、尾随逗号
restrict 用来修饰指针,表示该指针是所指向空间的唯一且初始的指针,所以对于该指针的操作, 编译器可以进行整合优化
int restrict *p = (int *)malloc(4); *p+=1; *p+=3 //此时编译器优化成 *p+=4; //多个同一限定符,只有一个有效 int* restrict restrict restrict p = (int*)malloc(4);
尾随逗号
int a[5] = {1,2,3,4,5,}; enum color{red,black,};
加不加都行
主函数、变量随时定义、for循环
C99标准主函数写法
int main(void) { return 0; } int main(int argc, char* argv[]) { return 0; } //其中 return 0;可以不写,编译器默认给加上
变量随时定义
C99 之前,变量的声明与定义必须放在所在作用域的最前面
C99 起,变量定义就没有顺序可言了,随用随定义即可
for循环
C99 起允许循环控制变量定义在小括号内,i 是 for 循环结构的局部变量
inline内联函数、可变参数宏、新的标准库
inline内联函数:
inline void fun() { }
内联函数具有内部链接作用域,即 static 的同功能,只在所在文件有效。
正常的函数调用是:调用处跳转到代码块内,执行完再跳回调用处,这个过程经过两次跳跃,花 费一定的时间
内联函数,编译期,在调用处将代码直接黏贴过去,相当于没有调用,也就没了跳过的过程,这 在代码的执行上加快的速度。代码的使用角度跟普通函数没有区别,区别在于编译器的处理角度, 优化角度。
优点是执行快,缺点是,如果函数代码很多,函数调用次数也很多,那么该段代码会在源代码中 复制黏贴很多分,这大大增加了代码的量,从而增加生成的软件的体积。所以,一般内联函数的 代码块很小,几行代码,并且调用的频率很高。
但是实际上,编译器会自行根据条件判断某个函数是否以内联处理,这是编译器的优化功能。即 即使某个函数不是内联函数,编译器有可能以内联处理。某个函数声明为内联函数,编译器有可 能不把它做内联处理。
可变参数宏:
__VA_ARGS __
#define X(...) printf(__VA_VRGS__)
预定义宏 __ STDC_HOSTED __ (C99) 如果是 1,表示本地系统支持标准 C 库
__ STDC_ISO_10646 __ (C99) 支持最新版本的 iso10646 标准,即最新的 unicode 字符集的版本
__ STDC_IEC_559__ (C99) 如果支持浮点数 IEC60559 标准时定义为 1,否则数值是未定义 ieee754
__ STDC_IEC_559_COMPLEX __ (C99) 如果复数支持 IEC60559 标准时定义为 1 __STDC_MB_MIGHT_NEQ_WC __ (C99) 如果’x’ != L’x’ 则为 1
__STDC_UTF_16 __ (C11) 如果支持 char16_t,则为 1 uchar.h
__STDC_UTF_32 __ (C11) 如果支持 char32_t,则为 1 uchar.h
__STDC_ANALYZABLE __ (C11)
__STDC_LIB_EXT1 __ (C11)
__STDC_NO_ATOMICS __ 附录 L 被支持为1,各种错误检查情况
附录 K 被支持为1,一系列边界检查_s函数的支持
(C11) 如果不支持原子算数运算,设为 1
__STDC_NO_COMPLEX __ (C11) 如果不支持复数算数运算,设为 1 __STDC_NO_THREADS __ (C11) 如果不支持线程操作,设为 1
__STDC_NO_VLA __ (C11) 如果支持 VLA,则为 1
__STDC_IEC_60559_BFP __ (C23) 如果支持 2 进制浮点运算
__ STDC_IEC_60559_DFP __ (C23) 如果支持 10 进制浮点运算
新的标准库:
<complex.h> <fenv.h> <inttypes.h> <stdbool.h> <stdint.h> <tgmath.h>
_Pragma、函数参数void
_Pragma:和pragma作用一样
二者功能上没有区别,仅仅是形式上有点儿区别,那么新的形式有啥好处么? 写上完整的预处理指令,很麻烦,我们梦想通过宏来实现
#define ONCE #pragma once ONCE 使用
函数参数void:
函数没有参数时,加 void,表示不接受任何参数,即没有参数,什么都不加表示参数不确定
void fun() { } void fun2(void) { } fun(456,"dawd");//正常 fun2(456,"dawd");//error
预定义标识符:
__func__:打印该函数名