译注:本文摘编自 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>,吧整个式子放在上面表达式的前面你就能明白原因了。