1. 程式人生 > >C_Primer第1章 初識C語言

C_Primer第1章 初識C語言

本章介紹以下內容:

  • C的歷史和特性
  • 編寫程式的步驟
  • 編譯器和連結器的一些知識
  • C標準

歡迎來到C語言的世界,C是一門功能強大的專業化程式語言,深受業餘程式設計愛好者和專業程式設計師的喜愛。本章為讀者學習這一強大而流行的語言打好基礎,並介紹幾種開發C程式最可能使用的環境。

我們先來了解C語言的起源和一些特性,包括它的缺點。然後,介紹程式設計的起源並探討一些程式設計的基本原則。最後,討論如何在一些常見的系統中執行C程式。

1.1 C語言的起源

1972年,貝樂實驗室的丹尼斯-裡奇(Dennis Ritch)和肯-湯普遜(Ken Thompson)在開發UNIX作業系統時設計了C語言。然而,C語言不完全是裡奇突發奇想而來,他是在B語言(湯普遜發明)的基礎上進行設計。至於B語言的起源,那是另一個故事。C語言設計的初衷是將其作為程式設計師使用的一種程式設計工具,因此,其主要目標是成為有用的語言。

雖然絕大多數語言都以實用為目標,但是通常也會考慮其他方面。例如,Pascal的主要目標是為更好地學習程式設計原理提供紮實的基礎;而BASIC的主要目標是開發出類似英文的語言,讓不熟悉計算機的學生輕鬆學習程式設計。這些目標固然很重要,但是隨著計算機的迅猛發展,它們已經不是主流語言。然而,最初為程式設計開發的C語言,現在已成為首先的程式語言之一。

1.2 選擇C語言的理由

在過去40多年裡,C語言已成為最重要、最流行的程式語言之一。它的成長歸功於使用過的人都對它很滿意。過去的20多年裡,雖然許多人都從C語言轉而使用其他程式語言(如,C++、Objective C、Java等),但是C語言仍憑藉自身實力在眾多語言中脫穎而出。在學習C語言的過程中,會發現它的許多優點(見圖1.1)。下面,我們來看看其中較為突出的幾點。

1.2.1

C是一門流行的語言,融合了電腦科學理論和實踐的控制特性。C語言的設計理念讓使用者能輕鬆地完成自頂向下的規劃、結構化程式設計和模組化設計。因此,用C語言編寫的程式更易懂、更可靠。

1.2.2 高效性

C是高效的語言, 設計上,它充分利用了當前計算機的優勢,因此C程式相對更緊湊,而且執行速度很快。實際上,C語言具有通常是組合語言才具有的微調控制能力(組合語言是為特殊的中央處理單元設計的一系列內部指令,便用助詞符表示:不同的CPU系列使用不同的組合語言),可以根據具體情況微調程式以獲得最大執行速度或最有效地使用記憶體。

 

1.2.3 可移植性

C是可移植的語言,這意味著,在一種系統中編寫的C程式稍作修改或不修改就能在其他系統執行。如需要修改,也只需要簡單更改主程式標頭檔案的少許項即可。大部分語言都希望成為可移植語言,但是,如果經歷過把IBM PB BASIC程式轉成蘋果BASIC(兩者是近親),或者在UNIX系統中執行IBM大型機的FORTRAN程式的人都知道,移植是最麻煩的事。C語言是可移植方面的佼佼者。從8位處理器到克雷超級計算機,許多計算機體系結構都可以使用C編譯器(C編譯器是把C程式碼轉換成計算機內部指令的程式)。但是要注意,程式中針對特殊硬體裝置(如,顯示監視器)或作業系統特殊功能(如,Windows8 或OS X)編寫的部分,通常中不可移植的。

由於C語言與UNIX關係密切,UNIX系統通常會將C編譯器作為軟體包的一部分。安裝Linux時,通常也會安裝C編譯器。供個人計算機使用的C編譯器很多,執行各種版本的Windows和Macintosh(即Mac)的PC都能找到合適的C編譯器。因此,無論是使用家族計算機、專業工作站。還是大型機,都能找到針對特定系統的C編譯器。

1.2.4 強大而靈活

