1. 程式人生 > >關於C語言中的Complex(複數型別)和imaginary(虛數型別)(

關於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;
  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的支援