关于C语⾔中的Complex(复数类型)和imaginary(虚数类
型)(
关于C语⾔中的Complex(复数类型)和imaginary(虚数类型)
1、C99 新增了复数类型(_Complex)和虚数类型(_Imaginary)。简单来说,C99 提供了三种复数类型:float _Complex,double
_Complex,和 long double _Complex。对于 float _Complex类型的变量来说,它包含两个 float类型的值,⼀个⽤于表⽰复数的实部(real part),另⼀个⽤于表⽰虚部(imaginary part)。类似地,double _Complex 包含两个 double类型的值。C99 也提供了三种虚数类型:float _Imaginary,double _Imaginary,以及 long double _Imaginary。虚数类型只有虚部,没有实部。
包含标准头⽂件 complex.h 后,我们就可以⽤ complex来代表 _Complex,⽤imaginary来代表 _Imaginary,以及⽤ I来代表虚数单位i,也就是 -1的平⽅根。例如:
#include <complex.h>
double _Complex x = 5.2;
double complex y = 5.0 * I;
double complex z = 5.2 – 5.0 * I;
注意:_Complex类型对于独⽴式环境(freestanding environment)来说是可选的。可选的意思是,不强制必须⽀持这种类型。⽽所谓独⽴式环境,是指 C 程序可以在没有操作系统的情况下运⾏。_Imaginary类型在任何环境下都是可选的。⽬前的编译器对这两种类型的⽀持都不太好,在此就不对这两种类型进⾏更深⼊的讨论了
2、C99 新增了 _Bool 类型(布尔类型)⽤于表⽰真/假。_Bool 类型的变量的值只能是 0 或者 1。⽆论赋予任何⾮零值给 _Bool 类型的变量,它的值都只会是 1。例如:
_Bool i_am_true = 15; // i_am_true 的值是 1
_Bool am_i_true = (var > 15); // 当 var ⼤于 15 时,am_i_true 的值为 1
包含标准头⽂件 stdbool.h 后,我们可以⽤ bool 代替 _Bool ,true 代替 1 ,false 代替 0 。例如:
bool no_error = true;
no_error = false;
这么做是为了和 C++ 兼容,stdbool.h 是 C99 新增的。
3、register修饰符
  register修饰符暗⽰编译程序相应的变量将被频繁地使⽤,如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度。例如下⾯的内存块拷贝代码,
  #ifdef NOSTRUCTASSIGN
  memcpy (d, s, l)
  {register char *d;
  register char *s;
  register int i;
  while (i--)
  *d++ = *s++;
  }
  #endif
  但是使⽤register修饰符有⼏点限制。
  ⾸先,register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是⼀个单个的值,并且长度应该⼩于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。
  其次,因为register变量可能不存放在内存中,所以不能⽤“&”来获取register变量的地址。
  由于寄存器的数量有限,⽽且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作⽤的register修饰符的数⽬和类型都依赖于运⾏程序的机器,⽽任何多余的register修饰符都将被编译程序所忽略。
  在某些情况下,把变量保存在寄存器中反⽽会降低程序的运⾏速度。因为被占⽤的寄存器不能再⽤于其它⽬的;或者变量被使⽤的次数不够多,不⾜以装⼊和存储变量所带来的额外开销。
4、volatile关键字
volatile总是与优化有关,编译器有⼀种技术叫做数据流分析,分析程序中的变量在哪⾥赋值、在哪⾥使⽤、在哪⾥失效,分析结果可以⽤于常量合并,常量传播等优化,进⼀步可以死代码消除。但有时这些
优化不是程序所需要的,这时可以⽤volatile关键字禁⽌做这些优化,volatile的字⾯含义是易变的,它有下⾯的作⽤:
1) 不会在两个操作之间把volatile变量缓存在寄存器中。在多任务、中断、甚⾄setjmp环境下,变量可能被其他的程序改变,编译器⾃⼰⽆法知道,volatile就是告诉编译器这种情况。
2)不做常量合并、常量传播等优化,所以像下⾯的代码:
  volatile int i = 1;
value函数什么意思
  if (i > 0) ...
  if的条件不会当作⽆条件真。 意思i可能被其它程序所改变
3)对volatile变量的读写不会被优化掉。如果你对⼀个变量赋值但后⾯没⽤到,编译器常常可以省略那个赋值操作,然⽽对Memory Mapped IO的处理是不能这样优化的。
4)volatile变量能防⽌优化,⽐如说你在某个地⽅可能连续调⽤了好⼏次这个函数,于是编译器优化后,可能就调⽤⼀次,其他⼏次就采⽤这⼀次调⽤的返回值,⽽volatile修饰后,要让每⼀次都进⾏函数调⽤,⽽不采⽤暂存值。
5、 内联函数(inline)
什么是内联函数:内联函数是为了解决C 预处理器宏存在的问题所提出⼀种解决⽅案,⽤来提⾼函数使⽤效率。内联函数使⽤inline关键字定义,并且函数体和申明必须结合在⼀起,
否则编译器将他作为普通函数对待。
inline void function(int x); //仅仅是申明函数,没有任何效果
inline void function(int x) //正确
{
return x;
}
在类内部定义的函数⾃动的为内联函数,
不需要加关键字inline。
class point
{
int i;
public:
void SetValue(int x) //内联函数
{
i = x;
}
}
内联函数和普通函数的区别
普通函数:编译器在它的符号表中放⼊函数类型(包含名字和参数类型的函数原型及函数的返回类型)。
内联函数:函数的代码也被放⼊符号表,代码是以源程序形式还是以编译过的汇编指令形式存放取决于编译器。
当内联函数太复杂,编译器将不能执⾏内联。
6、restrict的含义和⽤法
概括的说,关键字restrict只⽤于限定指针;该关键字⽤于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进⾏修改操作的途径;这样的后果是帮助编译器进⾏更好的代码优化,⽣成更有效率的汇编代码。举个简单的例⼦
int foo (int* x, int* y)
...{
*x = 0;
*y = 1;
return *x;
}
很显然函数foo()的返回值是0,除⾮参数x和y的值相同。可以想象,99%的情况下该函数都会返回0⽽不
是1。然⽽编译起必须保证⽣成100%正确的代码,因此,编译器不能将原有代码替换成下⾯的更优版本
int f (int* x, int* y)
...{
*x = 0;
*y = 1;
return 0;
}
现在我们有了restrict这个关键字,就可以利⽤它来帮助编译器安全的进⾏代码优化了
int f (int *restrict x, int *restrict y)
...{
*x = 0;
*y = 1;
return *x;
}
此时,由于指针 x 是修改 *x的唯⼀途径,编译起可以确认 “*y=1; ”这⾏代码不会修改 *x的内容,因此可以安全的优化为
int f (int *restrict x, int *restrict y)
...{
*x = 0;
*y = 1;
return 0;
}
最后注意⼀点,restrict是C99中定义的关键字,C++⽬前并未引⼊;在GCC可通过使⽤参数" -std=c99"来开启对C99的⽀持