C語言功能強大且靈活(計算機領域經常使用這兩個詞)。例如,功能強大且靈活的UNIX作業系統,大部分是用C語言寫的;其他語言(如,FORTRAN、Perl、Python、Pascal、LISP、Logo、BASIC)的許多編譯器和直譯器都是用C語言編寫的。因此,在UNIX機上使用FORTRAN時,最終是由C程式生成最後的可執行程式。C程式可以用於解決物理學和工程學的問題,甚至可用於製作電影的動畫特效。

1.2.5 面向程式設計師

C語言是為了滿足程式設計師的需求而設計的,程式設計師利用C可以訪問硬體、操作記憶體中的位。C語言有豐富的運算子,能讓程式設計師簡潔地表達自己的意圖,C沒有Pascal嚴謹,但是卻比C++的限制多。這樣的靈活性性既是優點也是缺點。優點是,許多工用C來處理都非常簡潔(如,轉換資料的格式);缺點是,你可能會犯一些莫名其妙的錯誤, 這些錯誤不可以在其他語言中出現。C語言在提供更多自由的同時,也讓使用者承擔了更大的責任。

另外,大多數C實現都有一個大型的庫,包含眾多有用的C函式,這些函式用於處理程式設計師經常需要解決的問題。

1.2.6 缺點

人無完人,金無足赤。C語言也有一些缺點。例如,前面提到的,要享受用C語言自由程式設計樂趣,就必須承擔更多的責任。特別是,C語言使用指標,而涉及指標的程式設計錯誤往往難以察覺。 有句話說的好:想擁有自由就必須時刻保持警惕。

C語言緊湊簡潔,結合了大量的運算子。正因如此,我們也可以編寫出讓人極其費解的程式碼。雖然沒必要強迫自己編寫晦澀的程式碼,但是有興趣寫寫也無妨。試問,除C語言外還為哪種語言舉辦過年度混亂程式碼大大賽?

1.3 C語言的應用範圍

早在2-世紀80年代,C語言就已經成為小型計算機(UNIX系統)使用的主流語言。從那以後,C語言的應用範圍擴充套件到微型機(個人計算機)和大型機(龐然大物)。如圖1.2所示,許多軟體公司都用C語言來開發文書處理程式、電子表格、編譯器和其他產品,因為用C語言編寫的程式緊湊而高效。更重要的是,C程式很方便修改,而且移植到新型號的計算機中也沒什麼問題。

無論是軟體公司、經驗豐富的C程式設計師,還是其他使用者,都能從C語言中收益。越來越多的計算機使用者轉而求助C語言解決一些安全問題。不一定非得是計算機專家也能使用C語言。

20世紀90年代,許多軟體公司開始改用C++來開發大型的程式設計專案。C++在C語言的基礎上嫁接了面向物件程式設計工具(面向物件 程式設計是一門哲學,它通過對語言建模來適應問題,而不是對問題建模以適應語言)。C++幾乎是C的超集,這意味著任何C程式差不多就是一個C++程式。學習C語言,也相當於學習了許多C++的知識。

雖然這些年來C++和JAVA非常流行,但是C語言仍是軟體業中的核心技能。在最想具備的技能中,C語言通常位居前十。特別是,C語言已成為嵌入式系統程式設計的流行語言。也就是說,越來越多的汽車、照相機、DVD播放機和其他現代化裝置的微處理器都用C語言進行程式設計。除此之外,C語言還從長期被FORTRAN獨佔的科學程式設計領域分得一杯羹 。最終,作為開發作業系統的卓越語言,C在Linux開發中扮演著極其重要的角色。因此,在進入21世紀的第2個10年中,C語言仍然保持著強勁的勢頭。

簡而言之,C語言是最重要的程式語言之一,將來也是如此。如果你想拿下一份程式設計的工作,被瓿到是否會C語言是,最好回答是“是”。                                                                                                                                                                                              1.4 計算機能做什麼

 在學習如何用C語言程式設計之前,最好了解一下計算機的工作原理。這些知識有助於你理解用C語言編寫程式和執行C程式時所發生的事情之間有什麼聯絡。

