......................................
}
SECTIONS
{
...................................
PieVectTable : > PIE_VECT, PAGE = 1
.....................................
}
2.在C中制定該中斷的結構體:
#pragma DATA_SECTION(PieVectTable,"PieVectTable");
struct PIE_VECT_TABLE PieVectTable;(在DSP28_GlobalVariableDefs.C中初始化)
3.用一組常數(shù)(按照中斷向量的順序)初始化該名字為PIE_VECT_TABLE的表:
typedef interrupt void(*PINT)(void);這里有些一問,一下應該為函數(shù)名??
// Define Vector Table:
struct PIE_VECT_TABLE {
// Reset is never fetched from this table.
// It will always be fetched from 0x3FFFC0 in either
// boot ROM or XINTF Zone 7 depending on the state of
// the XMP/MC input signal. On the F2810 it is always
// fetched from boot ROM.
PINT PIE1_RESERVED;
PINT PIE2_RESERVED;
PINT PIE3_RESERVED;
PINT PIE4_RESERVED;
PINT PIE5_RESERVED;
PINT PIE6_RESERVED;
PINT PIE7_RESERVED;
PINT PIE8_RESERVED;
PINT PIE9_RESERVED;
PINT PIE10_RESERVED;
PINT PIE11_RESERVED;
PINT PIE12_RESERVED;
PINT PIE13_RESERVED;
// Non-Peripheral Interrupts:
PINT XINT13; // XINT13
PINT TINT2; // CPU-Timer2
PINT DATALOG; // Datalogging interrupt
PINT RTOSINT; // RTOS interrupt
PINT EMUINT; // Emulation interrupt
PINT XNMI; // Non-maskable interrupt
PINT ILLEGAL; // Illegal operation TRAP
PINT USER0; // User Defined trap 0
PINT USER1; // User Defined trap 1
PINT USER2; // User Defined trap 2
PINT USER3; // User Defined trap 3
PINT USER4; // User Defined trap 4
PINT USER5; // User Defined trap 5
PINT USER6; // User Defined trap 6
PINT USER7; // User Defined trap 7
PINT USER8; // User Defined trap 8
PINT USER9; // User Defined trap 9
PINT USER10; // User Defined trap 10
PINT USER11; // User Defined trap 11
// Group 1 PIE Peripheral Vectors:
PINT PDPINTA; // EV-A
PINT PDPINTB; // EV-B
PINT rsvd1_3;
PINT XINT1;
PINT XINT2;
PINT ADCINT; // ADC
PINT TINT0; // Timer 0
PINT WAKEINT; // WD
// Group 2 PIE Peripheral Vectors:
PINT CMP1INT; // EV-A
PINT CMP2INT; // EV-A
PINT CMP3INT; // EV-A
PINT T1PINT; // EV-A
PINT T1CINT; // EV-A
PINT T1UFINT; // EV-A
PINT T1OFINT; // EV-A
PINT rsvd2_8;
// Group 3 PIE Peripheral Vectors:
PINT T2PINT; // EV-A
PINT T2CINT; // EV-A
PINT T2UFINT; // EV-A
PINT T2OFINT; // EV-A
PINT CAPINT1; // EV-A
PINT CAPINT2; // EV-A
PINT CAPINT3; // EV-A
PINT rsvd3_8;
// Group 4 PIE Peripheral Vectors:
PINT CMP4INT; // EV-B
PINT CMP5INT; // EV-B
PINT CMP6INT; // EV-B
PINT T3PINT; // EV-B
PINT T3CINT; // EV-B
PINT T3UFINT; // EV-B
PINT T3OFINT; // EV-B
PINT rsvd4_8;
// Group 5 PIE Peripheral Vectors:
PINT T4PINT; // EV-B
PINT T4CINT; // EV-B
PINT T4UFINT; // EV-B
PINT T4OFINT; // EV-B
PINT CAPINT4; // EV-B
PINT CAPINT5; // EV-B
PINT CAPINT6; // EV-B
PINT rsvd5_8;
// Group 6 PIE Peripheral Vectors:
PINT SPIRXINTA; // SPI-A
PINT SPITXINTA; // SPI-A
PINT rsvd6_3;
PINT rsvd6_4;
PINT MRINTA; // McBSP-A
PINT MXINTA; // McBSP-A
PINT rsvd6_7;
PINT rsvd6_8;
// Group 7 PIE Peripheral Vectors:
PINT rsvd7_1;
PINT rsvd7_2;
PINT rsvd7_3;
PINT rsvd7_4;
PINT rsvd7_5;
PINT rsvd7_6;
PINT rsvd7_7;
PINT rsvd7_8;
// Group 8 PIE Peripheral Vectors:
PINT rsvd8_1;
PINT rsvd8_2;
PINT rsvd8_3;
PINT rsvd8_4;
PINT rsvd8_5;
PINT rsvd8_6;
PINT rsvd8_7;
PINT rsvd8_8;
// Group 9 PIE Peripheral Vectors:
PINT RXAINT; // SCI-A
PINT TXAINT; // SCI-A
PINT RXBINT; // SCI-B
PINT TXBINT; // SCI-B
PINT ECAN0INTA; // eCAN
PINT ECAN1INTA; // eCAN
PINT rsvd9_7;
PINT rsvd9_8;
// Group 10 PIE Peripheral Vectors:
PINT rsvd10_1;
PINT rsvd10_2;
PINT rsvd10_3;
PINT rsvd10_4;
PINT rsvd10_5;
PINT rsvd10_6;
PINT rsvd10_7;
PINT rsvd10_8;
// Group 11 PIE Peripheral Vectors:
PINT rsvd11_1;
PINT rsvd11_2;
PINT rsvd11_3;
PINT rsvd11_4;
PINT rsvd11_5;
PINT rsvd11_6;
PINT rsvd11_7;
PINT rsvd11_8;
// Group 12 PIE Peripheral Vectors:
PINT rsvd12_1;
PINT rsvd12_2;
PINT rsvd12_3;
PINT rsvd12_4;
PINT rsvd12_5;
PINT rsvd12_6;
PINT rsvd12_7;
PINT rsvd12_8;
};
然后在使我們在.cmd文件中定義的表有以上屬性:
extern struct PIE_VECT_TABLE PieVectTable;(在.h文件中)
4.初始化該表(在.c文件中)使之能夠為主程序所使用:
const struct PIE_VECT_TABLE PieVectTableInit = {
PIE_RESERVED, // Reserved space
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
PIE_RESERVED,
// Non-Peripheral Interrupts
INT13_ISR, // XINT13 or CPU-Timer 1
INT14_ISR, // CPU-Timer2
DATALOG_ISR, // Datalogging interrupt
RTOSINT_ISR, // RTOS interrupt
EMUINT_ISR, // Emulation interrupt
NMI_ISR, // Non-maskable interrupt
ILLEGAL_ISR, // Illegal operation TRAP
USER0_ISR, // User Defined trap 0
USER1_ISR, // User Defined trap 1
USER2_ISR, // User Defined trap 2
USER3_ISR, // User Defined trap 3
USER4_ISR, // User Defined trap 4
USER5_ISR, // User Defined trap 5
USER6_ISR, // User Defined trap 6
USER7_ISR, // User Defined trap 7
USER8_ISR, // User Defined trap 8
USER9_ISR, // User Defined trap 9
USER10_ISR, // User Defined trap 10
USER11_ISR, // User Defined trap 11
// Group 1 PIE Vectors
PDPINTA_ISR, // EV-A
PDPINTB_ISR, // EV-B
rsvd_ISR,
XINT1_ISR,
XINT2_ISR,
ADCINT_ISR, // ADC
TINT0_ISR, // Timer 0
WAKEINT_ISR, // WD
// Group 2 PIE Vectors
CMP1INT_ISR, // EV-A
CMP2INT_ISR, // EV-A
CMP3INT_ISR, // EV-A
T1PINT_ISR, // EV-A
T1CINT_ISR, // EV-A
T1UFINT_ISR, // EV-A
T1OFINT_ISR, // EV-A
rsvd_ISR,
// Group 3 PIE Vectors
T2PINT_ISR, // EV-A
T2CINT_ISR, // EV-A
T2UFINT_ISR, // EV-A
T2OFINT_ISR, // EV-A
CAPINT1_ISR, // EV-A
CAPINT2_ISR, // EV-A
CAPINT3_ISR, // EV-A
rsvd_ISR,
// Group 4 PIE Vectors
CMP4INT_ISR, // EV-B
CMP5INT_ISR, // EV-B
CMP6INT_ISR, // EV-B
T3PINT_ISR, // EV-B
T3CINT_ISR, // EV-B
T3UFINT_ISR, // EV-B
T3OFINT_ISR, // EV-B
rsvd_ISR,
// Group 5 PIE Vectors
T4PINT_ISR, // EV-B
T4CINT_ISR, // EV-B
T4UFINT_ISR, // EV-B
T4OFINT_ISR, // EV-B
CAPINT4_ISR, // EV-B
CAPINT5_ISR, // EV-B
CAPINT6_ISR, // EV-B
rsvd_ISR,
// Group 6 PIE Vectors
SPIRXINTA_ISR, // SPI-A
SPITXINTA_ISR, // SPI-A
rsvd_ISR,
rsvd_ISR,
MRINTA_ISR, // McBSP-A
MXINTA_ISR, // McBSP-A
rsvd_ISR,
rsvd_ISR,
// Group 7 PIE Vectors
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
// Group 8 PIE Vectors
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
// Group 9 PIE Vectors
SCIRXINTA_ISR, // SCI-A
SCITXINTA_ISR, // SCI-A
SCIRXINTB_ISR, // SCI-B
SCITXINTB_ISR, // SCI-B
ECAN0INTA_ISR, // eCAN
ECAN1INTA_ISR, // eCAN
rsvd_ISR,
rsvd_ISR,
// Group 10 PIE Vectors
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
// Group 11 PIE Vectors
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
// Group 12 PIE Vectors
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
rsvd_ISR,
};
//---------------------------------------------------------------------------
// InitPieVectTable:
//---------------------------------------------------------------------------
// This function initializes the PIE vector table to a known state.
// This function must be executed after boot time.
//
void InitPieVectTable(void)
{
int16 i;
Uint32 *Source = (void *) &ieVectTableInit;
Uint32 *Dest = (void *) &ieVectTable;
EALLOW;
for(i=0; i < 128; i++)
*Dest++ = *Source++;
EDIS;
// Enable the PIE Vector Table
PieCtrl.PIECRTL.bit.ENPIE = 1;
}
5.中斷服務程序:
讓以上的數(shù)值指向你所要的服務程序,例如:
PieVectTable.TINT2 = &ISRTimer2;
那么,ISRTimer2也就成了中斷服務程序,
×××切記:一定要在主程序的開始先聲明該程序:
interrupt void ISRTimer2(void);
.............
.............
然后按照您的需要編制該程序:
interrupt void ISRTimer2(void)
{
CpuTimer2.InterruptCount++;
}
從圖上可以看到,整個代碼的編譯過程分為編譯和鏈接兩個過程,編譯對應圖中的大括號括起的部分,其余則為鏈接過程。
編譯過程
編譯過程又可以分成兩個階段:編譯和會匯編。
編譯
編譯是讀取源程序(字符流),對之進行詞法和語法的分析,將高級語言指令轉換為功能等效的匯編代碼,源文件的編譯過程包含兩個主要階段:
第一個階段是預處理階段,在正式的編譯階段之前進行。預處理階段將根據(jù)已放置在文件中的預處理指令來修改源文件的內容。如#include指令就是一個預處理指令,它把頭文件的內容添加到.cpp文件中。這個在編譯之前修改源文件的方式提供了很大的靈活性,以適應不同的計算機和操作系統(tǒng)環(huán)境的限制。一個環(huán)境需要的代碼跟另一個環(huán)境所需的代碼可能有所不同,因為可用的硬件或操作系統(tǒng)是不同的。在許多情況下,可以把用于不同環(huán)境的代碼放在同一個文件中,再在預處理階段修改代碼,使之適應當前的環(huán)境。
主要是以下幾方面的處理:
(1)宏定義指令,如 #define a b
對于這種偽指令,預編譯所要做的是將程序中的所有a用b替換,但作為字符串常量的 a則不被替換。還有 #undef,則將取消對某個宏的定義,使以后該串的出現(xiàn)不再被替換。
(2)條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif等。
這些偽指令的引入使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理。預編譯程序將根據(jù)有關的文件,將那些不必要的代碼過濾掉
(3) 頭文件包含指令,如#include "FileName"或者#include <FileName>等。
在頭文件中一般用偽指令#define定義了大量的宏(最常見的是字符常量),同時包含有各種外部符號的聲明。采用頭文件的目的主要是為了使某些定義可以供多個不同的C源程序使用。因為在需要用到這些定義的C源程序中,只需加上一條#include語句即可,而不必再在此文件中將這些定義重復一遍。預編譯程序將把頭文件中的定義統(tǒng)統(tǒng)都加入到它所產生的輸出文件中,以供編譯程序對之進行處理。包含到c源程序中的頭文件可以是系統(tǒng)提供的,這些頭文件一般被放在 /usr/include目錄下。在程序中#include它們要使用尖括號(<>)。另外開發(fā)人員也可以定義自己的頭文件,這些文件一般與 c源程序放在同一目錄下,此時在#include中要用雙引號("")。
(4)特殊符號,預編譯程序可以識別一些特殊的符號。
例如在源程序中出現(xiàn)的LINE標識將被解釋為當前行號(十進制數(shù)),F(xiàn)ILE則被解釋為當前被編譯的C源程序的名稱。預編譯程序對于在源程序中出現(xiàn)的這些串將用合適的值進行替換。
預編譯程序所完成的基本上是對源程序的“替代”工作。經(jīng)過此種替代,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件。這個文件的含義同沒有經(jīng)過預處理的源文件是相同的,但內容有所不同。下一步,此輸出文件將作為編譯程序的輸出而被翻譯成為機器指令。
第二個階段編譯、優(yōu)化階段,經(jīng)過預編譯得到的輸出文件中,只有常量;如數(shù)字、字符串、變量的定義,以及C語言的關鍵字,如main,if,else,for,while,{,}, +,-,*,\等等。
編譯程序所要作得工作就是通過詞法分析和語法分析,在確認所有的指令都符合語法規(guī)則之后,將其翻譯成等價的中間代碼表示或匯編代碼。
優(yōu)化處理是編譯系統(tǒng)中一項比較艱深的技術。它涉及到的問題不僅同編譯技術本身有關,而且同機器的硬件環(huán)境也有很大的關系。優(yōu)化一部分是對中間代碼的優(yōu)化。這種優(yōu)化不依賴于具體的計算機。另一種優(yōu)化則主要針對目標代碼的生成而進行的。
對于前一種優(yōu)化,主要的工作是刪除公共表達式、循環(huán)優(yōu)化(代碼外提、強度削弱、變換循環(huán)控制條件、已知量的合并等)、復寫傳播,以及無用賦值的刪除,等等。
后一種類型的優(yōu)化同機器的硬件結構密切相關,最主要的是考慮是如何充分利用機器的各個硬件寄存器存放的有關變量的值,以減少對于內存的訪問次數(shù)。另外,如何根據(jù)機器硬件執(zhí)行指令的特點(如流水線、RISC、CISC、VLIW等)而對指令進行一些調整使目標代碼比較短,執(zhí)行的效率比較高,也是一個重要的研究課題。
匯編
匯編實際上指把匯編語言代碼翻譯成目標機器指令的過程。對于被翻譯系統(tǒng)處理的每一個C語言源程序,都將最終經(jīng)過這一處理而得到相應的目標文件。目標文件中所存放的也就是與源程序等效的目標的機器語言代碼。目標文件由段組成。通常一個目標文件中至少有兩個段:
代碼段:該段中所包含的主要是程序的指令。該段一般是可讀和可執(zhí)行的,但一般卻不可寫。
數(shù)據(jù)段:主要存放程序中要用到的各種全局變量或靜態(tài)的數(shù)據(jù)。一般數(shù)據(jù)段都是可讀,可寫,可執(zhí)行的。
UNIX環(huán)境下主要有三種類型的目標文件:
(1)可重定位文件
其中包含有適合于其它目標文件鏈接來創(chuàng)建一個可執(zhí)行的或者共享的目標文件的代碼和數(shù)據(jù)。
(2)共享的目標文件
這種文件存放了適合于在兩種上下文里鏈接的代碼和數(shù)據(jù)。第一種是鏈接程序可把它與其它可重定位文件及共享的目標文件一起處理來創(chuàng)建另一個目標文件;
第二種是動態(tài)鏈接程序將它與另一個可執(zhí)行文件及其它的共享目標文件結合到一起,創(chuàng)建一個進程映象。
(3)可執(zhí)行文件
它包含了一個可以被操作系統(tǒng)創(chuàng)建一個進程來執(zhí)行之的文件。匯編程序生成的實際上是第一種類型的目標文件。對于后兩種還需要其他的一些處理方能得到,這個就是鏈接程序的工作了。
鏈接過程
由匯編程序生成的目標文件并不能立即就被執(zhí)行,其中可能還有許多沒有解決的問題。
例如,某個源文件中的函數(shù)可能引用了另一個源文件中定義的某個符號(如變量或者函數(shù)調用等);在程序中可能調用了某個庫文件中的函數(shù),等等。所有的這些問題,都需要經(jīng)鏈接程序的處理方能得以解決。
鏈接程序的主要工作就是將有關的目標文件彼此相連接,也即將在一個文件中引用的符號同該符號在另外一個文件中的定義連接起來,使得所有的這些目標文件成為一個能夠誒操作系統(tǒng)裝入執(zhí)行的統(tǒng)一整體。
根據(jù)開發(fā)人員指定的同庫函數(shù)的鏈接方式的不同,鏈接處理可分為兩種:
(1)靜態(tài)鏈接
在這種鏈接方式下,函數(shù)的代碼將從其所在地靜態(tài)鏈接庫中被拷貝到最終的可執(zhí)行程序中。這樣該程序在被執(zhí)行時這些代碼將被裝入到該進程的虛擬地址空間中。靜態(tài)鏈接庫實際上是一個目標文件的集合,其中的每個文件含有庫中的一個或者一組相關函數(shù)的代碼。
(2) 動態(tài)鏈接
在此種方式下,函數(shù)的代碼被放到稱作是動態(tài)鏈接庫或共享對象的某個目標文件中。鏈接程序此時所作的只是在最終的可執(zhí)行程序中記錄下共享對象的名字以及其它少量的登記信息。在此可執(zhí)行文件被執(zhí)行時,動態(tài)鏈接庫的全部內容將被映射到運行時相應進程的虛地址空間。動態(tài)鏈接程序將根據(jù)可執(zhí)行程序中記錄的信息找到相應的函數(shù)代碼。
對于可執(zhí)行文件中的函數(shù)調用,可分別采用動態(tài)鏈接或靜態(tài)鏈接的方法。使用動態(tài)鏈接能夠使最終的可執(zhí)行文件比較短小,并且當共享對象被多個進程使用時能節(jié)約一些內存,因為在內存中只需要保存一份此共享對象的代碼。但并不是使用動態(tài)鏈接就一定比使用靜態(tài)鏈接要優(yōu)越。在某些情況下動態(tài)鏈接可能帶來一些性能上損害。
我們在linux使用的gcc編譯器便是把以上的幾個過程進行捆綁,使用戶只使用一次命令就把編譯工作完成,這的確方便了編譯工作,但對于初學者了解編譯過程就很不利了,下圖便是gcc代理的編譯過程:
從上圖可以看到:
預編譯
將.c 文件轉化成 .i文件
使用的gcc命令是:gcc –E
對應于預處理命令cpp
編譯
將.c/.h文件轉換成.s文件
使用的gcc命令是:gcc –S
對應于編譯命令 cc –S
匯編
將.s 文件轉化成 .o文件
使用的gcc 命令是:gcc –c
對應于匯編命令是 as
鏈接
將.o文件轉化成可執(zhí)行程序
使用的gcc 命令是: gcc
對應于鏈接命令是 ld
總結起來編譯過程就上面的四個過程:預編譯、編譯、匯編、鏈接。Lia了解這四個過程中所做的工作,對我們理解頭文件、庫等的工作過程是有幫助的,而且清楚的了解編譯鏈接過程還對我們在編程時定位錯誤,以及編程時盡量調動編譯器的檢測錯誤會有很大的幫助的。
]]>#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情:
1). #define 語法的基本知識(例如:不能以分號結束,括號的使用,等等)
2). 懂得預處理器將為你計算常數(shù)表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。
3). 意識到這個表達式將使一個16位機的整型數(shù)溢出-因此要用到長整型符號L,告訴編譯器這個常數(shù)是的長整型數(shù)。
4). 如果你在你的表達式中用到UL(表示無符號長整型),那么你有了一個好的起點。記住,第一印象很重要。
2. 寫一個“標準”宏MIN,這個宏輸入兩個參數(shù)并返回較小的一個。
#define MIN(A,B) ((A) <= (B) (A) : (B))
這個測試是為下面的目的而設的:
1). 標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變?yōu)闃藴蔆的一部分,宏是方便產生嵌入代碼的唯一方法,對于嵌入式系統(tǒng)來說,為了能達到要求的性能,嵌入代碼經(jīng)常是必須的方法。
2). 三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產生比if-then-else更優(yōu)化的代碼,了解這個用法是很重要的。
3). 懂得在宏中小心地把參數(shù)用括號括起來
4). 我也用這個問題開始討論宏的副作用,例如:當你寫下面的代碼時會發(fā)生什么事?
least = MIN(*p++, b);
3. 預處理器標識#error的目的是什么?
如果你不知道答案,請看參考文獻1。這問題對區(qū)分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種
問題的答案。當然如果你不是在找一個書呆子,那么應試者最好希望自己不要知道答案。
死循環(huán)(Infinite loops)
4. 嵌入式系統(tǒng)中經(jīng)常要用到無限循環(huán),你怎么樣用C編寫死循環(huán)呢?
這個問題用幾個解決方案。我首選的方案是:
while(1) { }
一些程序員更喜歡如下方案:
for(;;) { }
這個實現(xiàn)方式讓我為難,因為這個語法沒有確切表達到底怎么回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的
基本原理。如果他們的基本答案是:“我被教著這樣做,但從沒有想到過為什么。”這會給我留下一個壞印象。
第三個方案是用 goto
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。
數(shù)據(jù)聲明(Data declarations)
5. 用變量a給出下面的定義
a) 一個整型數(shù)(An integer)
b) 一個指向整型數(shù)的指針(A pointer to an integer)
c) 一個指向指針的的指針,它指向的指針是指向一個整型數(shù)(A pointer to a pointer to an integer)
d) 一個有10個整型數(shù)的數(shù)組(An array of 10 integers)
e) 一個有10個指針的數(shù)組,該指針是指向一個整型數(shù)的(An array of 10 pointers to integers)
f) 一個指向有10個整型數(shù)數(shù)組的指針(A pointer to an array of 10 integers)
g) 一個指向函數(shù)的指針,該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數(shù)組,該指針指向一個函數(shù),該函數(shù)有一個整型參數(shù)并返回一個整型數(shù) ( An array of ten pointers to functions that take an integer argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人們經(jīng)常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。
但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者如果不知道
所有的答案(或至少大部分答案),那么也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那么他又能為什么出準備呢?
Static
6. 關鍵字static的作用是什么?
這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:
1). 在函數(shù)體,一個被聲明為靜態(tài)的變量在這一函數(shù)被調用過程中維持其值不變。
2). 在模塊內(但在函數(shù)體外),一個被聲明為靜態(tài)的變量可以被模塊內所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問。它是一個本地的全局變量。
3). 在模塊內,一個被聲明為靜態(tài)的函數(shù)只可被這一模塊內的其它函數(shù)調用。那就是,這個函數(shù)被限制在聲明它的模塊的本地范圍內使用。
大多數(shù)應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化數(shù)據(jù)和代碼范圍的好處和重要性。
Const
7.關鍵字const是什么含意?
我 只要一聽到被面試者說:“const意味著常數(shù)”,我就知道我正在和一個業(yè)余者打交道。去年Dan Saks已經(jīng)在他的文章里完全概括了const的所有 用法,因此ESP(譯者:Embedded Systems Programming)的每一位讀者應該非常熟悉const能做什么和不能做什么.如果你從沒有讀到那篇文章,只要能說出const意味著“只讀”就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)如果應試者能正確回答這個問題,我將問他一個附加的問題:下面的聲明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的作用是一樣,a是一個常整型數(shù)。第三個意味著a是一個指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個意思a是一個指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后一個意味著a是一個指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時指針也是不可修改的)。如果應試者能正確回答這些問題,那么他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字 const,也還是能很容易寫出功能正確的程序,那么我為什么還要如此看重關鍵字const呢?我也如下的幾下理由:
1). 關鍵字const 的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數(shù)為常量是為了告訴了用戶這個參數(shù)的應用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。)
2). 通過給優(yōu)化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。
3). 合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數(shù),防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現(xiàn)。
Volatile
8. 關鍵字volatile有什么含意 并給出三個不同的例子。
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1). 并行設備的硬件寄存器(如:狀態(tài)寄存器)
2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
3). 多線程應用中被幾個任務共享的變量
回答不出這個問題的人是不會被雇傭的。我認為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。嵌入式系統(tǒng)程序員經(jīng)常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
1). 一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
2). 一個指針可以是volatile 嗎?解釋為什么。
3). 下面的函數(shù)有什么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2). 是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
3). 這段代碼的有個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數(shù),編譯器將產生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulation)
9. 嵌入式系統(tǒng)總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。
對這個問題有三種基本的反應
1). 不知道如何下手。該被面者從沒做過任何嵌入式系統(tǒng)的工作。
2). 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到Infineon為其較復雜的通信芯片寫的驅動程序,它用到了bit fields因此完全對我無用,因為我的編譯器用其它的方式 來實現(xiàn)bit fields的。從道德講:永遠不要讓一個非嵌入式的家伙粘實際硬件的邊。
3). 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下:
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些人喜歡為設置和清除值而定義一個掩碼同時定義一些說明常數(shù),這也是可以接受的。我希望看到幾個要點:說明常數(shù)、|=和&=~操作。
訪問固定的內存位置(Accessing fixed memory locations)
10. 嵌入式系統(tǒng)經(jīng)常具有要求程序員去訪問某特定的內存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。
這一問題測試你是否知道為了訪問一絕對地址把一個整型數(shù)強制轉換(typecast)為一指針是合法的。這一問題的實現(xiàn)方式隨著個人風格不同而不同。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一個較晦澀的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。
中斷(Interrupts)
11. 中斷是嵌入式系統(tǒng)中重要的組成部分,這導致了很多編譯開發(fā)商提供一種擴展—讓標準C支持中斷。具代表事實是,產生了一個新的關鍵字 __interrupt。下面的代碼就使用了__interrupt關鍵字去定義了一個中斷服務子程序(ISR),請評論一下這段代碼的。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
這個函數(shù)有太多的錯誤了,以至讓人不知從何說起了:
1). ISR 不能返回一個值。如果你不懂這個,那么你不會被雇用的。
2). ISR 不能傳遞參數(shù)。如果你沒有看到這一點,你被雇用的機會等同第一項。
3). 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
4). 與第三點一脈相承,printf()經(jīng)常有重入和性能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到后兩點,那么你的被雇用前景越來越光明了。
代碼例子(Code examples)
12 . 下面的代碼輸出是什么,為什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) puts("> 6") : puts("<= 6");
}
這個問題測試你是否懂得C語言中的整數(shù)自動轉換原則,我發(fā)現(xiàn)有些開發(fā)者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是“>6”。原因是當表達式中存在有符號類型和無符號類型時所有的操作數(shù)都自動轉換為無符號類型。因此-20變成了一個非常大的正整數(shù),所以該表達式計算出的結果大于6。這一點對于應當頻繁用到無符號數(shù)據(jù)類型的嵌入式系統(tǒng)來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。
13. 評價下面的代碼片斷:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下:
unsigned int compzero = ~0;
這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經(jīng)驗里,好的嵌入式程序員非常準確地明白硬件的細節(jié)和它的局限,然而PC機程序往往把硬件作為一個無法避免的煩惱。
到了這個階段,應試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那么這個測試就在這里結束了。但如果顯然應試者做得不錯,那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優(yōu)秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…
動態(tài)內存分配(Dynamic memory allocation)
14. 盡管不像非嵌入式計算機那么常見,嵌入式系統(tǒng)還是有從堆(heap)中動態(tài)分配內存的過程的。那么嵌入式系統(tǒng)中,動態(tài)分配內存可能發(fā)生的問題是什么?
這 里,我期望應試者能提到內存碎片,碎片收集的問題,變量的持行時間等等。這個主題已經(jīng)在ESP雜志中被廣泛地討論過了(主要是 P.J. Plauger, 他的解釋遠遠超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應試者進入一種虛假的安全感覺后,我拿出這么一個小節(jié)目:下面的代碼片段的輸出是什么,為什么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
這是一個有趣的問題。最近在我的一個同事不經(jīng)意把0值傳給了函數(shù)malloc,得到了一個合法的指針之后,我才想到這個問題。這就是上面的代碼,該代碼的輸出是“Got a valid pointer”。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。
Typedef
15. Typedef 在C語言中頻繁用以聲明一個已經(jīng)存在的數(shù)據(jù)類型的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指針。哪種方法更好呢?(如果有的話)為什么?
這是一個非常微妙的問題,任何人答對這個問題(正當?shù)脑颍┦菓敱还驳摹4鸢甘牵簍ypedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一個擴展為
struct s * p1, p2;
上面的代碼定義p1為一個指向結構的指,p2為一個實際的結構,這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指針。
]]>C語言面向過程,OP,C++語言面向對象,OO.
但實際上大家可能能關注到,不管如何OO,如何劃分類和對象,但是,具體到一個功能,還是要用函數(shù)來實現(xiàn),不管如何寫程序,到了函數(shù)內部,其實還是那些if、for、while等等語句,還是面向過程的。
所以,我和我的同事,平時并不會明顯去分辨C和C++的異同,在我們看來,二者本來就是一體的。
其實這個世界上,完全的OO是不存在的,當我們實現(xiàn)一個功能的時候,很多時候,就是界定一些數(shù)據(jù),針對數(shù)據(jù)添加一些處理流程,獲得一個結果,這件事情,本質上就是個過程。
但C++還是很有用的。
因為很多年以前,大家覺得傳統(tǒng)面向過程的語言,如C,如Basic,如Pascal,都有一個缺點,就是在程序中,彼此暴露了太多的細節(jié),這造成各個功能之間,由于程序員的失誤,很容易發(fā)生粘連,聯(lián)系。換而言之,就算是非法訪問,通常也是合法而成功的,不會被編譯器檢查出來。比如C就允許全局變量和遠指針調用。
這在開發(fā)大型系統(tǒng)的時候,就出現(xiàn)了bug率居高不下,大型工程項目很難完成的缺點。
正是因為此,大家在上世紀七十年代,提出了模塊化開發(fā)的思想,試圖通過各個模塊的獨立開發(fā)和運行,強行阻斷各個模塊不必要的耦合性,來達到讓程序穩(wěn)定的目的。
但這樣畢竟是人工在操作,是人做的,就可能會犯錯誤,大家覺得有必要在編譯器這一級,要強調模塊之間的獨立性。
這個時候,大家經(jīng)過分析,發(fā)現(xiàn)程序其實核心是和數(shù)據(jù)打交道的,一個數(shù)據(jù),業(yè)務上只要界定了用途,基本上,可能的訪問方法就確定得差不多了,那么,我們有沒有可能,把一類數(shù)據(jù),以及其方法,從編譯器的角度區(qū)別開來,構建獨立模塊,外部模塊僅能訪問到它允許訪問的方法,其他的方法,全部報錯,拒絕編譯呢?
答案是當然可以。大家就這么做了。那么,我們看看,一類數(shù)據(jù)和其允許的方法的集合,就是對象啦,在這個思想上,OO,面向對象的思想就產生了。
最初,這個語言是一個新的語言,好像叫smalltalk吧,不過,這個時候的語言,還是實驗室產品,沒有投入商業(yè)運營。
但這個時候,市場上,由于UNIX的推動,C語言基本上已經(jīng)一統(tǒng)天下了。很多人都學的C語言,讓大家去學習一門新語言,尤其是開發(fā)思路完全不同的語言,是不可想象的,成本太高。大家就想,能不能折中一下,以C的語法為藍本,開發(fā)一套OO的語言,C++就這么誕生了。
其實OP到OO,C到C++,本質上講,就是一個數(shù)據(jù)私有化的過程。甚至整個語言的發(fā)展史,也是一個數(shù)據(jù)私有化的過程。如匯編語言,其實是沒有私有數(shù)據(jù)的,所有的內存都可以被訪問。
人們通過編譯器的界定和完善,逐漸實現(xiàn)數(shù)據(jù)私有化,最終的目標就是實現(xiàn)一個軟件系統(tǒng)內部各個模塊之間,高內聚,低耦合的目標,最終保證程序員的產品質量,進而提高生產率。
至于后面的泛型編程,多態(tài),繼承等等,無非是在這條路上繼續(xù)了一步而已,當然,也是為了盡量減少程序員的代碼輸入量,進一步提升生產效率而已。
所以,從數(shù)據(jù)組織上講,C++比C先進了一大步,但從功能實現(xiàn)上講,C++和C并無本質不同。C++到現(xiàn)在,都不是一種完全的面向對象語言,因為它都仍然保留了全局變量。
所以我的意見,兩個一起學,不要刻意去區(qū)分,好像用C就要用純C,沒必要。
我們工程中,系統(tǒng)級的模塊組織,一般式C++的對象,每個單步功能,流程的實現(xiàn),我們都是C的函數(shù),僅僅是放在類里面而已
]]>為什么我第一段要講講c語言呢,因為c語言是一切語言的基礎,這好像是我大學時第一次接觸編程語言的老師跟我說得一句話,如果c語言不能掌握,其他的語言肯定也不會好的。現(xiàn)在這我話我已經(jīng)深信不疑了,不管是java也好,c#也好還是最新的python等等語言吧,在我看來都是以c的基礎來進行的,所謂的新無非是自己不用再想一些函數(shù)庫德算法了,我承認這是一種進步,至少是站在別人的肩膀上做事.效率的時代講究的也是效率這也正是講究效率開發(fā)公司對這些方便語言的鐘情。這是可以理解的事情。不過這里我要說的是c++,這是真正的與c語言接觸緊密但是確實又非常實用的語言,人們都說c++是個好東東,可是在開發(fā)的時候卻有時極力的回避這種語言,最終是因為c語言的復雜性。說句我個人的看法,我覺得這樣做是不對的,因為高科技不是一中大眾化的知識,講究的是一個人或一個團隊的智慧,就是因為c++的復雜性就不去用它,或是少去用它而是用更多的方便開發(fā)語言,這種想法是不好的,因為如果一個搞程序的人對算法都不能很好的去自行開發(fā),只會是去沿用別人寫好的東西。充其量跟工廠里的機器裝配工很像,無非是把基本的一些寫好的類庫函數(shù)庫羅列好了。如果真是這樣這是軟件業(yè)的恥辱,也是中國軟件業(yè)的恥辱。
很多時候都是在說為什么人家外國的程序員都他媽的那么牛,一個個根爺爺?shù)乃频模瑢懙脴藴室彩呛玫模鞯臇|西也是好的,就連出的代碼書也是好的。我想這里應該有一定的原因吧。昨天把c++的基本課程設計的書看了一遍,發(fā)現(xiàn)c++如果真正的實用開發(fā),真是太帥了,不管是從代碼的羅列,思想的拓展。真是讓人感覺又一種耳目一新的感覺。c++的這種真正的面向對象是一種完美的結構,當然世界上不可能有那么完美的語言。但是給我的感覺是一種思路的拓寬,讓我對程序的架構有一種新的認識,可能是自己的編程語言基礎差,看了c++的基礎讓我感觸頗深,如果我寫得不像話,那還要請您對原諒畢竟我還是一個在不斷上升中的小小菜鳥,我接觸編程還不到2年,這里也就給我點鼓勵,給您鞠躬了。
]]>丹尼斯·麥卡利斯泰爾·里奇(Dennis MacAlistair Ritchie,1941年9月9日生),出生于美國紐約布朗克斯維爾(Bronxville)。著名的美國計算機科學家,對C語言和其他編程語言、Multics和Unix等操作系統(tǒng)的發(fā)展做出了巨大貢獻。
里奇在哈佛大學學習物理學和應用數(shù)學畢業(yè),1967年他進入貝爾實驗室,主管貝爾實驗室位于新澤西州的計算機科學研究中心的系統(tǒng)軟件研究部門,目前他是朗訊技術公司系統(tǒng)軟件研究部門的領導人。1983年他與肯·湯普遜一起獲得了圖靈獎。理由是他們“研究發(fā)展了通用的操作系統(tǒng)理論,尤其是實現(xiàn)了UNIX操作系統(tǒng)”。1999年兩人為發(fā)展C語言和Unix操作系統(tǒng)一起獲得了美國國家技術獎章。
當有人問里奇為什么使用他使用的方式開發(fā)了C語言的時候,里奇回答說“這樣做看上去很好”,他說任何人在同一地方、同一時間會像他那樣做的。但是其他許多人認為這只不過反映出了里奇的謙虛。C++的開發(fā)者和設計師、里奇在貝爾實驗室的同事比雅尼·斯特勞斯特魯普說:“假如里奇決定在那十年里將他的精力花費在稀奇古怪的數(shù)學上,那么Unix將胎死腹中。”
肯·湯普遜(左)和丹尼斯·里奇(右)
事實上,丹尼斯·里奇與肯·湯普遜兩人發(fā)展了C語言,同時發(fā)展了Unix操作系統(tǒng),在電腦工業(yè)史上占有重要的席位。至今為止C語言在發(fā)展軟件和操作系統(tǒng)時依然是一個非常常用的電腦語言,它對許多現(xiàn)代的編程語言如C++、C#、Objective-C、Java和JavaScript擁有極大的影響。在操作系統(tǒng)方面Unix也具有極大的影響:今天市場上有許多不同的Unix方言如AIX、Solaris、Mac OS X和BSD等,以及與Unix非常相似的系統(tǒng)如Minix和非常普及的Linux操作系統(tǒng)。甚至其Microsoft Windows操作系統(tǒng)與Unix相競爭的微軟為他們的用戶和開發(fā)者提供了與Unix相容的工具和C語言編譯器。
里奇還參加發(fā)展了Unix和C語言的兩個后繼軟件:Plan 9和Inferno操作系統(tǒng)以及Limbo語言。兩者均是基于他以前的工作上發(fā)展的。
在技術討論中,他常被稱為dmr,這是他在貝爾實驗室的Email地址。值得注意的是,雖然丹尼斯·里奇是C語言的作者,他本人最喜歡的程序語言卻是Alef。
Dennis MacAlistair Ritchie
冒泡法排序函數(shù):]]>選擇法排序函數(shù):void bubble( int a[] , int n)
{
int i,j,k;
for (i=1,i<n;i++)
for (j=0;j< n-i-1; j++)
if (a[j]>a[j+1])
{
k=a[j];
a[j]=a[j+1];
a[j+1]=k;
}
}void sort( int a[] , int n)
...{
int i,j,k,t;
for (i=0,i< n-1 ;i++)
...{
k=i ;
for ( j=i+1 ;j<n;j++)
if (a[k]<a[j]) k=j ;
if ( k!=i )
...{
t=a[i];
a[i]=a[k];
a[k]=t;
}
}
}
//main.c
#include "swilib.h"
void ElfKiller(void) { //用于 elf 退出時的相關操作
extern void *ELF_BEGIN;
//這里一般使用 mfree(), freeWS() 來釋放內存
((void (*)(void *))(mfree_adr()))(ELF_BEGIN); // 懶得解釋 :(
}
int main(char *exename, char *fname) { //主函數(shù)
//參數(shù) exename 表示被動使用的ELF? 格式 4:\Zbin\xyz.elf
//參數(shù) fname 傳遞文件名, 格式 0:\Misc\data.txt
//如果 ELF 啟動自身則為 0
char *mem;
int i, err;
int handle;
if (fname) {
//操作標準文件:
handle = fopen(fname, A_ReadWrite+A_BIN+A_Append+A_Create, P_READ+P_WRITE, err);
//表示按記錄文件打開,數(shù)據(jù)添加到文件末尾,如果文件不存在則創(chuàng)建之
//如果為 handle=fopen (fname,A_ReadOnly+A_BIN, 0,err);
//則表示按只讀方式打開文件,具體常數(shù)參看 swilib.h
if (handle != -1) { //-1 = error
mem = malloc(10000); //分配內存: AllocWS() 按行分配 (2b)
if (mem != 0) { //0 = error
i = fread(handle, mem, 10000, err); //返回讀取得字節(jié)數(shù),如果錯誤返回 error。
//放置你的代碼在這里 makesomebody (mem,i);
fwrite(handle, mem, i, err);
mfree(mem); //釋放內存: FreeWS() 按行釋放
}
fclose(handle); //關閉文件
}
}
SUBPROC((void *)ElfKiller); //放這個東西在這里就最好了,不存在也沒關系!
return(0);
}
//PS. 由于 x65 中文件的讀取和記錄是按 32767 字節(jié)的塊操作的,
//因此將 fread() 和 fwrite() 改造為 fread32 () 和 fwrite32()
int fread32(int fh, char *buf, int len, unsigned int *err) { // (c) Rst7
int clen;
int rlen;
int total=0;
while (len) {
if (len > 16384) clen = 16384; else clen = len;
total += (rlen = fread(fh, buf, clen, err));
if (rlen != clen) break;
buf += rlen;
len -= clen;
}
return(total);
}
;Func.asm
PUBLIC ELF_BEGIN
RSEG ELFBEGIN:DATA
ELF_BEGIN
defadr MACRO a,b
PUBLIC a
a EQU b
ENDM
END
//main.c
//屏幕和鍵盤處理
#include "swilib.h"
typedef struct {
GUI gui;
//WSHDR *ws1;
//WSHDR *ws2;
//int i1;
} MAIN_GUI;
typedef struct {
CSM_RAM csm;
int gui_id;
} MAIN_CSM;
const int minus11 = -11;
const unsigned int INK = 0;
const unsigned int PAPER = 1;
volatile int xx = 0, yy = 0; //繪圖坐標
const char bmp[12] = {0xFC, 0x86, 0xB3, 0xA9, 0xB1, 0xA9, 0x81, 0xFF, 0, 0, 0, 0};
const IMGHDR img = {8, 12, 0x1, 0, (char *)bmp};
//============
//屏幕輸出
//============
void DrwImg(IMGHDR *img, int x, int y, int *pen, int *brush) {
RECT rc;
DRWOBJ drwobj;
StoreXYWHtoRECT(rc, x, y, img->w, img->h);
SetPropTo_Obj5(drwobj, &rc, 0, img);
SetColor(drwobj, pen, brush);
DrawObject(drwobj);
}
void DrawScreen(void) {
int *ink = GetPaletteAdrByColorIndex(INK);
int *paper = GetPaletteAdrByColorIndex(PAPER);
int x = xx;
DrwImg((IMGHDR *)img, x, yy, ink, paper);
}
//繪制屏幕
void method0(MAIN_GUI *data) {
DrawScreen();
}
void method1(MAIN_GUI *data, void *(*malloc_adr)(int)) {}
void method2(MAIN_GUI *data, void (*mfree_adr)(void *)) {}
void method3(MAIN_GUI *data, void *(*malloc_adr)(int), void (*mfree_adr)(void *)) {}
void method4(MAIN_GUI *data, void (*mfree_adr)(void *)) {}
void method7(MAIN_GUI *data, void (*mfree_adr)(void *)) {}
int method8(void) {return(0);}
int method9(void) {return(0);}
//============
//按鍵控制
//============
int method5 (MAIN_GUI *data, GUI_MSG *msg) {
//if (msg->gbsmsg->msg==KEY_UP) //釋放按鍵時
if ((msg->gbsmsg->msg == KEY_DOWN) || (msg->gbsmsg->msg == LONG_PRESS)) //按下鍵或者長按鍵時
switch(msg->gbsmsg->submess) {
case RED_BUTTON:
return(1); //發(fā)生 generalFunc 流調用 GUI - > 關閉 GUI
case UP_BUTTON:
if (yy > 0) --yy; break;
case LEFT_BUTTON:
if (xx > 0) --xx; break;
case DOWN_BUTTON:
if (yy < 130) ++yy; break;
case RIGHT_BUTTON:
if ( xx < 120) ++xx; break;
//case GREEN_BUTTON:
//case RIGHT_SOFT:
//case ENTER_BUTTON:
//case LEFT_SOFT:
//case VOL_UP_BUTTON:
//case VOL_DOWN_BUTTON:
//case '0':
//case '9':
//case '#':
//SUBPROC((void *)DoDiskAccess,1);
//降低其他處理的優(yōu)先級以繪制窗口
}
DrawScreen();
return(0);
}
const void *const gui_methods[11] = {
(void *)method0, //Redraw
(void *)method1, //Create
(void *)method2, //Close
(void *)method3, //Focus
(void *)method4, //Unfocus
(void *)method5, //OnKey
0,
(void *)method7, //Destroy
(void *)method8,
(void *)method9,
0
};
const RECT Canvas={0,0,131,175};
void maincsm_oncreate(CSM_RAM *data) {
MAIN_GUI *main_gui = malloc(sizeof (MAIN_GUI));
MAIN_CSM *csm = (MAIN_CSM *)data;
zeromem(main_gui, sizeof (MAIN_GUI));
//ustk=malloc(STKSZ); //為程序分配內存
//info_ws=AllocWS(512);
main_gui->gui.canvas = (void *)(Canvas);
main_gui->gui.flag30 = 2;
main_gui->gui.methods = (void *)gui_methods; //基本方法(見上面)
main_gui->gui.item_ll.data_mfree = (void (*)(void *))mfree_adr(); //我也不清楚:(
csm->csm.state = 0;
csm->csm.unk1 = 0;
csm->gui_id = CreateGUI(main_gui); //直接創(chuàng)建 GUI
}
void Killer(void) { //退出程序
extern void *ELF_BEGIN;
//mfree(ustk); //釋放內存
//FreeWS(info_ws);
((void (*)(void *))(mfree_adr()))(ELF_BEGIN);
}
void maincsm_onclose(CSM_RAM *csm) {
//GBS_StopTimer(light_tmr);
SUBPROC((void *)Killer);
}
int maincsm_onmessage(CSM_RAM *data, GBS_MSG *msg) {
return(1);
}
unsigned short maincsm_name_body[140];
const struct {
CSM_DESC maincsm;
WSHDR maincsm_name;
} MAINCSM = {
{
maincsm_onmessage, //信息進程
maincsm_oncreate, //創(chuàng)建時調用的方法
//如果機型為 S75 移除以下4行
//并在 swilib.h 里取消對 #define NEWSGOLD 這行的注釋
//0,
//0,
//0,
//0,
maincsm_onclose, //關閉時調用的方法
sizeof (MAIN_CSM),
1,
minus11
},
{
maincsm_name_body,
NAMECSM_MAGIC1,
NAMECSM_MAGIC2,
0x0,
139
}
};
int main(char *exename, char *fname) {
char dummy[sizeof (MAIN_CSM)];
//strcpy(filename,fname); //保存數(shù)據(jù)到文件
CreateCSM(MAINCSM.maincsm, dummy, 0);
return 0;
}
前不久CSDN刊登了一篇《C語言已經(jīng)死了》的文章,引起了一些爭論。其實那篇文章是從Ed Burnette的博客上轉載來的,原文題目是“Die, C, die!”,直譯過來應該是《去死吧,C!》,表達的是一種詛咒,而不是判斷。翻譯稱《C語言已經(jīng)死了》,顯然是一種煽風點火的誤讀。CSDN網(wǎng)友對于其觀點已經(jīng)進行了批判,不過坦率地說,由于這些批判基于一個扭曲的翻譯文本,所以不但沒有什么新鮮的地方,而且也沒有抓住原作者的重點。
實際情況是這樣的,最近一段時間,在國外的技術社群里刮起了一股風,不少人在討論Java做為C語言替代者而成為最主流的基礎軟件編程語言的可能性。從大部分人發(fā)表的觀點來看,對于Java替代C的趨勢還是支持的。
基礎軟件是指這樣一類軟件,其主要任務是把計算機的潛能充分發(fā)揮出來,面向上層應用軟件提供一個高效、可靠的功能集。這些軟件會被密集地調用,性能上的一點點滯后都會在實踐中被成百上千倍的放大。所以對于基礎軟件來說,性能至少與可靠性一樣重要。我們在一些基礎軟件的源代碼里,常常看到一些丑陋的設計,看到一些變態(tài)的黑客技巧,在其他的領域里,這是不被鼓勵的,但是在基礎軟件中,這就是合理的,可以接受的。
C語言目前仍在一些領域里堅挺,在操作系統(tǒng)、虛擬機和設備驅動程序開發(fā)方面,它可能是永遠的王者。但是在其他的基礎軟件領域,比如數(shù)據(jù)庫、網(wǎng)絡服務器、圖形圖像處理等,C語言繼續(xù)占據(jù)霸主地位的原因其實只有兩個,一是快,二是熟悉的人多,而且經(jīng)驗豐富。
但是這兩點現(xiàn)在都遭到了挑戰(zhàn)。
首先是速度。Java的執(zhí)行速度在JDK1.4的時候達到了這樣一個水平,就是對于一個一般水平的開發(fā)者來說,他寫的C++程序已經(jīng)不再比對等的Java程序跑得更快了。隨后的JDK 5.0和6.0進一步提高了執(zhí)行性能,由不同的組織舉行的多項評測結果表明,Java與C語言的整體執(zhí)行效率差距在一倍以內,也就是說,素以速度著稱、并且為了速度放棄了很多東西的C語言,現(xiàn)在比裝備齊全的Java只快不到一倍了。這還不算,如果考慮到新的計算環(huán)境,C語言的速度優(yōu)勢有可能僅僅是一個錯覺。因為,世界上只有很少的人有能力在多CPU計算平臺上用C語言寫出又快又正確的大程序,在這些人中間,又只有很少很少的人有能力用C語言寫出一個在大型的、異構的網(wǎng)絡環(huán)境下能夠充分發(fā)揮各節(jié)點計算能力的大規(guī)模并行程序。也就是說,你也許有能力把程序效能提高一倍,從而充分發(fā)揮一臺價值6000元人民幣的PC的計算潛力,為客戶節(jié)省1000元錢。但如果是在一個由1000臺機器組成的大型異構網(wǎng)絡并行計算的環(huán)境下,你寫的C程序恐怕性能還會遠遠低于對應的Java程序,更不要說巨大的后期維護成本,而由此帶來的損失可能是1000萬或者更多。
其次是經(jīng)驗。很多人都宣稱自己的C功力如何如何了得,但是實際上,即使是真正的C高手也不得不花相當可觀的時間來尋找并且調試錯誤,尤其是內存方面的錯誤。大部分用C寫的上規(guī)模的軟件都存在一些內存方面的錯誤,需要花費大量的精力和時間把產品穩(wěn)定下來。這還沒有把安全方面的缺陷考慮在內,現(xiàn)在大部分的開發(fā)者在代碼安全方面的知識都很薄弱,安全漏洞在代碼中相當普遍,而在C語言中,這一不足暴露得格外明顯。最大的挑戰(zhàn)或許得說是并發(fā)問題了,并發(fā)是一個很復雜的問題,需要在相當高的抽象層面上解決,而C語言的抽象機制過于簡單,提供不了高層的抽象,因此在開發(fā)者只能從一些“并發(fā)原語”出發(fā)去構造并發(fā)程序,這跟用鉛筆刀鋸大樹沒什么分別,直截了當?shù)卣f,大部分C程序員根本沒有能力編寫高效無缺陷的并發(fā)程序。
所以殘酷的事實是,當一個人說自己的C語言如何了得,經(jīng)驗如何豐富時,非常可能他說的是,自己在用C語言寫單機、單線程的,不會遭到外界攻擊的,在時間預算上沒有什么壓力,而且用戶能夠忍受一個很長的產品穩(wěn)定期的應用程序方面非常有經(jīng)驗。遺憾的是,市場環(huán)境和計算環(huán)境已經(jīng)完全變化。面對更復雜的計算環(huán)境,用C語言來編寫高質量的大規(guī)模軟件,是只有真正的專家團隊才能完成的工作。如果你曾經(jīng)有過連續(xù)數(shù)日苦苦追蹤和調試一個內存泄露、或者線程錯誤的經(jīng)歷,你就會明白,你可能不是這樣的專家。
相比之下,Java在抽象機制、基礎設施、安全和并發(fā)方面,與C語言比起來,就好像是馬克沁重機槍對弓箭。比如并發(fā),Java 5.0加入的java.util.concurrent包,可能是目前主流語言中對于并發(fā)問題最強有力的支持庫。Java的內存管理和安全機制,也已經(jīng)被實踐證明確實能夠有效地減少程序的缺陷。這也就是那篇詛咒文章的原文的意圖。
所以,我的態(tài)度明確的,我認為Java替代C是一個進步的想法,不過世界上進步的想法很多,能夠美夢成真的卻寥寥無幾。Java是否真的能夠在基礎軟件領域強有力地替代C語言呢?我看至少短期內還做不到,原因如下:
1. 人的問題。能夠用C語言寫出優(yōu)秀基礎軟件的人固然不多,能用Java寫出來的人恐怕更少。Java有好幾百萬開發(fā)者,但是他們在干什么?大部分是去搞企業(yè)級開發(fā)、Web開發(fā)了,有多少人真的理解Java的內存模型?有多少人能夠熟練使用concurrent包中提供的那些工具?很多使用Java多年的人沒有寫過socket程序,不了解Java多線程的開銷,不清楚如何進行性能診斷和調優(yōu),而這些在寫基礎軟件的時候是必備的技能。大部分Java程序員在剛剛學會Java之后就轉向Web開發(fā),把主要精力花費在掌握一個又一個大型的、復雜的、具有厚厚的抽象層和華麗結構的frameworks上,不但對真實計算機體系結構不清楚,對于Java虛擬出來的那個計算環(huán)境也不清楚。因此,要把Java社群編程轉變成能夠擔負起下一代基礎軟件開發(fā)工作的尖兵,不但難度很大,而且必須花費足夠的時間。
2. Java的內存消耗太大。對于系統(tǒng)級程序來說,內存消耗大,就意味著cache命中率降低,與磁盤交換數(shù)據(jù)的可能性增大,對性能的影響還是比較嚴重的。現(xiàn)在很多人還是覺得Java慢,主要的原因已經(jīng)不是Java跑得慢,而是由于內存消耗過大導致的綜合性能下降。這個問題不解決,Java就只能用來做一些比較上層的基礎軟件。也許隨著計算機硬件的發(fā)展,這個問題會逐步得到解決?
3. 風格的問題。這個問題我認為是最嚴重的。基礎軟件開發(fā)崇尚的是自由、直接、透明、簡單、高效,要像匕首一樣鋒利,像戰(zhàn)士一樣勇猛,像農夫一樣樸實,反對繁瑣華麗的設計,反對架床迭屋的層層抽象,反對復雜的結構和不必要的靈活性。而Java社群多年來形成的設計風格與此格格不入,甚至可以說是對立的。Java在意識形態(tài)上是要面向企業(yè)應用軟件的開發(fā),所以特別強調架構,強調設計模式,強調標準,強調規(guī)規(guī)矩矩,強調高姿態(tài),強調一種華貴的宮廷氣質。在C中,你吃飯就是吃飯,捧起碗來喝酒,放下筷子罵娘,甩開膀子抓肉,擼起袖子抹油。而在Java中,你經(jīng)常為了要干某件事,先new一個對象,然后以這個對象為參數(shù)new另一個對象,如此這般重復n遍,得到真正需要的對象,最后就是為了調用那個對象的一個方法,就好比吃飯時焚香洗面,漱口凈手,戰(zhàn)戰(zhàn)兢兢,畢恭畢敬。在C中,遇到問題要像亡命徒,像流氓版程咬金,管你三七二十一,沖上去就是三板斧,還怕劈不死你丫的。在Java里,遇到問題要像宋襄公,要張榜檄文,要名正言順,要禮儀之邦,要把架子拉開了,把譜兒擺足了。Java的口號是,不管劈不劈的死,先把你小子感動了再說。 這套繁瑣的東西,對于基礎軟件開發(fā)來說,既不必要,也很難習慣。需要說明的是,這不是Java語言的問題,其實Java本身不必如此復雜、如此巴洛克。從語言本身來看,Java也可以是輕快直接的,也可是酣暢淋漓的。只不過十多年來幾乎沒有人這樣用過,所以大家已經(jīng)不知道:如果不來個一步三叩首,那么該怎么用Java寫程序?
正是因為上面的這種種原因(可能還不全面),直到最近,第一流的基礎軟件幾乎都還是C語言編寫的,或者至少其核心部分還是以C為主。而且我認為,在短期內,這種局面不會有大的改變。當然,如果Java社群能夠克服上面的這些問題,充分發(fā)揮出Java本身的優(yōu)勢,在基礎領域開發(fā)出一大批一流的支撐軟件,那么局面是可以改變的,而且這種改變也是進步的,值得歡迎的
]]>數(shù)組是由具有相同類型的數(shù)據(jù)元素組成的有序集合。數(shù)組是由數(shù)組名來表示的,數(shù)組中的數(shù)據(jù)由特定的下標來唯一確定。引入數(shù)組的目的,是使用一塊連續(xù)的內存空間存儲多個類型相同的數(shù)據(jù),以解決一批相關數(shù)據(jù)的存儲問題。數(shù)組與普通變量一樣,也必須先定義,后使用。數(shù)組在C51語言的地位舉足輕重,因此深入地了解數(shù)組是很有必要的。下面就對數(shù)組進行詳細的介紹。
(1)一維數(shù)組
一維數(shù)組是最簡單的數(shù)組,用來存放類型相同的數(shù)據(jù)。數(shù)據(jù)的存放是線性連續(xù)的。
用以下例程說明數(shù)組的建立、數(shù)據(jù)操作:
[size=#]#include
[size=#]unsigned char array[10];//定義一個有10個單元的數(shù)組
[size=#]void main()
[size=#]{
[size=#]unsigned char i;
[size=#]for(i=0;i<10;i++)
[size=#]{
[size=#]array=i; //用下標調用數(shù)組中的元素
[size=#]}
[size=#]while(1);
[size=#]}
數(shù)組名是用來表示數(shù)組的標識,其實它是數(shù)組的首地址,即一個指針。不過它所表示的地址是固定的,不能改動。如前幾章所述的相關內容,array[2]與*(array+2)是等效的,不過不能用array++,因為array是常量。
上面[size=#]的程序中的數(shù)組是靜態(tài)建立的,以下例程來用說明數(shù)組的動態(tài)建立。
[size=#]#include
[size=#]#include
[size=#]unsigned char *parray;
[size=#]void main()
[size=#]{
[size=#]unsigned char i;
[size=#]parray=(unsigned char *)malloc(10); //動態(tài)創(chuàng)建一個數(shù)組
[size=#]for(i=0;i<10;i++)
[size=#]{
[size=#]parray=i; //向數(shù)組中賦值
[size=#]}
[size=#]free(parray); //釋放數(shù)組
[size=#]while(1);
[size=#]}
[size=#]字符串是數(shù)組的一個重要特例。它的每個單元的數(shù)據(jù)均為字符類型(char),最后一個單元為'\0'(0x00[size=#]),用來表示字符串的結束。C51函數(shù)庫中提供了專門對字符串進行處理的函數(shù),用以下例程說明:
[size=#]#include
[size=#]#include
[size=#]char s[]={'y','a','h','o','o','\0'};
[size=#]//定義一個字符串,并對它進行初始化,以'\0'結束
[size=#]void main()
[size=#]{
[size=#]char s_temp[10];
[size=#]strcpy(s_temp,s);//strcpy位于string.h頭文件中,實現(xiàn)字符拷貝
[size=#]//s為一個常量,不能s++
[size=#]strcpy(s_temp,"yahoo");//與上面的語句等效
[size=#]while(1);
[size=#]}
[size=#]以下列出幾種字符串的靈活用法,希望能夠幫助讀者深入了解字符串:
[size=#]#include
[size=#]#include
[size=#]char *get_sub_string(char *s,unsigned char n)
[size=#]{
[size=#]int i;int d=0;int fore=0;
[size=#]int len=strlen(s);
[size=#]for(i=0;i< FONT>
[size=#]{
[size=#]if(s==',')
[size=#]{
[size=#]s='\0';
[size=#]d++;
[size=#]if(d==n)
[size=#]{
[size=#]return s+fore;
[size=#]}
[size=#]else
[size=#]{
[size=#]fore=i+1;
[size=#]}
[size=#]}
[size=#]}
[size=#]return NULL;
[size=#]}
[size=#]void main()
[size=#]{
[size=#]unsigned char c;
[size=#]char string[20];
[size=#]c="yahoo"[2]; //c='h'
[size=#]strcpy(string,"123,234,345,456");
[size=#]strcpy(string,get_sub_string(string,2));
[size=#]while(1);
[size=#]}
[size=#](2)二維數(shù)組
[size=#]可由兩個下標確定元素的數(shù)組就稱為二維數(shù)組。其定義的一般形式為:
類型說明符 數(shù)組名[常量表達式1][常量表達式2]
例如:int array[6][4];
定義了一個二維數(shù)組array,有6行4列,共24個元素。
兩個方括號中的常量表達1與常量表達式2規(guī)定了數(shù)組的行數(shù)與列數(shù),從而確定了數(shù)組中的元素個數(shù)。行下標從0開始,最大為5,共6行;列下標也從0開始,最大為3,共4列。數(shù)組中共有6X4=24個元素,具體如下表示:
array[0][0] |
array[0][1] |
array[0][2] |
array[0][3] |
array[1][0] |
array[1][1] |
array[1][2] |
array[1][3] |
array[2][0] |
array[2][1] |
array[2][2] |
array[2][3] |
array[3][0] |
array[3][1] |
array[3][2] |
array[3][3] |
array[4][0] |
array[4][1] |
array[4][2] |
array[4][3] |
array[5][0] |
array[5][1] |
array[5][2] |
array[5][3] |
實際使用時,可以把上述二維數(shù)組看作一個6行4列的矩陣,是一個平面的二維結構。那么編譯程序是如何用一[size=#]維的存儲空間給這樣一個二維結構分配連續(xù)的存儲單元的呢[size=#]C51采用按行存放的方法,即在內存中先存放第0行元素,再存放第1行、第2行、......元素,每行中先存放第0列,接著存放第1列、第2列、......的元素。
[size=#]#include
[siz=#]#include
[size=#]void main()
[size=#]{