譯注:本文摘編自 quora 的一個熱門問答貼。 請在linux系統下測試本文中出現的代碼
<code>switch</code>語句中的<code>case</code> 關鍵詞可以放在<code>if-else</code>或者是循環當中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<code>switch</code>
<code>(a)</code>
<code>{</code>
<code> </code><code>case</code>
<code>1:;</code>
<code> </code><code>// ...</code>
<code> </code><code>if</code>
<code>(b==2)</code>
<code> </code><code>{</code>
<code> </code><code>case</code>
<code>2:;</code>
<code> </code><code>// ...</code>
<code> </code><code>}</code>
<code> </code><code>else</code>
<code>case</code> <code>3:</code>
<code> </code><code>for</code>
<code>(b=0;b<10;b++)</code>
<code> </code><code>{</code>
<code> </code><code>case</code>
<code>5:;</code>
<code> </code><code>// ...</code>
<code> </code><code>}</code>
<code> </code><code>break</code><code>;</code>
<code>4:</code>
了解聲明有一條很簡單的法則,不過不是什麼“從左向右”這種沒道理卻到處宣傳的法則。這一法則的觀點是,一個聲明是要告訴你,你所聲明的對象要如何使用。例如:
<code>int</code>
<code>*p;</code><code>/* *p是int類型的, 是以p是指向int類型的指針 */</code>
<code>a[5];</code><code>/* a[0], ..., a[4] 是int類型的, 是以a是int類型的數組 */</code>
<code>*ap[5];</code><code>/* *ap[0], .., *ap[4] 是int類型的, 是以ap是包含指向int類型指針的指針數組 */</code>
<code>(*pa)[5];</code><code>/* (*pa)[0], ..., (*pa)[4] 是int類型的, 是以pa是指向一個int類型數組的指針 */</code>
在c99之前,你隻能按順序初始化一個結構體。在c99中你可以這樣做:
<code>struct</code>
<code>foo {</code>
<code> </code><code>int</code>
<code>x;</code>
<code>y;</code>
<code>z;</code>
<code>};</code>
<code>foo foo = {.z = 3, .x = 5};</code>
這段代碼首先初始化了<code>foo.z</code>,然後初始化了<code>foo.x</code>. <code>foo.y</code> 沒有被初始化,是以被置為0。
這一文法同樣可以被用在數組中。以下三行代碼是等價的:
<code>a[5] = {[1] = 2, [4] = 5};</code>
<code>a[] = {[1] = 2, [4] = 5};</code>
<code>a[5] = {0, 2, 0, 0, 5};</code>
<code>restrict</code>關鍵詞是一個限定詞,可以被用在指針上。它向編譯器保證,在這個指針的生命周期内,任何通過該指針通路的記憶體,都隻能被這個指針改變。比如,在
<code>f(</code><code>const</code>
<code>int</code><code>* restrict x,</code><code>int</code><code>* y) {</code>
<code> </code><code>(*y)++;</code>
<code>z = *x;</code>
<code> </code><code>(*y)--;</code>
<code> </code><code>return</code>
<code>}</code>
編譯器可能會假設,<code>x</code>和<code>y</code> 所指的并不是同一個<code>int</code>對象,因為如果它們指向了同一個對象,則<code>x</code>的值将可以通過<code></code><code>y</code>修改,這正是你保證不會發生的。是以,将允許編譯器來優化<code>f</code>,就好像函數原本被寫做如下這樣:
<code>*x;</code>
如果你違反協定向<code>f</code>傳遞兩個指向同一int對象的指針時,将産生未定義行為。
我猜想,引入這一特性最初的動機之一是想讓c語言在數值計算時可以fortran一樣快。在fortran 中,預設假定數組不會重疊,是以隻有你通過<code>restrict</code> 限定詞來顯式的告訴編譯器數組不能重疊,編譯器才能在c語言中進行這樣的優化。
在
<code>void</code>
<code>f(</code><code>int</code>
<code>a[</code><code>static</code>
<code>10]) {</code>
<code> </code><code>/* ... */</code>
中,你向編譯器保證,你傳遞給<code>f</code> 的指針指向一個具有至少10個<code>int</code> 類型元素的數組的首個元素。我猜這也是為了優化;例如,編譯器将會假定<code>a</code> 非空。編譯器還會在你嘗試要将一個可以被靜态确定為null的指針傳入或是一個數組太小的時候發出警告。
<code>a[</code><code>const</code><code>]) {</code>
你不能修改指針<code>a.</code>,這和說明符<code>int * const a.</code>作用是一樣的。然而,當你結合上一段中提到的<code>static</code> 使用,比如在<code>int a[static const 10]</code> 中,你可以獲得一些使用指針風格無法得到的東西。
這個表達式會在編譯期間根據控制表達式的類型,在一個含有一個或多個備選方案的集合中做出選擇。下面這個例子可以很好的說明這一切:
<code>#define cbrt(x) _generic((x), \</code>
<code> </code><code>long</code>
<code>double</code><code>: cbrtl, \</code>
<code> </code><code>default</code><code>: cbrt, \</code>
<code> </code><code>float</code><code>: cbrtf \</code>
<code> </code><code>)(x)</code>
是以,如果<code>expr</code> 是<code>long double</code>類型的, <code>cbrt(expr)</code> 被轉換為<code>cbrtl(expr)</code>,如果是<code>float</code>類型
則轉換為<code>cbrtf(expr)</code> ,或是轉換為<code>cbrt(expr)</code>,如果是其他不同的類型(比如說<code>double</code>)。注意,<code>_generic</code> 可以用在宏以外的地方,但是用在宏裡面最好因為c不允許你進行函數重載。
我相信大家都知道<code>wint_t</code> 但是 <code>wint_t</code> 到底是個什麼鬼東西呢?
好吧,記住<code>fgetc</code> 實際上并不會傳回 <code>char</code> 。它會傳回<code>int</code>。顯然這是因為<code>fgetc</code> 必須傳回傳回一個與其他<code>char</code> 都不同的值,也就是<code>eof</code>,表示到達檔案末尾。基于相同的原因,<code>fgetwc</code> 并不傳回<code>wchar_t</code>。它會傳回一個類型,叫做<code>wint_t</code> 可以表示所有無效<code>wchar_t</code> 類型,包括<code>weof</code>,來表示到達檔案末尾。
下面這段c程式可以準确的列印2的747次方而不産生誤差。這是為什麼呢?
程式:
<code>#include <stdio.h></code>
<code>#include <math.h></code>
<code>main() {</code>
<code> </code><code>printf</code><code>(</code><code>"%.0f\n"</code><code>,</code><code>pow</code><code>(2,747));</code>
<code>0;</code>
輸出結果:
<code>740298315191606967520227188330889966610377319868419938630605715764070011466206019559325413145373572325939050053182159998975553533608824916574615132828322000124194610605645134711392062011527273571616649243219599128195212771328</code>
答案:
這個問題包含兩個部分。
其一,2的次方可以在<code>double</code> 中被準确的儲存而不産生任何精度上的損失(這一結論直到2^1023都是對的,再往後就會産生上溢,得到一個正無窮的值)
另外一部分,很多人猜測是語言實作中的某些特殊情況導緻的,但是實際上并非如此。的确,當輸入的資料可以被2的某高次方整除時,有一部分代碼被執行了,但是本質上這隻是通常實作工作時的一個副作用。基本上,<code>printf</code> 在列印數字(任何類型)的時候隻是做了從二進制到十進制的轉換。并且由于結果對于浮點數可能會過大,<code>printf</code> 的内部實作包含和使用一個大整型實作,盡管在c中并沒有大整型這種變量(在gcc源代碼中,<code>vfprintf.c</code> 和<code>dtoa.c</code> 中包含了很多轉換,如果你想要了解可以一看。)
如果你嘗試列印3^474,
<code> </code><code>printf</code><code>(</code><code>"%.0f\n"</code><code>,</code><code>pow</code><code>(3,474));</code>
<code>14304567688284661153278974752312031583901259203711201647725006924333106634519194823303091330277684776547167093155518867557708479462413116497799842448027156309852771422896137582164841870381535840058702788340257784498862132559872</code>
結果仍然是一個很大的數且位數也正确,但是這一次卻不夠精确。這裡會産生一個相對誤差,因為3^474不能以雙精度浮點數準确的表示。準确的數應該是這樣的143045676882846603471…
譯注:在linux系統上是可以的,在windows 64位上後面會有很多0
我發現一些c語言特性或者是小技巧,我覺得隻有很少的人知道。
因為<code>printf()</code> 函數傳回它所列印的字元的個數,我們可以利用這一點來使數字相加,代碼如下:
<code>#include<stdio.h>;</code>
<code>add(</code><code>int</code>
<code>a,</code><code>int</code>
<code>b){</code>
<code> </code><code>if</code><code>(</code><code>if</code><code>(a!=0&&b!=0))</code>
<code> </code><code>return</code>
<code>printf</code><code>(</code><code>"%*c%*c"</code><code>,a,</code><code>'\r'</code><code>,b,</code><code>'\r'</code><code>);</code>
<code> </code><code>else</code>
<code>return</code> <code>a!=0?a:b;</code>
<code>main(){</code>
<code>a = 0, b = 0;</code>
<code> </code><code>printf</code><code>(</code><code>"enter the two numbers to add\n"</code><code>);</code>
<code> </code><code>scanf</code><code>(</code><code>"%d %d"</code><code>,&a,&b);</code>
<code> </code><code>printf</code><code>(</code><code>"required sum is %d"</code><code>,add(a,b));</code>
利用位操作同樣也可以做到:
<code>x,</code><code>int</code>
<code>y)</code>
<code> </code><code>if</code>
<code>(y == 0)</code>
<code>add( x ^ y, (x & y) << 1);</code>
通常我們都這樣使用它:
<code>x = (y < 0) ? 10 : 20;</code>
但是同樣也可以這樣用:
<code>(y < 0 ? x : y) = 20;</code>
<code>static</code>
<code>void</code> <code>foo (</code><code>void</code><code>) { }</code>
<code>void</code> <code>bar (</code><code>void</code><code>) {</code>
<code>return</code>
<code>foo();</code><code>// 注意這裡的傳回語句.</code>
<code>main (</code><code>void</code><code>) {</code>
<code>bar();</code>
通常逗号表達式會這樣使用:
<code>for</code>
<code>(</code><code>int</code>
<code>i=0; i<10; i++, dosomethingelse())</code>
<code> </code><code>/* whatever */</code>
但是你可以在其他任何地方使用逗号表達式:
<code>j = (</code><code>printf</code><code>(</code><code>"assigning variable j\n"</code><code>), getvaluefromsomewhere());</code>
每條語句都進行了求值,但是表達式的值是最後一個語句的值。
<code>struct mystruct a = {0};</code>
這将把結構體中全部元素初始化為0
<code>int x = 'abcd';</code>
這會把x的值設定為0×41424344(或者0×44434241,取決于架構)
<code>a = 3;</code>
<code> </code><code>float</code>
<code>b = 6.412355;</code>
<code> </code><code>printf</code><code>(</code><code>"%.*f\n"</code><code>,a,b);</code>
<code>*</code> 符号可以達到這一目的
希望這些可以幫助到大家
此緻敬禮
你可以在奇怪的地方使用<code>#include</code>
如果你寫:
<code>main()</code>
<code> </code><code>printf</code>
<code>#include "fragment.c" </code>
且<code>fragment.c</code> 包含:
<code>(</code><code>"dayum!\n"</code><code>);</code>
這完全沒有問題。隻要<code>#include</code> 包含完整可解析的c表達式,預處理器并不在意它放在什麼位置。
<code>printf("%4$d %3$d %2$d %1$d", 1, 2, 3, 9); //将會列印9 3 2 1</code>
<code>scanf("%*d%d", &a);// 如果輸入1 2,則隻會得到2</code>
<code>switch</code><code>(c) {</code>
<code> </code><code>case</code>
<code>'a'</code> <code>...</code><code>'z'</code><code>:</code><code>//do something</code>
<code> </code><code>break</code><code>;</code>
<code>1 ... 5 :</code><code>//do something</code>
<code>printf</code><code>(</code><code>"%d"</code><code>,0b1101);</code><code>// prints 13</code>
<code>main;</code>
譯注:雖然編譯沒有error但是卻不能執行
假定我們有一個數組<code>char a[100]</code>
讀取一個字元串:
<code>scanf("%[^\n]\n", a);//表示一直讀取直到遇到'\n',并且忽略掉'\n'</code>
讀取字元串直到遇到逗号:
<code>scanf("%[^,]", a);//但是這次不會忽略逗号</code>
如果你想忽略掉某個輸入,使用在<code>%</code> 後使用<code>*</code>,如果你想要得到<code>john smith</code> 的姓:
<code>scanf</code><code>(</code><code>"%s %s"</code><code>, temp, last_name);</code><code>//典型答案,使用一個臨時變量</code>
<code>scanf</code><code>(</code><code>"%s"</code><code>, last_name);</code>
<code>// 另一種答案,使用一個變量但是調用兩次 `scanf()`</code>
<code>scanf</code><code>(</code><code>"%*s %s"</code><code>, last);</code>
<code>//最佳答案,因為你不需要額外的變量或是調用兩次`scanf()`</code>
順便提一句,你應該非常小心的使用<code>scanf</code> 因為它可能會是你的輸入緩沖溢出!通常你應該使用<code>fgets</code> 和<code>sscanf</code> 而不是僅僅使用<code>scanf</code>,使用<code>fgets</code> 來讀取一行,然後用<code>sscanf</code> 來解析這一行,就像上面示範的一樣。
<code>~-n</code> 等于<code>n-1</code>
<code>-~n</code> 等于<code>n+1</code>
原因:
當我們寫<code>-n</code>時,實際上是以補碼形式儲存,是以<code>-n</code> 可以寫成<code>~n + 1</code>,吧整個式子放在上面表達式的前面你就能明白原因了。