現代的計算機由多種部件構成。中央處理單元(CPU)承擔絕大部分的運算工作。隨機存取記憶體(RAM)是儲存程式和檔案的工作區;而永久記憶體儲存裝置(過去一般批機械硬碟,現在還包括固態硬碟)即使在關閉計算機後,也不會丟失之前儲存的程式和檔案。另外,琮有各種外因裝置(如,鍵盤、滑鼠、觸控式螢幕、監視器)提供人與計算機間的互動。CPU負責處理程式,接下來我們重點討論綜的工作原理。

CPU的工作非常簡單,至少從以下簡短的描述中看是這樣。它從記憶體中獲取並執行一條指令,然後再從記憶體中獲取並執行下條指令,諸如此類(1GHZ的CPU一秒鐘能重複這樣的操作大約十億次,因此,CPU能以驚人的速度從事枯燥的工作)。CPU有自己的小工作區-------由若干個暫存器組成,第個暫存器都可以儲存一個數字。一個暫存器存下一條指令的記憶體地址,CPU使用該地址來獲取更新下一條指令。在獲取指令後,CPU在另一個暫存器中儲存該指令,並更新第1個暫存器存下一條指令的地址,CPU能理解的指令有限(這些指令的集合叫作指令集)。而且,這些指令相當具體,其中的許多指令都是用於請求計算機把一個數字從一個位置移動到另一個位置。例如,從記憶體移到到暫存器。

下面介紹兩個有趣的知識。其一,儲存在計算機中的所有內容都是數字。其二,計算機程式最終必須以數字指令碼(即,機器語言)來表示。                                                                                                                                                                                      1.5 高階計算機語言和編譯器         

高階程式語言(如,C)以多種方式簡化了程式設計工作。首先,不必用數字碼表示指令;其次,使用的指令更貼近你如何想這個問題,而不是類似計算機那樣繁瑣的步驟。

編譯器是把高階語言程式翻譯成計算機能理解的機器語言指令集的短葉馬唐 。程式設計師進行高階思維活動,而編譯順則負責處理冗長乏味的細節工作。

編譯器還有一個優勢,一般而言,不同CPU製造商使用的指令系統和編碼格式不同。

1.6 語言標準

  1987        K&R C        "C語言參考手冊"已成為實現C語言的指導標準。

1.6.1 第1個ANSI/ISO C標準

C89或C90 ANSI C

1.6.2 C99標準

雖然該 標準已釋出了很長時間,

1.6.3 C11標準

1.7 使用C語言的7個步驟

              1.7.1 第1步:定義程式的目標

在動手寫程式之前,要在腦中有清晰的思路。想要程式去做什麼首先自己要明確自己想做什麼,思考你的程式要哪些資訊,要進行哪些計算和控制,以及程式應該要報告什麼資訊。在這一步驟中,不涉及具體的計算機語言,應該用一般術語來描述問題。

1.7.1 第2步:設計程式

對程式該完成佬任務有概念性的認識後,就應該考慮如何用程式來完成它。例如,使用者介面應該是怎樣的?如何組織程式?目標使用者是誰?準備花多長時間來完成這個程式? 

除此之外,還要決定在程式(還可能是輔助檔案)中如何表示資料,以及用什麼方法處理資料,學習C語言之初,遇到的問題都很簡單,沒什麼可選的。但是,隨著要處理的情況越來越複雜,需要決策和考慮的方面也越來越多。通常,選擇一個合適的方式表示資訊要可以容易地設計程式和處理資料。

再次強調,應該用一般術語描述問題,而不是用具體的程式碼。但是,你的某些決策可能取決於語言的特性。例如,在資料表示方面,C的程式設計師就比Pascal的程式設計師有更多選擇。   

1.7.8 說明

程式設計並非像描述那樣是一具線性的過程。有時,要在不同的步驟之間往復。例如,在寫程式碼時發現之前設計不切實際,或者想到了一個更好的解決方案,或者等程式執行後,想改變原來的設計思路,對程式做文字註釋為今後的修改提供了方便。

許多初學者經常忽略第1步和第2步(定義程式目標和設計程式),直接跳到第3步(編寫程式碼)。剛開始學習時,編寫的程式非常簡單,完全可以在腦中構思好整個過程。即使寫鉕了,也很容易發現。但是,隨著編寫的程式越來越龐大、越來越複雜,動腦不動手可不行,而且程式中隱藏的錯誤也越來越難找。最終,那些跳過前兩個步驟的人往往浪費了更多的時間,因為他們寫出的程式難看、缺乏條理、讓人難以理解。。要編寫的程式越大越複雜,事先定義和設計程式環節的工作量就越大。 

