您的位置:首页 > 编程语言 > C语言/C++

关于C语言中的Complex(复数类型)和imaginary(虚数类型)(

2013-08-13 15:15 225 查看
关于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;

  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的支持
 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: