自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

最丑陋的C語言特性:tgmath.h

開發(fā) 后端
<tgmath.h>是一個在C99引入的,標準C語言庫提供的頭文件。對于Fortran編寫的數(shù)值軟件,它向C語言提供更加簡潔的接口。

<tgmath.h>是一個在C99引入的,標準C語言庫提供的頭文件。對于Fortran編寫的數(shù)值軟件,它向C語言提供更加簡潔的接口。

跟C語言不同,F(xiàn)ortran提供了編寫在該語言內(nèi)部的“固有函數(shù)”,其表現(xiàn)得更像操作符一樣。固有函數(shù)接受不同類型的參數(shù),并根據(jù)參數(shù)的類型返回 對應(yīng)類型的返回值。同時,F(xiàn)ortran中的普通函數(shù)(“外部函數(shù)”)的行為跟C語言中的函數(shù)類似,對類型要求嚴格(即函數(shù)參數(shù)的類型必須符合,返回值也 是固定的)。舉個例子,F(xiàn)ortran77提供了一個名為INT的函數(shù),它能夠接受Integer、Real、Double和Complex的參數(shù),并總 是返回Integer。另有一個名為SIN的函數(shù),接受Real、Double和Complex的參數(shù)并返回相同類型的值。這兩個函數(shù)僅僅是固有函數(shù)的一 小部分。

某種意義上,這個特性幫了程序員不少忙,因為即使變量類型改變了,函數(shù)調(diào)用也不需要更改。另一方面,用戶定義的函數(shù)不能像這樣工作,因此這些附加的便利性只有在不調(diào)用用戶定義函數(shù)的情況下才成立。

僅僅根據(jù)以上描述,就已經(jīng)有一些C程序員認為這個特性是丑陋的了。同樣的理由,他們認為把printf整合到C中一樣丑陋。

這個功能和其他特性在C99被整合進C語言,包含在在之前提到的中,目的是更好的支持數(shù)值計算。其中提供了三角函數(shù)和對數(shù)函數(shù),舍入相關(guān)的函數(shù)和少 數(shù)其它函數(shù)。這個頭文件定義了一系列宏,覆蓋了中已有的一些函數(shù);例如,cos宏在參數(shù)是double的時候表現(xiàn)得像cos函數(shù)一樣,參數(shù)是float時 像cosf,參數(shù)是long double時像cosl,double _Complex時像ccos,參數(shù)是float _Complex時像ccosf,參數(shù)是long double _Complex時像ccosl。最終,如果參數(shù)是任何整形,宏調(diào)用cos函數(shù),就像參數(shù)被隱式的轉(zhuǎn)換為了double類型一樣。

這個特性丑陋的第二個理由在于它試圖模仿成函數(shù),但是這個模仿不但不完美,甚至是非常危險的:如果你嘗試著將泛型宏cos當成一個參數(shù)傳遞給函數(shù),而事實上它總是被當做對應(yīng)double的cos函數(shù),因為cos后面不緊跟一個左括號的話宏根本不會展開。

最后一個被認為丑陋的理由在于,這樣的宏在嚴格意義上的C上根本不能實現(xiàn),它們需要依靠某種編譯器支持——另外,某些經(jīng)驗(例如,glibc實現(xiàn)中 bug被發(fā)現(xiàn)的速度)表明,這個特性基本上沒有使用過,因此不應(yīng)該被算作這個語言核心的一部分,尤其是它根本就不支持潛在的特性。(相比之 下,<stdarg.h>對便攜性的支持就非常的好。)

說了這么多,這個特性又丑陋有沒有實用價值,我干嘛提到它?我寫這個文章的原因是我在考察glibc的時候,發(fā)現(xiàn)它是一個如此天才的實現(xiàn)。我認為它應(yīng)該用一種更好的辦法被后人銘記,而不是像下面這樣的注釋一樣。

        2000-08-01 Ulrich Drepper drepper@redhat.com

Joseph S. Myers jsm28@cam.ac.hk

* math/tgmah.h: Make standard compliant. Don’t ask how.

最直接模仿Fortran編譯器的方法是使用一個簡單的宏:(我會用cos來舉大部分例子,其他宏的語法是相似的。)

  1. #define cos(X) __tgmath_cos(X) 

編譯器會將__tgmath_cos當做內(nèi)部操作符,然后將其轉(zhuǎn)換成某一個前端的函數(shù)調(diào)用。