磨刀不誤砍柴工,應該養成先規劃再動手編寫程式碼的好習慣,用紙和筆記錄下程式的目標和設計框架。這樣在編寫程式碼的過程中會更加得心應手、條理清晰。

1.8 程式設計機制

程式清單1.2 c程式

#include <stdio.h>
int main(void)
{
    printf("Concrete contains gravel and cement.\n");
    return 0;
}

1.8.1 目的碼檔案、可執行檔案和庫

C程式設計的基本策略是,用程式把原始碼檔案轉換為可執行檔案(其中包含可直接執行的機器語言程式碼)。典型的C實現通過編譯和連結兩個步驟來完成這一過程。編譯器把原始碼轉換成中間程式碼,連結器把中間程式碼和其他程式碼合併,生成可執行檔案。C使用這種分而治之的方法方便對程式進行模組化,可以獨立編譯單獨的模組,稍後再用連結器合併已編譯的模組。通過這種方式,如果只更改某個模組,不必因此重新編譯其他模組。另外,連結器還將你編寫的程式和預編譯的庫程式碼合併。

中間檔案有多種形式。我們在這裡描述的是最普遍的一種形式,即把原始碼轉換為機器語言程式碼,並把結果放在目的碼檔案(或簡稱目標檔案)中(這裡假設原始碼只有一個檔案)。雖然目標檔案中包含機器語言程式碼,但是並不能直接執行該檔案。因為目標檔案中儲存的是編譯器翻譯的原始碼,這還不是一個完整的程式。

目的碼檔案缺失啟動程式碼(startup code)。啟動程式碼充當著程式和作業系統之間蝗介面。例如,可以在MS Windows或Linux系統執行IBM PC相容機。這兩種情況所使用的硬體相同,所以目的碼相同,但是Windows和Linux所需要的啟動程式碼不同,因為這些系統處理程式的方式不同。

目的碼還缺少庫函式。幾乎所有的C程式都要使用C標準庫的函式。例如,concrete.c中就使用了printf()函式。目的碼檔案並不包含該函式的程式碼,它只包含了使用printf()函式的指令。printf()函式真正的程式碼儲存在另一個被稱為庫的檔案中。庫檔案中有許多函式的目的碼。

連結器的作用是,把你編寫的目的碼、系統的標準啟動程式碼和庫程式碼這3部分合併成一個檔案,即執行檔案。對於庫存程式碼,連結器只會把程式中要用到的庫函式程式碼提取出來(見圖1.4)。

                                                                                                               

 簡而言之,目標誰的和可執行檔案都由機器語言指令組成的。然而,目標檔案中只包含編譯器為你編寫的程式碼翻譯的機器語言程式碼,可執行檔案中還包含你編寫的程式中使用的庫函式和啟動程式碼機器程式碼。

1.8.2 UNIX系統

1.8.3 GNU編譯器集合和LLVM專案

gcc和clang

cc -v 

顯示你所使用的編譯器及其版本。

gcc和clang命令都可以根據不同的版本選擇執行時選項來呼叫不同C標準。

gcc -std=c99 inform.c

gcc -std=c1x inform.c

gcc -std=c11 inform.c

第1行呼叫C99標準,第2行呼叫GCC接受C11之前的草案標準,第3行呼叫GCC 接受的C11標準。Clang編譯器在這一點上用法與GCC相同。

1.8.4 LInux系統

要使用GNU提供的gcc 公共域C編譯器。編譯命令類似於:

gcc inform.c

cc為gcc的別名。

1.8.2 PC的命令列編譯器

Cygwin,MinGW,Borland。

1.8.6整合開發環境(Windows)

Microsoft Visual Studio Express

1.8.7 Windows/Linux

1.8.8 Macintosh中的C

Xcode

1.9 本書的組織結構

1.10 本書的約定

2本書使用的系統

"我們的系統"批Imac上執行OS X 10.8.4,Xcode 4.6.2 Clang3.2編譯器。

Microsoft Visual Studio Express 2012 ,Pelles C 7.0 Ubuntu 13.04 Linux GCC 4.7.3。