身爲一名程序員,或多或少都會了解一點 C 語言,我如今還清楚地記得,大一剛接觸 C 語言時被它所支配的恐懼。C 語言無比強大,被稱爲「高級彙編語言」足以見得它的威力,也能夠看出它常常與計算機底層打交道;它的指針部分更爲精彩(也是最難的部分),那麼咱們就跳過它最難的部分,檢查一下你對 C 語言掌握的程度。程序員
下面是一個函數 sum_elements( ),它的做用是對給定的數組中全部元素求和並返回其值,按照代碼中給定的值去執行,你認爲會獲得什麼結果呢?數組
1#include<stdio.h>
2
3// 一個有 bug 的函數
4float sum_elements(float a[], unsigned length) {
5 int i;
6 float result;
7
8 result = 0;
9 for (i = 0; i <= length - 1; i++)
10 result += a[i];
11
12 return result;
13}
14
15void main()
16{
17 float a[1] = { 0.1 };
18 float sum;
19
20 sum = sum_elements(a, 0);
21 printf("%f\n", sum);
22}
複製代碼
當咱們讓 length = 0 時,想要獲得的結果是 0.000000,可是運行時你會發現該程序會報出內存訪問異常錯誤。你知道是什麼緣由出現這個錯誤嗎?這就是檢驗你功底是否紮實的時候了,先仔細看看代碼,好好想想再繼續往下看。app
... ...函數
怎麼樣,知道是什麼緣由致使這段代碼出現了咱們預料以外的錯誤了嗎?這裏的 bug 是無符號整數(unsigned)致使的。ui
在 C 語言中,無符號整數是 4 個字節,1 個字節爲 8 位,十進制數 0 用二進制表示爲 0000 0000 0000 0000 0000 0000 0000 0000,計算機作減法是經過補碼進行,補碼爲源碼除符號位外各位取反再加一。-1 的補碼爲 1111 1111 1111 1111 1111 1111 1111 1111,計算 length - 1 (0 - 1) 就是求 length 與 -1 的補碼之和,獲得的結果爲 1111 1111 1111 1111 1111 1111 1111 1111,由於以前定義形參的時候將 length 定義爲無符號整數,因此 C 語言將計算結果按照無符號整數解釋,獲得的十進制數字爲 4294967295(2^32 - 1)而不是咱們想要的 -1,循環時 i 初值被賦爲 0 ,一直小於這個數,因此循環會不斷地進行,代碼將試圖訪問數組 a 的非法元素,致使內存訪問異常。spa
這個程序表面上一切正常,很符合正常人思路,數組下標不能爲負數,所以形參 length 用無符號整數表示;中止條件 i <= length - 1 看上去也十分天然。可是將這兩個條件組合在一塊兒,意料以外的事情就發生了。C 語言確實很強大,可是若是咱們的計算機基礎知識不紮實,極可能出現各類奇奇怪怪的 bug。原來我一直以爲學那些枯燥無味的計算機基礎知識沒用,可是越往前走愈加覺計算機基礎知識很重要。書到用時方恨少,出了 bug 找不到。指針
如今這個 bug 的緣由已經找到了,如何修改這個 bug 使得該程序可以順利執行呢?一種方法是修改循環條件,改爲 i < length ;另外一種方法是將形參 length 定義爲 int 類型。code