让你的 C 程序更有效率的 10 种方法
fmms 13年前
<p><span id="result_box" lang="zh-CN"><span>任何代码</span><span>的</span><span>美丽</span><span>不仅在于</span><span>找到</span><span>一个给定的</span><span>问题</span><span>的</span><span>解决方案</span><span>,</span><span>但</span><span>在</span><span>它的简单性</span><span>,</span><span>有效性</span><span>,</span><span>紧凑性和</span><span>效率</span><span>(内存)</span><span>。</span><span>设计</span><span>的代码</span><span>比</span><span>实际执行</span><span>更难</span><span> 。</span><span>因此,</span><span>每</span><span>一个</span><span>程序员当用C语言开发时,都应该</span><span>保持这些</span><span>基本的东西</span><span>在</span><span>头脑</span><span>中。</span></span></p> <p><span id="result_box" lang="zh-CN"><span> </span><span> </span><span>本文向你介绍规范你的C代码的10种方法。</span></span></p> <p><strong>1. <span id="result_box" lang="zh-CN" class="short_text">避免不必要的函数调用</span></strong></p> <p>考虑下面的2个函数:</p> <blockquote> <p>void str_print( char *str )<br /> {<br /> int i;<br /> for ( i = 0; i < strlen ( str ); i++ ) {<br /> printf("%c",str[ i ] );<br /> }<br /> }</p> </blockquote> <blockquote> void str_print1 ( char *str ) <br /> { <br /> int len; <br /> len = strlen ( str ); <br /> for ( i = 0; i < len; i++ ) { <br /> printf("%c",str[ i ] ); <br /> } <br /> } </blockquote> <p><span id="result_box" lang="zh-CN"><span>请</span><span>注意</span><span> </span><span>这两个函数</span><span>的</span><span>功能</span><span>相似</span><span>。</span><span>然而,</span><span>第一个函数</span><span>调用</span><span>strlen()函数</span><span>多次</span><span>,</span><span>而第二个</span><span>函数只</span><span>调用</span><span>函数strlen(</span><span>)</span><span>一次。</span><span>因此</span><span>第一个函数</span><span>性能</span><span>明显比</span><span>第二个</span><span>好</span><span>。</span></span></p> <p><strong><span lang="zh-CN"><span>2、</span></span><span id="result_box" lang="zh-CN" class="short_text"><span>避免</span><span>不必要</span><span>的</span><span>内存引用</span></span></strong></p> <p><span lang="zh-CN" class="short_text"><span>这次我们再用2个例子来对比解释:</span></span></p> <blockquote> int multiply ( int *num1 , int *num2 ) <br /> { <br /> *num1 = *num2; <br /> *num1 += *num2; <br /> return *num1; <br /> } </blockquote> <blockquote> int multiply1 ( int *num1 , int *num2 ) <br /> { <br /> *num1 = 2 * *num2; <br /> return *num1; <br /> } </blockquote> <p><span id="result_box" lang="zh-CN"><span>同样</span><span>,</span><span>这两个函数</span><span>具有类似的功能</span><span>。</span><span>所不同的是</span><span>在第一个函数</span><span>(</span><span> </span></span>1 for reading *num1 , 2 for reading *num2 and 2 for writing to *num1<span id="result_box" lang="zh-CN"><span>)</span><span>有5</span><span>个内存的引用</span><span>,</span><span>而</span><span>在</span><span>第二个函数</span><span>是</span><span>只有2个</span><span>内存引用</span><span>(</span></span>one for reading *num2 and one for writing to *num1<span id="result_box" lang="zh-CN"><span>)</span><span>。</span><span>现在</span><span>你认为</span><span>哪一个</span><span>好些</span><span>?</span></span></p> <p><strong>3、节约内存<span id="result_box" lang="zh-CN" class="short_text"><span>(</span><span>内存</span><span>对齐和</span><span>填充</span><span>的</span><span>概念</span></span>)</strong></p> <blockquote> <p>struct {<br /> char c;<br /> int i;<br /> short s;<br /> }str_1;<strong><br /> </strong></p> </blockquote> <blockquote> struct { <br /> char c; <br /> short s; <br /> int i; <br /> }str_2; </blockquote> <p>假设一个字符需要1个字节,short占用2个字节和int需要4字节的内存。起初,我们会认为上面定义的结构是相同的,因此占据相同数量的内存。然而,而str_1占用12个字节,第二个结构只需要8个字节?这怎么可能呢?</p> <p>请注意,在第一个结构,3个不同的4个字节被分配到三种数据类型,而在第二个结构的前4个自己char和short可以被采用,int可以采纳在第二个的4个字节边界(一共8个字节)。</p> <p><strong>4、</strong><span id="result_box" lang="zh-CN"><strong>使用无符号整数,而不是整数的,如果你知道的值将永远是否定的。</strong></span></p> <p><span id="result_box" lang="zh-CN"><strong> </strong><span>有些</span><span>处理器可以处理</span><span>无符号</span><span>的</span><span>整数</span><span>比有符号整数的运算速度要快。</span><span>(</span><span>这也是</span><span>很好</span><span>的</span><span>实践,</span><span>帮助</span></span>self-documenting代码<span id="result_box" lang="zh-CN"><span>)</span><span>。</span></span></p> <p><strong><span lang="zh-CN">5、</span></strong><span id="result_box" lang="zh-CN" class="short_text"><strong><span>在</span><span>一个</span><span>逻辑</span><span>条件语句中常数项永远在</span><span>左侧。</span></strong></span></p> <blockquote> <p>int x = 4;<br /> if ( x = 1 ) {<br /> x = x + 2;<br /> printf("%d",x); // Output is 3<br /> }</p> </blockquote> <blockquote> int x = 4; <br /> if ( 1 = x ) { <br /> x = x + 2; <br /> printf("%d",x); // Compilation error <br /> } </blockquote> <p><span id="result_box" lang="zh-CN"><span>使用</span><span class="atn">“</span><span>=”</span><span>赋值</span><span>运算符</span><span>,替代</span><span class="atn">“=</span><span>=”相等</span><span>运算符,这是个常见的输入错误。</span><span> </span><span>常数项</span><span>放在左侧,</span><span>将产生一个</span><span>编译时错误</span><span>,让</span><span>你</span><span>轻松</span><span>捕获</span><span>你的错误</span><span>。</span><span>注:</span><span>“=”</span><span>是赋值运算符</span><span>。</span> <span class="hps">b = 1</span><span>会设置</span><span>变量b</span><span>等于</span><span>值1</span><span>。</span> <span class="hps">“==”</span><span>相等运算符</span><span>。</span><span>如果</span><span>左侧</span><span>等于</span><span>右侧</span><span>,返回true,</span><span>否则返回false。</span></span></p> <p><span id="result_box" lang="zh-CN"><span>6、</span></span><span id="result_box" lang="zh-CN" class="short_text"><span>在可能的情况下</span><span>使用</span></span>typedef替代macro。<span id="result_box" lang="zh-CN" class="short_text"><span>当然有时候你无法避免macro,但是typedef更好。</span></span></p> <blockquote> <p>typedef int* INT_PTR;<br /> INT_PTR a , b;<br /> # define INT_PTR int*;<br /> INT_PTR a , b;</p> </blockquote> <p><span id="result_box" lang="zh-CN"><span>在这个</span><span>宏定义中</span><span>,</span><span>a是一个</span><span>指向整数的指针</span><span>,</span><span>而</span><span>b是</span><span>只有</span><span>一个整数</span><span>声明</span><span>。</span><span>使用typedef</span> <span class="hps">a和b都是</span><span> </span><span>整数</span><span>的</span><span>指针</span><span>。</span></span></p> <p><strong>7、</strong><span id="result_box" lang="zh-CN"><strong>确保声明和定义是静态的,除非您希望从不同的文件中调用该函数</strong><span><strong>。</strong></span></span></p> <p><span id="result_box" lang="zh-CN"><span>在同一文件函数对其他函数可见,才称之为静态函数。</span><span>它</span><span>限制其他</span><span>访问内部函数,如果</span><span>我们希望</span><span>从</span><span>外界</span><span>隐藏</span><span>该函数</span><span>。</span><span>现在</span><span>我们并不</span><span>需要为内部函数创建头文件,其他看不到该函数。</span></span></p> <p><span id="result_box" lang="zh-CN"><span>静态</span><span>声明一个函数</span><span>的</span><span>优点</span><span>包括:</span></span></p> <p><span id="result_box" lang="zh-CN"><span>A)</span><span>两个或两个以上</span><span>具有相同名称</span><span>的</span><span>静态</span><span>函数</span><span>,</span><span>可用于</span><span>在不同的文件</span><span>。</span></span></p> <p><span id="result_box" lang="zh-CN"><span>B)</span><span>编译消耗</span><span>减少</span><span>,因为没有</span><span>外部符号</span><span>处理</span><span>。</span></span></p> <p><span id="result_box" lang="zh-CN"><span>让</span><span>我们</span><span>做</span><span>更好的</span><span>理解</span><span>,下面的例子</span><span>:</span></span></p> <blockquote> <p><br /> /*first_file.c*/<br /> static int foo ( int a )<br /> {<br /> /*Whatever you want to in the function*/<br /> }<br /> /*second_file.c*/<br /> int foo ( int )<br /> int main()<br /> {<br /> foo(); // This is not a valid function call as the function foo can only be called by any other function within first_file.c where it is defined.<br /> return 0;<br /> }</p> </blockquote> <p><span id="result_box" lang="zh-CN"><strong><span>8、使用</span></strong></span><em>Memoization</em><span id="result_box" lang="zh-CN"><strong><span>,以避免</span><span>递归重复</span><span>计算</span></strong><br /> <br /> <span>考虑</span></span><span lang="zh-CN"><span>Fibonacci</span></span><span id="result_box" lang="zh-CN"><span>(</span></span><span>斐波那契</span><span id="result_box" lang="zh-CN"><span>)问题</span><span>;</span></span><span lang="zh-CN"><span>Fibonacci</span></span><span id="result_box" lang="zh-CN"><span>问题是可以</span><span>通过</span><span>简单</span><span>的</span><span>递归方法</span><span>来解决:</span></span></p> <blockquote> <p><span lang="zh-CN"><span>int fib ( n )<br /> {<br /> if ( n == 0 || n == 1 ) {<br /> return 1;<br /> }<br /> else {<br /> return fib( n - 2 ) + fib ( n - 1 );<br /> }<br /> }<br /> </span></span></p> </blockquote> <p><span id="result_box" lang="zh-CN" class="short_text"><span>注:</span><span>在</span><span>这里</span><span>,</span><span>我们考虑</span></span><span lang="zh-CN"><span>Fibonacci</span></span><span id="result_box" lang="zh-CN"><span> 系列从</span></span><span id="result_box" lang="zh-CN" class="short_text"><span>1开始</span><span>,</span><span>因此,</span><span>该系列</span><span>看起来:</span><span>1,</span><span>1</span><span>,</span><span>2</span><span>,</span><span>3</span><span>,</span><span>5</span><span>,</span><span>8</span><span>,</span><span>...</span></span></p> <p><a href="/misc/goto?guid=4958183169206006359"><img title="04114621_jeub.png" border="0" alt="04114621_jeub.png" src="https://simg.open-open.com/show/e87fff9988517985efcf3e8aea87e198.jpg" /></a></p> <p><span id="result_box" lang="zh-CN"><span>注意:从</span><span>递归树</span><span>,</span><span>我们</span><span>计算</span><span>fib(3)</span><span>函数2次,</span><span>fib</span><span>(2)函数3次。</span><span>这是</span><span>相同函数的重复计算。</span><span>如果n</span><span>非常大,fib</span><span><n</span><span>(</span><span>i)</span><span>函数增长i<n。解决这一问题的快速方法将是计算函数值1次,存储在一些地方,需要时计算,而非一直重复计算。</span></span></p> <p><span id="result_box" lang="zh-CN"><span>这个简单的技术叫做</span></span><em>Memoization</em><span id="result_box" lang="zh-CN"><span>,可以被用在递归,加强计算速度。</span></span><span id="result_box" lang="zh-CN"><span><br /> <br /> </span></span><span id="result_box" lang="zh-CN"><span>fibonacci 函数</span></span><em>Memoization</em><span id="result_box" lang="zh-CN"><span>的代码,应该是下面的这个样子:</span></span></p> <blockquote> <p><span lang="zh-CN"><span>int calc_fib ( int n )<br /> {<br /> int val[ n ] , i;<br /> for ( i = 0; i <=n; i++ ) {<br /> val[ i ] = -1; // Value of the first n + 1 terms of the fibonacci terms set to -1<br /> }<br /> val[ 0 ] = 1; // Value of fib ( 0 ) is set to 1<br /> val[ 1 ] = 1; // Value of fib ( 1 ) is set to 1<br /> return fib( n , val );<br /> }<br /> int fib( int n , int* value )<br /> {<br /> if ( value[ n ] != -1 ) {<br /> return value[ n ]; // Using memoization<br /> }<br /> else {<br /> value[ n ] = fib( n - 2 , value ) + fib ( n - 1 , value ); // Computing the fibonacci term<br /> }<br /> return value[ n ]; // Returning the value<br /> }<br /> </span></span></p> </blockquote> <p>这里calc_fib( n )函数被main()调用。</p> <p><strong>9、</strong><span id="result_box" lang="zh-CN"><strong><span>避免</span>悬空指针和野指针</strong><br /> <br /> <span>一个指针的</span><span>指向</span><span>对象</span><span>已被删除</span><span>,那么就成了悬空指针。</span><span>野</span><span>指针是</span><span>那些</span><span>未初始化</span><span>的</span><span>指针</span><span>,</span><span>需要注意的是</span><span>野</span><span>指针</span><span>不</span><span>指向</span><span>任何</span><span>特定的内存位置</span><span>。</span></span></p> <blockquote> <p>void dangling_example()<br /> {<br /> int *dp = malloc ( sizeof ( int ));<br /> /*........*/<br /> free( dp ); // dp is now a dangling pointer<br /> dp = NULL; // dp is no longer a dangling pointer<br /> }</p> </blockquote> <blockquote> void wild_example() <br /> { <br /> int *ptr; // Uninitialized pointer <br /> printf("%u"\n",ptr ); <br /> printf("%d",*ptr ); <br /> } </blockquote> <p>当遭遇这些指针,程序通常是”怪异“的表现。</p> <p>10、 <span id="result_box" lang="zh-CN"><span>永远记住</span><span>释放你分配给程序的任何内存。上面的例子就是如果释放dp指针(我们使用malloc()函数调用)。</span></span></p> <p><span lang="zh-CN"><span>原文:<a href="/misc/goto?guid=4958183169943736465">http://www.fortystones.com/tips-to-make-c-program-effective/</a></span></span></p>