-
Notifications
You must be signed in to change notification settings - Fork 25
/
ch16s02.html
13 lines (13 loc) · 10.2 KB
/
ch16s02.html
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>2. 其它运算符</title><link rel="stylesheet" href="styles.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Linux C编程一站式学习" /><link rel="up" href="ch16.html" title="第 16 章 运算符详解" /><link rel="prev" href="ch16s01.html" title="1. 位运算" /><link rel="next" href="ch16s03.html" title="3. Side Effect与Sequence Point" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">2. 其它运算符</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch16s01.html">上一页</a> </td><th width="60%" align="center">第 16 章 运算符详解</th><td width="20%" align="right"> <a accesskey="n" href="ch16s03.html">下一页</a></td></tr></table><hr /></div><div class="sect1" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2762347"></a>2. 其它运算符</h2></div></div></div><div class="sect2" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a id="id2762352"></a>2.1. 复合赋值运算符</h3></div></div></div><p>复合赋值运算符(Compound Assignment Operator)<a id="id2762363" class="indexterm"></a>包括<code class="literal">*=</code> <code class="literal">/=</code> <code class="literal">%=</code> <code class="literal">+=</code> <code class="literal">-=</code> <code class="literal"><<=</code> <code class="literal">>>=</code> <code class="literal">&=</code> <code class="literal">^=</code> <code class="literal">|=</code>,一边做运算一边赋值。例如<code class="literal">a += 1</code>相当于<code class="literal">a = a + 1</code>。但有一点细微的差别,前者对表达式<code class="literal">a</code>只求值一次,而后者求值两次,如果<code class="literal">a</code>是一个复杂的表达式,求值一次和求值两次的效率是不同的,例如<code class="literal">a[i+j] += 1</code>和<code class="literal">a[i+j] = a[i+j] + 1</code>。那么仅仅是效率上的差别吗?对于没有Side Effect的表达式,求值一次和求值两次的结果是一样的,但对于有Side Effect的表达式则不一定,例如<code class="literal">a[foo()] += 1</code>和<code class="literal">a[foo()] = a[foo()] + 1</code>,如果<code class="literal">foo()</code>函数调用有Side Effect,比如会打印一条消息,那么前者只打印一次,而后者打印两次。</p><p>在<a class="xref" href="ch06s03.html#iter.for">第 3 节 “for语句”</a>讲自增、自减运算符时说<code class="literal">++i</code>相当于<code class="literal">i = i + 1</code>,其实更准确地说应该是等价于<code class="literal">i += 1</code>,表达式<code class="literal">i</code>只求值一次,而<code class="literal">--i</code>等价于<code class="literal">i -= 1</code>。</p></div><div class="sect2" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a id="id2762537"></a>2.2. 条件运算符</h3></div></div></div><p>条件运算符(Conditional Operator)<a id="id2762544" class="indexterm"></a>是C语言中唯一一个三目运算符(Ternary Operator)<a id="id2762552" class="indexterm"></a>,带三个操作数,它的形式是<code class="literal">表达式1 ? 表达式2 : 表达式3</code>,这个运算符所组成的整个表达式的值等于表达式2或表达式3的值,取决于表达式1的值是否为真,可以把它想像成这样的函数:</p><pre class="programlisting">if (表达式1)
return 表达式2;
else
return 表达式3;</pre><p>表达式1相当于<code class="literal">if</code>语句的控制表达式,因此它的值必须是标量类型,而表达式2和3相当于同一个函数在不同情况下的返回值,因此它们的类型要求一致,也要做Usual Arithmetic Conversion。</p><p>下面举个例子,定义一个函数求两个参数中较大的一个。</p><pre class="programlisting">int max(int a, int b)
{
return (a > b) ? a : b;
}</pre></div><div class="sect2" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a id="id2762598"></a>2.3. 逗号运算符</h3></div></div></div><p>逗号运算符(Comma Operator)<a id="id2762605" class="indexterm"></a>也是一种双目运算符,它的形式是<code class="literal">表达式1, 表达式2</code>,两个表达式不要求类型一致,左边的表达式1先求值,求完了直接把值丢掉,再求右边表达式2的值作为整个表达式的值。逗号运算符是左结合的,类似于+ - * /运算符,根据组合规则可以写出<code class="literal">表达式1, 表达式2, 表达式3, ..., 表达式n</code>这种形式,<code class="literal">表达式1, 表达式2</code>可以看作一个子表达式,先求表达式1的值,然后求表达式2的值作为这个子表达式的值,然后这个值再和表达式3组成一个更大的表达式,求表达式3的值作为这个更大的表达式的值,依此类推,整个计算过程就是从左到右依次求值,最后一个表达式的值成为整个表达式的值。</p><p>注意,函数调用时各实参之间也是用逗号隔开,这种逗号是分隔符而不是逗号运算符。但可以这样使用逗号运算符:</p><pre class="programlisting">f(a, (t=3, t+2), c)</pre><p>传给函数<code class="literal">f</code>的参数有三个,其中第二个参数的值是表达式<code class="literal">t+2</code>的值。</p></div><div class="sect2" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a id="id2762676"></a>2.4. sizeof运算符与typedef类型声明</h3></div></div></div><p><code class="literal">sizeof</code>是一个很特殊的运算符,它有两种形式:“<span class="quote">sizeof 表达式</span>”和“<span class="quote">sizeof(类型名)</span>”。这个运算符很特殊,“<span class="quote">sizeof 表达式</span>”中的子表达式并不求值,而只是根据类型转换规则求得子表达式的类型,然后把这种类型所占的字节数作为整个表达式的值。有些人喜欢写成“<span class="quote">sizeof(表达式)</span>”的形式也可以,这里的括号和<code class="literal">return(1);</code>的括号一样,不起任何作用。但另外一种形式“<span class="quote">sizeof(类型名)</span>”的括号则是必须写的,整个表达式的值也是这种类型所占的字节数。</p><p>比如用<code class="literal">sizeof</code>运算符求一个数组的长度:</p><pre class="programlisting">int a[12];
printf("%d\n", sizeof a/sizeof a[0]);</pre><p>在上面这个例子中,由于<code class="literal">sizeof 表达式</code>中的子表达式不需要求值,所以不需要到运行时才计算,事实上在编译时就知道<code class="literal">sizeof a</code>的值是48,<code class="literal">sizeof a[0]</code>的值是4,所以在编译时就已经把<code class="literal">sizeof a/sizeof a[0]</code>替换成常量12了,这是一个常量表达式。</p><p><code class="literal">sizeof</code>运算符的结果是<code class="literal">size_t</code>类型的,这个类型定义在<code class="literal">stddef.h</code>头文件中,不过你的代码中只要不出现<code class="literal">size_t</code>这个类型名就不用包含这个头文件,比如像上面的例子就不用包含这个头文件。C标准规定<code class="literal">size_t</code>是一种无符号整型,编译器可以用<code class="literal">typedef</code>做一个类型声明:</p><pre class="programlisting">typedef unsigned long size_t;</pre><p>那么<code class="literal">size_t</code>就代表<code class="literal">unsigned long</code>型。不同平台的编译器可能会根据自己平台的具体情况定义<code class="literal">size_t</code>所代表的类型,比如有的平台定义为<code class="literal">unsigned long</code>型,有的平台定义为<code class="literal">unsigned long long</code>型,C标准规定<code class="literal">size_t</code>这个名字就是为了隐藏这些细节,使代码具有可移植性。所以注意不要把<code class="literal">size_t</code>类型和它所代表的真实类型混用,例如:</p><pre class="programlisting">unsigned long x;
size_t y;
x = y;</pre><p>如果在一种ILP32平台上定义<code class="literal">size_t</code>代表<code class="literal">unsigned long long</code>型,这段代码把<code class="literal">y</code>赋给<code class="literal">x</code>时就把高位截掉了,结果可能是错的。</p><p><code class="literal">typedef</code>这个关键字用于给某种类型起个新名字,比如上面的<code class="literal">typedef</code>声明可以这么看:去掉<code class="literal">typedef</code>就成了一个变量声明<code class="literal">unsigned long size_t;</code>,<code class="literal">size_t</code>是一个变量名,类型是<code class="literal">unsigned long</code>,那么加上<code class="literal">typedef</code>之后,<code class="literal">size_t</code>就是一个类型名,就代表<code class="literal">unsigned long</code>类型。再举个例子:</p><pre class="programlisting">typedef char array_t[10];
array_t a;</pre><p>这相当于声明<code class="literal">char a[10];</code>。类型名也遵循标识符的命名规则,并且通常加个<code class="literal">_t</code>后缀表示Type。</p></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch16s01.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch16.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch16s03.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">1. 位运算 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 3. Side Effect与Sequence Point</td></tr></table></div></body></html>