我見過的被推選出的最簡潔的解決方法,是在編譯器前段給基本函數(shù)加上了重載支持,這可以利用運營商擴展來實現(xiàn)。(否則,C語言標準會要求編譯器檢查某個標示符的不兼容聲明。)

  1. #define cos(X) __tgmath_cos(X) 
  2. #praga compiler_vendor overload __tgmath_cos 
  3. double __tgmath_cos (double x) 
  4. {return (cos) (x); } 
  5. float __tgmath_cos (float x) 
  6. {return cosf (x); } 
  7. long double __tgmath_cos (long double x) 
  8. {return cosl (x); } 
  9. ... 

(簡單的習題: 為什么在定義__tgmath_cos(double)時,cos兩旁有括號呢?)

當然,僅僅為了<tgmath.h>的這個目的而實現(xiàn)它是一件非常繁雜的工作。(雖然它有可能能在C++前端上工作。)沒人想在C語言中用這樣一個笨重的擴展,何況本就沒多少程序使用<tgmath.h>,所以似乎這樣擴展編譯器有些不值得。

glibc的實現(xiàn)必須依靠那些用已經(jīng)成熟的gcc版本推出的擴展,因此要實現(xiàn)它更加復雜了。

首先,讓我們實現(xiàn)一個選擇正確函數(shù)類型的宏吧。因為C語言不支持條件宏擴展,因此條件判斷語句需要包含在擴展代碼中。我們需要像下面這樣代碼:

  1. #define cos(X) \ 
  2.   (X is real) ? ( \ 
  3.     (X has type double \ 
  4.       || X has an integer type) \ 
  5.       ? (cos) (X) \ 
  6.       : (X has type long double) \ 
  7.       ? cosl(X) \ 
  8.       : cosf (X) \ 
  9.   ) : ( 
  10.     (X has type double _Complex) \ 
  11.     ? ccos (X) 
  12. .... 

而且,我們發(fā)現(xiàn)寫上面那樣的條件判斷語句非常簡單。

  • “x is real”就是sizeof (X) == sizeof (__real__ (X))
  • “x has an integer type”就是(typeof (X))1.1 == 1(中等的習題:(__typeof__ (X))0.1 == 0不正確。這是為什么呢?) (事實上,glibc在某些情況使用了__builtin_classify_type,一種嵌入式的內(nèi)部gcc,而在上述情況使用了另一種相似的替代。)
  • “x has type double/long double/float“也能被sizeof區(qū)分。但在有些硬件結(jié)構(gòu)下,一些C類型被映射成相同的硬件類型,這時區(qū)分的結(jié)果可能那么精確,不過在這些硬件結(jié)構(gòu)下這些不同類型的運算都沒有差別,而且外部的C語言也不能識別出差別了。就C語言的”as-if”原則來說,這算是相當不錯的了。

好的,這樣一來我們的cos宏就能選擇正確的函數(shù)來調(diào)用了。不過不幸的是,它總是返回long double _Complex類型的結(jié)果。原因在于,? :操作符的返回值的類型會是第二和第三操作數(shù)類型的“常用算術(shù)轉(zhuǎn)換”。

我們能夠避免這些類型轉(zhuǎn)換來使用我們自己選擇的類型,這需要另一個gcc擴展,聲明表達式:

  1. #define cos(X) ({ result_type __var; \ 
  2.   if (X is real) { \ 
  3.     if ((X has type double) \ 
  4.       || (X has an integer type)) { \ 
  5.       __var = (cos) (X); \ 
  6.     else if (X has type long double) \ 
  7.       __var = cosl (X); \ 
  8. ... 
  9.   __var; }) 

#p#

現(xiàn)在,這個宏的結(jié)果永遠會是result_type,問題引刃而解。

是嗎?

事實上并沒有。我們該怎么定義result_type?對于浮點數(shù)類型我們可以直接用__typeof__ (X),但我們又想用double作為整形參數(shù),況且C語言并沒有一種對于類型的? :操作數(shù),是吧。

前兩個練習放在那兒,并不是因為我是個老師,想檢查一下你的進度。它們是為了最后最有難度的習題準備的——或者是為了在你到這里之前就把你嚇跑。 (好吧,我想我已經(jīng)把大家都無聊死了,沒人能讀到這兒了。)雖然這個習題的上下文提示的已經(jīng)夠多了,也可能仍然不足以解答,來看看吧:

困難的習題:以下兩個結(jié)果有何不同?

  1. 1 ? (int *)0 : (void *)0 

  1. 1 ? (int *)0 : (void *)1 

以及為什么?

不像之前的兩個習題稍作研究和思考就能解決,這個習題(尤其是為什么的部分)有可能要求你閱讀C語言標準,因此我在這里做出解釋。

首先,解釋一下概念是必要的:

  • 從編譯器的角度來說,一個整形常數(shù)表達式就是一個整形表達式有一個常數(shù)值:編譯器能夠計算這個常數(shù)而不用任何除了常數(shù)合并以外的優(yōu)化。尤其是這個表達式不會用到任何其他變量的值。
  • 空指針就是一個值等于整數(shù)值0的指針??罩羔樐軌蚴侨魏晤愋偷闹羔?。
  • 空指針常量是一種句法結(jié)構(gòu)??罩羔槼A康闹翟谵D(zhuǎn)換成一個指針類型時,是一個空指針(“空指針”和“它的值”都在上文說過了)??蘸暾归_成空指針。

因為空指針常量是一種句法結(jié)構(gòu),它就有一個句法定義,它要不是一個等于零的整型常量,要不一個轉(zhuǎn)換成void *的表達式。舉個例子,0, 0L, 1 - 1, (((1 - 1))), (void *)(void *)(1 - 1)都是空指針常量,但(int *)0(void *)1就不是。

(其實,當其定義為一個表達式的值時,它就不是一個句法結(jié)構(gòu)了。不過最好就這樣假裝它是個句法結(jié)構(gòu),因為大部分情況下,“值為零的整型常量表達式”其實就是字面上的0。)

現(xiàn)在我們來看看C語言標準的6.5.15部分的第六段,這部分講到了條件操作符? :,有以下內(nèi)容:

如果第二和第三操作數(shù)都是指針…,那么結(jié)果類型也會是一個指針…。更有,如果兩個操作數(shù)都是指向類型相兼容的指針的話…,結(jié)果類型會是一個…指向其合成類型的指針;如果一個操作數(shù)是一個空指針常量,結(jié)果類型跟另一個操作數(shù)的類型相同;否則,…結(jié)果類型是一個指向void…的指針。

因此,在下面表達式中

  1. 1 ? (int *)0 : (void *)0 

第三個操作數(shù)是一個空指針常量,因此結(jié)果是(int *)0。而在

  1. 1 ? (int *)0 : (void *)1 

中,第三個操作數(shù)不是一個空指針常量,因此結(jié)果是。這就是我們對于類型的條件操作符,我們只需再稍加修繕。

注意到這個表達式(其中X是個整形)是一個整形常量表達式。

  1. 1 ? (__typeof__ (X) *)0 : (void *)(X has a integer type) 

因此,如果X是一個整形變量,結(jié)果就是(void *)0,否則就是。而下面這個式子。

  1. 1 ? (int *)0 : (void *)(!(X has an integer type)) 

在X是整形的情況下結(jié)果是(int *)0,否則結(jié)果是(void *)0。注意到兩個情況中都有其中一個結(jié)果是(void *)0。

我們定義上面兩個表達式分別為E1和E2,那么,以下表達式:

  1. 1 ? (__typeof__ (E1))0 : (__typeof__ (E2))0 

在x是整形的時候為(int *)0,否則為(__typeof__ (X) *)0。同上,我們注意到有一個表達式總是空指針常量。

最后,我們定義result_type為:

  1. __typeof__ (*(1 ? (__typeof__ (E1))0 : (__typeof__ (E2))0)) 

原文鏈接:http://carolina.mff.cuni.cz/%7Etrmac/blog/2005/the-ugliest-c-feature-tgmathh/?foo

譯文鏈接:http://blog.jobbole.com/49139/

責任編輯:陳四芳 來源: 伯樂在線
相關(guān)推薦

2018-04-19 14:54:12

2009-09-18 09:59:39

C# CLR

2013-12-30 10:42:42

C++特性

2011-11-14 09:56:17

C++

2009-08-19 16:50:32

Visual C#C#語言特性

2009-09-18 15:53:37

C# 3.0新語言特性

2014-08-05 13:09:34

Objective-C動態(tài)特性

2013-03-26 10:27:01

JavaScriptjson

2010-01-11 13:37:31

C++語言

2020-09-19 17:46:20

React Hooks開發(fā)函數(shù)

2015-08-03 10:25:58

C語言特性不為人知

2025-02-14 00:00:20

C#C/C++語言

2016-10-13 13:33:41

反射特性c#

2021-07-15 23:18:48

Go語言并發(fā)

2010-01-15 17:38:37

C++語言

2010-10-28 13:06:45

Java克隆特性

2014-11-25 10:18:17

Objective-C

2009-08-18 17:03:49

C#3.5新特性

2009-09-17 16:34:24

C#組件類

2018-02-01 16:26:44

面試題static變量
點贊
收藏

51CTO技術(shù)棧公眾號