日韩欧美国产极速不卡一区,国产手机视频在线观看尤物,国产亚洲欧美日韩蜜芽一区,亚洲精品国产免费,亚洲二区三区无码中文,A大片亚洲AV无码一区二区三区,日韩国语国产无码123

您好,歡迎來(lái)到維�(kù)電子市場(chǎng)�(wǎng) 登錄 | 免費(fèi)注冊(cè)

�(lián)系供�(yīng)商在線客服: QQ:2355278032QQ:389939789
廣告

C�(yǔ)言
閱讀�33699�(shí)間:2010-12-07 09:27:20

  C�(yǔ)言是目前世界上流行、使用最廣泛的程序設(shè)�(jì)�(yǔ)言。C�(yǔ)言�(duì)操作系統(tǒng)和系�(tǒng)使用程序以及需要對(duì)硬件�(jìn)行操作的�(chǎng)�,用C�(yǔ)言明顯�(yōu)于其它語(yǔ)言,許多大型應(yīng)用軟件都是用C�(yǔ)言編寫(xiě)�。C�(yǔ)言具有繪圖能力�(qiáng),可移植�,并具備很強(qiáng)的數(shù)�(jù)處理能力,因此適于編�(xiě)系統(tǒng)軟件,三維,二維圖形和動(dòng)�(huà)它是�(shù)值計(jì)算的�(yǔ)言�

�(jié)�(gòu)體定�

  �(yǔ)言中的“結(jié)�(gòu)體”其�(shí)就相�(dāng)于其他語(yǔ)言中的“記錄�,結(jié)�(gòu)體的定義方法如下�

  例如�

Struct student

{  int num;

  char name[20];

  char sex;

  int age;

  float  score;

  char addr[30];

};(注意的分號(hào)不能省略��

  其中行的“student”是該結(jié)�(gòu)體的名稱,花括號(hào)里面的內(nèi)容是�(jié)�(gòu)體的成員�,這是聲明�(jié)�(gòu)體的一般形��也可以在聲明�(jié)�(gòu)體的同時(shí)�(duì)它�(jìn)行初始化,例如:

struct stu

{

        int num;

        char *name;

        char sex;

        float score;

}pupil[5]={

        {101,"Tom",'M',45.8},

        {102,"Mike",'M',62.5},

        {103,"Chris",'F',92.5},

        {104,"Rose",'F',87.6},

        {105,"Nate",'M',58.8}

};

  該代碼中的“pupil[5]”稱為結(jié)�(gòu)體數(shù)�,它屬于�(jié)�(gòu)體變量,在定義該變量的同�(shí)�(duì)它�(jìn)行了初始化操��我們也可以先聲明結(jié)�(gòu)體,然后再對(duì)它�(jìn)行初始化操作�

  例如�

#include <stdio.h>

int main()

{

        struct student

        {              

                char name[8];

                int age;

                char sex[4];

                char depart[20];

                float grade1,grade2,grade3;

        }a;

        float wage;

        char c='Y';

        if(c=='Y'||c=='y')     

        {

                printf("\nName:");

                scanf("%s", a.name);    

                printf("Age:");

                scanf("%d", &a.age);   

                printf("Sex:");

                scanf("%s", a.sex);

                printf("Dept:");

                scanf("%s", a.depart);

                printf("Grade1:");

                scanf("%f", &a.grade1); 

                printf("Grade2:");

                scanf("%f", &a.grade2);

                printf("Grade3:");

                scanf("%f", &a.grade3); 

                wage=a.grade1+a.grade2+a.grade3;

                printf("The sum of wage is %6.2f\n", wage);

        }

        return 0;

}

  該程序中定義了一�(gè)名為“student”的�(jié)�(gòu)�,變量名為“a�,然后再后面“if”包含的符合�(yǔ)句中�(duì)該結(jié)�(gòu)體�(jìn)行初始化。在�,我們可以看出,�(duì)�(jié)�(gòu)體的初始�,只能對(duì)它里面的每�(gè)成員分別初始化�

#include <stdio.h>

struct stu

{

        int num;

        char *name;

        char sex;

        float score;

}pupil[5]={

        {101,"Tom",'M',45.8},

        {102,"Mike",'M',62.5},

        {103,"Chris",'F',92.5},

        {104,"Rose",'F',87.6},

        {105,"Nate",'M',58.8}

};

void avg(struct stu *ps)

{

        int c=0,i;

        float ave,s=0;

        for(i=0;i<5;i++,ps++)

        {

                s+=ps->score;

                if(ps->score<60) c+=1;

        }

        printf("s=%.3f\n",s);

        ave=s/5;

        printf("average=%.3f\ncount=%d\n",ave,c);

}

int main()

{

        struct stu *ps;

        ps=pupil;

        avg(ps);

        return 0;

}

 

  此程序是�(guān)于結(jié)�(gòu)體指針變量作函數(shù)參數(shù),這樣可以提高程序的運(yùn)行效�,程序中我們定義了一�(gè)“stu”的�(jié)�(gòu)�,變量名為“pupil[5]�,并�(duì)其�(jìn)行了初始化,在主函數(shù)中定義了一�(gè)該結(jié)�(gòu)體的指針ps,將pupil賦值給ps,當(dāng)函數(shù)avg()�(diào)用該�(jié)�(gòu)體時(shí),用指針ps�(lái)傳遞pupil的地址,從�,提高了該程序的效率�

  �(jié)�(gòu)體與指針的結(jié)合使用,可以有效的解決現(xiàn)�(shí)生活中的很多�(wèn)�,因此C�(yǔ)言中的指針和結(jié)�(gòu)體應(yīng)該能夠熟練的掌握�

常犯�(cuò)�

  �(yǔ)言的特�(diǎn)是:功能�(qiáng)、使用方便靈活。C編譯的程�?qū)φZ(yǔ)法檢查并不象其它�(yǔ)言那么�(yán)�,這就給編程人員留下“靈活的余地”,但還是由于這�(gè)靈活給程序的�(diào)試帶�(lái)了許多不�,尤其對(duì)初學(xué)C�(yǔ)言的人�(lái)�(shuō),經(jīng)常會(huì)出一些連自己都不知道錯(cuò)在哪里的�(cuò)�??粗绣e(cuò)的程序,不知該如何改�,本人通過(guò)�(duì)C的學(xué)�(xí),積累了一些C編程�(shí)常犯的錯(cuò)�,寫(xiě)給各位學(xué)員以供參��

  1.�(shū)�(xiě)�(biāo)�(shí)符時(shí),忽略了大小�(xiě)字母的區(qū)��

  main()

  {

  int a=5;

  printf("%d",A);

  }

  編譯程序把a(bǔ)和A�(rèn)為是兩�(gè)不同的變量名,而顯示出�(cuò)信息。C�(rèn)為大�(xiě)字母和小�(xiě)字母是兩�(gè)不同的字�。習(xí)慣上,符�(hào)常量名用大寫(xiě),變量名用小�(xiě)表示,以增加可讀��

  2.忽略了變量的類型,�(jìn)行了不合法的�(yùn)��

  main()

  {

  float a,b;

  printf("%d",a%b);

  }

  %是求余運(yùn)�,得到a/b的整余數(shù)。整型變量a和b可以�(jìn)行求余運(yùn)�,而實(shí)型變量則不允許�(jìn)行“求余”運(yùn)��

  3.將字符常量與字符串常量混��

  char c;

  c="a";

  在這里就混淆了字符常量與字符串常量,字符常量是由一�(duì)單引�(hào)括起�(lái)的單�(gè)字符,字符串常量是一�(duì)雙引�(hào)括起�(lái)的字符序�。C�(guī)定以“\”作字符串結(jié)束標(biāo)志,它是由系�(tǒng)自動(dòng)加上�,所以字符串“a”實(shí)際上包含兩�(gè)字符:‘a(chǎn)'和‘\',而把它賦給一�(gè)字符變量是不行的�

  4.忽略了�=”與�==”的區(qū)別�

  在許多語(yǔ)言�,用�=”符�(hào)作為�(guān)系運(yùn)算符“等于�。如在BASIC程序中可以寫(xiě)

  if (a=3) then �

  但C�(yǔ)言中,�=”是賦值運(yùn)算符,�==”是�(guān)系運(yùn)算符。如�

  if (a==3) a=b;

  前者是�(jìn)行比較,a是否�3相等,后者表示如果a�3相等,把b值賦給a。由于習(xí)慣問(wèn)題,初學(xué)者往往�(huì)犯這樣的錯(cuò)��

  5.忘記加分�(hào)�

  分號(hào)是C�(yǔ)句中不可缺少的一部分,語(yǔ)句末尾必須有分號(hào)�

  a=1

  b=2

  編譯�(shí),編譯程序在“a=1”后面沒(méi)�(fā)�(xiàn)分號(hào),就把下一行“b=2”也作為上一行語(yǔ)句的一部分,這就�(huì)出現(xiàn)�(yǔ)法錯(cuò)�。改�(cuò)�(shí),有�(shí)在被指出有錯(cuò)的一行中未發(fā)�(xiàn)�(cuò)�,就需要看一下上一行是否漏掉了分號(hào)�

  { z=x+y;

  t=z/100;

  printf("%f",t);

  }

  �(duì)于復(fù)合語(yǔ)句來(lái)�(shuō),一�(gè)�(yǔ)句中的分�(hào)不能忽略不寫(xiě)(這是和PASCAL不同�)�

  6.多加分號(hào)�

  �(duì)于一�(gè)�(fù)合語(yǔ)�,如�

  { z=x+y;

  t=z/100;

  printf("%f",t);

  };

  �(fù)合語(yǔ)句的花括�(hào)后不�(yīng)再加分號(hào),否則將�(huì)�(huà)蛇添��

  又如�

  if (a%3==0);

  I++;

  本是如果3整除a,則I�1。但由于if (a%3==0)后多加了分號(hào),則if�(yǔ)句到此結(jié)�,程�?qū)�?zhí)行I++�(yǔ)�,不�3是否整除a,I都將自動(dòng)�1�

  再如�

  for (I=0;I<5;I++);

  {scanf("%d",&x);

  printf("%d",x);}

  本意是先后輸�5�(gè)�(shù),每輸入一�(gè)�(shù)后再將它輸出。由于for()后多加了一�(gè)分號(hào),使循環(huán)體變?yōu)榭照Z(yǔ)�,此�(shí)只能輸入一�(gè)�(shù)并輸出它�

  7.輸入變量�(shí)忘記加地址�(yùn)算符�&��

  int a,b;

  scanf("%d%d",a,b);

  這是不合法的。Scanf函數(shù)的作用是:按照a、b在內(nèi)存的地址將a、b的值存�(jìn)�?�?amp;a”指a在內(nèi)存中的地址�

  8.輸入�(shù)�(jù)的方式與要求不符。①scanf("%d%d",&a,&b);

  輸入�(shí),不能用逗號(hào)作兩�(gè)�(shù)�(jù)間的分隔�,如下面輸入不合法:

  3�

  輸入�(shù)�(jù)�(shí),在兩�(gè)�(shù)�(jù)之間以一�(gè)或多�(gè)空格間隔,也可用回車�,跳格鍵tab�

  ②scanf("%d,%d",&a,&b);

  C�(guī)定:如果在“格式控制”字符串中除了格式說(shuō)明以外還有其它字�,則在輸入數(shù)�(jù)�(shí)�(yīng)輸入與這些字符相同的字符。下面輸入是合法的:

  3�

  此時(shí)不用逗號(hào)而用空格或其它字符是不對(duì)的�

  3 4 3�

  又如�

  scanf("a=%d,b=%d",&a,&b);

  輸入�(yīng)如以下形式:

  a=3,b=4

  9.輸入字符的格式與要求不一致�

  在用�%c”格式輸入字符時(shí),“空格字符”和“轉(zhuǎn)義字符”都作為有效字符輸入�

  scanf("%c%c%c",&c1,&c2,&c3);

  如輸入a b c

  字符“a”送給c1,字符� ”送給c2,字符“b”送給c3,因?yàn)?c只要求讀入一�(gè)字符,后面不需要用空格作為兩�(gè)字符的間隔�

  10.輸入輸出的數(shù)�(jù)類型與所用格式說(shuō)明符不一��

  例如,a已定義為整型,b定義為實(shí)�

  a=3;b=4.5;

  printf("%f%d\n",a,b);

  編譯�(shí)不給出出�(cuò)信息,但�(yùn)行結(jié)果將與原意不�。這種�(cuò)誤尤其需要注意�

  11.輸入�(shù)�(jù)�(shí),企圖規(guī)定精��

  scanf("%7.2f",&a);

  這樣做是不合法的,輸入數(shù)�(jù)�(shí)不能�(guī)定精��

  12.switch�(yǔ)句中漏寫(xiě)break�(yǔ)��

  例如:根�(jù)考試成績(jī)的等�(jí)打印出百分制�(shù)��

  switch(grade)

  { case 'A':printf("85~100\n");

  case 'B':printf("70~84\n");

  case 'C':printf("60~69\n");

  case 'D':printf("<60\n");

  default:printf("error\n");

  由于漏寫(xiě)了break�(yǔ)句,case只起�(biāo)�(hào)的作�,而不起判斷作�。因�,當(dāng)grade值為A�(shí),printf函數(shù)在執(zhí)行完�(gè)�(yǔ)句后接著�(zhí)行第�、三、四、五�(gè)printf函數(shù)�(yǔ)�。正確寫(xiě)法應(yīng)在每�(gè)分支后再加上“break;”。例�

  case 'A':printf("85~100\n");break;

  13.忽視了while和do-while�(yǔ)句在�(xì)節(jié)上的區(qū)��

  (1)main()

  {int a=0,I;

  scanf("%d",&I);

  while(I<=10)

  {a=a+I;

  I++;

  }

  printf("%d",a);

  }

  (2)main()

  {int a=0,I;

  scanf("%d",&I);

  do

  {a=a+I;

  I++;

  }while(I<=10);

  printf("%d",a);

  }

  可以看到,當(dāng)輸入I的值小于或等于10�(shí),二者得到的�(jié)果相同。而當(dāng)I>10�(shí),二者結(jié)果就不同�。因?yàn)閣hile循環(huán)是先判斷后執(zhí)�,而do-while循環(huán)是先�(zhí)行后判斷。對(duì)于大�10的數(shù)while循環(huán)一次也不執(zhí)行循�(huán)�,而do-while�(yǔ)句則要執(zhí)行一次循�(huán)��

  14.定義�(shù)組時(shí)誤用變量�

  int n;

  scanf("%d",&n);

  int a[n];

  �(shù)組名后用方括�(hào)括起�(lái)的是常量表達(dá)式,可以包括常量和符�(hào)常量。即C不允許對(duì)�(shù)組的大小作動(dòng)�(tài)定義�

  15.在定義數(shù)組時(shí),將定義的“元素�(gè)�(shù)”誤�(rèn)為是可使的下�(biāo)��

  main()

  {STatic int a[10]={1,2,3,4,5,6,7,8,9,10};

  printf("%d",a[10]);

  }

  C�(yǔ)言�(guī)定:定義�(shí)用a[10],表示a�(shù)組有10�(gè)元素。其下標(biāo)值由0�(kāi)�,所以數(shù)組元素a[10]是不存在��

  16.初始化數(shù)組時(shí),未使用靜態(tài)存儲(chǔ)�

  int a[3]={0,1,2};

  這樣初始化數(shù)組是不對(duì)的。C�(yǔ)言�(guī)定只有靜�(tài)存儲(chǔ)(static)�(shù)組和外部存儲(chǔ)(exterm)�(shù)組才能初始化。應(yīng)改為�

  static int a[3]={0,1,2};

  17.在不�(yīng)加地址�(yùn)算符&的位置加了地址�(yùn)算符�

  scanf("%s",&str);

  C�(yǔ)言編譯系統(tǒng)�(duì)�(shù)組名的處理是:數(shù)組名代表該數(shù)組的起始地址,且scanf函數(shù)中的輸入�(xiàng)是字符數(shù)組名,不必要再加地址�&。應(yīng)改為�

  scanf("%s",str);

  18.同時(shí)定義了形參和函數(shù)中的局部變量�

  int max(x,y)

  int x,y,z;

  {z=x>y?x:y;

  return(z);

  }

  形參�(yīng)該在函數(shù)體外定義,而局部變量應(yīng)該在函數(shù)體內(nèi)定義。應(yīng)改為�

  int max(x,y)

  int x,y;

  {int z;

  z=x>y?x:y;

  return(z);

  }

高效編程的幾�

  編寫(xiě)高效�(jiǎn)潔的C�(yǔ)言代碼,是許多軟件工程師追求的目標(biāo)。本文就工作中的一些體�(huì)和經(jīng)�(yàn)做相�(guān)的闡�,不�(duì)的地方請(qǐng)各位指教�

  �1招:以空間換�(shí)�

  �(jì)算機(jī)程序中的矛盾是空間和�(shí)間的矛盾,那�,從這�(gè)角度出發(fā)逆向思維�(lái)考慮程序的效率問(wèn)�,我們就有了解決�(wèn)題的�1招——以空間換時(shí)間�

  例如:字符串的賦��

  方法A,通常的辦法:

  #define LEN 32

  char string1 [LEN];

  memset (string1,0,LEN);

  strcpy (string1,“This is a example!!”);

  方法B�

  const char string2[LEN] =“This is a example!�;

  char * cp;

  cp = string2 ;

  (使用的時(shí)候可以直接用指針�(lái)操作�)

  從上面的例子可以看出,A和B的效率是不能比的。在同樣的存�(chǔ)空間下,B直接使用指針就可以操作了,而A需要調(diào)用兩�(gè)字符函數(shù)才能完成。B的缺�(diǎn)在于靈活性沒(méi)有A�。在需要頻繁更改一�(gè)字符串內(nèi)容的�(shí)候,A具有更好的靈活�;如果采用方法B,則需要預(yù)存許多字符串,雖然占用了大量的內(nèi)�,但是獲得了程序�(zhí)行的高效率�

  如果系統(tǒng)的實(shí)�(shí)性要求很�,內(nèi)存還有一些,那我推薦你使用該招數(shù)�

  該招�(shù)的變招——使用宏函數(shù)而不是函�(shù)。舉例如下:

  方法C�

  #define bwMCDR2_ADDRESS 4

  #define bsMCDR2_ADDRESS 17

  int BIT_MASK(int __bf)

  {

  return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);

  }

  void SET_BITS(int __dst, int __bf, int __val)

  {

  __dst = ((__dst) & ~(BIT_MASK(__bf))) \

  (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

  }

  SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

  方法D�

  #define bwMCDR2_ADDRESS 4

  #define bsMCDR2_ADDRESS 17

  #define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)

  #define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))

  #define SET_BITS(__dst, __bf, __val) \

  ((__dst) = ((__dst) & ~(BIT_MASK(__bf))) \

  (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

  SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

  函數(shù)和宏函數(shù)的區(qū)別就在于,宏函數(shù)占用了大量的空間,而函�(shù)占用了時(shí)�。大家要知道的是,函�(shù)�(diào)用是要使用系�(tǒng)的棧�(lái)保存�(shù)�(jù)的,如果編譯器里有棧檢查選項(xiàng),一般在函數(shù)的頭�(huì)嵌入一些匯編語(yǔ)句對(duì)�(dāng)前棧�(jìn)行檢�;同�(shí),CPU也要在函�(shù)�(diào)用時(shí)保存和恢�(fù)�(dāng)前的�(xiàn)�(chǎng),�(jìn)行壓棧和彈棧操作,所以,函數(shù)�(diào)用需要一些CPU�(shí)�。而宏函數(shù)不存在這�(gè)�(wèn)題。宏函數(shù)僅僅作為�(yù)先寫(xiě)好的代碼嵌入到當(dāng)前程�,不�(huì)�(chǎn)生函�(shù)�(diào)用,所以僅僅是占用了空�,在頻繁�(diào)用同一�(gè)宏函�(shù)的時(shí)候,該現(xiàn)象尤其突��   D方法是我看到的的置位操作函數(shù),是ARM公司源碼的一部分,在短短的三行內(nèi)�(shí)�(xiàn)了很多功�,幾乎涵蓋了所有的位操作功�。C方法是其變體,其中滋味還需大家仔細(xì)體會(huì)�

  �2招:�(shù)�(xué)方法解決�(wèn)�

  �(xiàn)在我們演繹高效C�(yǔ)言編寫(xiě)的第二招——采用數(shù)�(xué)方法�(lái)解決�(wèn)��

  �(shù)�(xué)是計(jì)算機(jī)之母,沒(méi)有數(shù)�(xué)的依�(jù)和基�(chǔ),就�(méi)有計(jì)算機(jī)的發(fā)展,所以在編寫(xiě)程序的時(shí)�,采用一些數(shù)�(xué)方法�(huì)�(duì)程序的執(zhí)行效率有�(shù)量級(jí)的提高�

  舉例如下,求 1~100的和�

  方法E

  int I , j;

  for (I = 1 ;I<=100; I ++){

  j += I;

  }

  方法F

  int I;

  I = (100 * (1+100)) / 2

  這�(gè)例子是我印象最深的一�(gè)�(shù)�(xué)用例,是我的�(jì)算機(jī)啟蒙老師考我�。當(dāng)�(shí)我只有小�(xué)三年�(jí),可惜我�(dāng)�(shí)不知道用公式 N×(N+1�/ 2 �(lái)解決這�(gè)�(wèn)題。方法E循環(huán)�100次才解決�(wèn)�,也就是�(shuō)最少用�100�(gè)賦��100�(gè)判斷�200�(gè)加法(I和j�;而方法F僅僅用了1�(gè)加法�1次乘��1次除�。效果自然不言而喻。所�,現(xiàn)在我在編程序的時(shí)�,更多的是動(dòng)腦筋找規(guī)�,限度地�(fā)揮數(shù)�(xué)的威力來(lái)提高程序�(yùn)行的效率� �3招:使用位操�

  �(shí)�(xiàn)高效的C�(yǔ)言編寫(xiě)的第三招——使用位操作,減少除法和取模的運(yùn)算�

  在計(jì)算機(jī)程序�,數(shù)�(jù)的位是可以操作的最小數(shù)�(jù)單位,理論上可以用“位�(yùn)算”來(lái)完成所有的�(yùn)算和操作。一般的位操作是用來(lái)控制硬件的,或者做�(shù)�(jù)變換使用,但是,靈活的位操作可以有效地提高程序運(yùn)行的效率。舉例如下:

  方法G

  int I,J;

  I = 257 /8;

  J = 456 % 32;

  方法H

  int I,J;

  I = 257 >>3;

  J = 456 - (456 >> 4 << 4);

  在字面上好像H比G麻煩了好�,但�,仔�(xì)查看�(chǎn)生的匯編代碼就會(huì)明白,方法G�(diào)用了基本的取模函�(shù)和除法函�(shù),既有函�(shù)�(diào)用,還有很多匯編代碼和寄存器參與�(yùn)�;而方法H則僅僅是幾句相關(guān)的匯編,代碼更簡(jiǎn)�,效率更高。當(dāng)�,由于編譯器的不�,可能效率的差距不大,但�,以我目前遇到的MS C ,ARM C �(lái)�,效率的差距還是不小。相�(guān)匯編代碼就不在這里列舉��

  �(yùn)用這招需要注意的是,�?yàn)镃PU的不同而產(chǎn)生的�(wèn)�。比如說(shuō),在PC上用這招編寫(xiě)的程序,并在PC上調(diào)試通過(guò),在移植到一�(gè)16位機(jī)平臺(tái)上的�(shí)�,可能會(huì)�(chǎn)生代碼隱�。所以只有在一定技�(shù)�(jìn)階的基礎(chǔ)下才可以使用這招�

  �4招:匯編嵌入

  高效C�(yǔ)言編程的必殺技,第四招——嵌�?yún)R��   “在熟悉匯編�(yǔ)言的人眼里,C�(yǔ)言編寫(xiě)的程序都是垃圾”。這種�(shuō)法雖然偏激了一�,但是卻有它的道�。匯編語(yǔ)言是效率的�(jì)算機(jī)�(yǔ)言,但�,不可能靠著它來(lái)�(xiě)一�(gè)操作系統(tǒng)�?所�,為了獲得程序的高效率,我們只好采用變通的方法 ——嵌�?yún)R�,混合編程�

  舉例如下,將�(shù)組一賦值給�(shù)組二,要求每一字節(jié)都相��

  char string1[1024],string2[1024];

  方法I

  int I;

  for (I =0 ;I<1024;I++)

  *(string2 + I) = *(string1 + I)

  方法J

  #ifdef _PC_

  int I;

  for (I =0 ;I<1024;I++)

  *(string2 + I) = *(string1 + I);

  #else

  #ifdef _ARM_

  __asm

  {

  MOV R0,string1

  MOV R1,string2

  MOV R2,#0

  loop:

  LDMIA R0!, [R3-R11]

  STMIA R1!, [R3-R11]

  ADD R2,R2,#8

  CMP R2, #400

  BNE loop

  }

  #endif

  方法I是最常見(jiàn)的方�,使用了1024次循�(huán);方法J則根�(jù)平臺(tái)不同做了區(qū)�,在ARM平臺(tái)下,用嵌�?yún)R編僅�128次循�(huán)就完成了同樣的操�。這里有朋友會(huì)�(shuō),為什么不用標(biāo)�(zhǔn)的內(nèi)存拷貝函�(shù)�?這是�?yàn)樵谠�?shù)�(jù)里可能含有數(shù)�(jù)�0的字節(jié),這樣的話,標(biāo)�(zhǔn)�(kù)函數(shù)�(huì)提前�(jié)束而不�(huì)完成我們要求的操作。這�(gè)例程典型�(yīng)用于LCD�(shù)�(jù)的拷貝過(guò)�。根�(jù)不同的CPU,熟練使用相�(yīng)的嵌�?yún)R�,可以大大提高程序執(zhí)行的效率�   雖然是必殺技,但是如果輕易使用會(huì)付出慘重的代�(jià)。這是�?yàn)?,使用了嵌入?yún)R編,便限制了程序的可移植�,使程序在不同平�(tái)移植的過(guò)程中,臥虎藏龍,�(xiǎn)象環(huán)�!同�(shí)該招�(shù)也與�(xiàn)代軟件工程的思想相違背,只有在迫不得已的情況下才可以采用。切記,切記�

  使用C�(yǔ)言�(jìn)行高效率編程,我的體�(huì)僅此而已。在此以本文拋磚引玉,還�(qǐng)各位高手共同切磋。希望各位能給出更好的方法,大家一起提高我們的編程技��

聲明�(xué)�(xí)之初�(jí)�

  C�(yǔ)言為我們定義了四種基本�(shù)�(jù)類型:整型,浮點(diǎn)�,指針以及聚合類型(�(shù)組和�(jié)�(gòu)體等�,在此基�(chǔ)上,我們就可以聲明變量。我們平�(shí)�(jīng)常說(shuō)定義一�(gè)某種類型的變�,其�(shí)這樣�(shuō)不確�,應(yīng)該說(shuō)是聲明變��

  變量聲明的基本形式是�

  �(shuō)明符(一�(gè)或多�(gè)� 聲明表達(dá)式列�

  比如�(shuō):int a, b, c, d;

  C�(yǔ)言中對(duì)指針的聲明比較有代表�,我們來(lái)看一下:

  比如聲明一�(gè)指向int型的指針a:int *a;

  這�(gè)�(yǔ)句表示表�(dá)�*a�(chǎn)生的�(jié)果類型是int,而我們又知道*操作符執(zhí)行的是間接訪�(wèn)操作,所以可以推斷a肯定是一�(gè)指向int的指��

  C�(yǔ)言在本�(zhì)上是一種自由形式的�(yǔ)言,它給了程序員很大的空間,我們同樣可以這樣�(xiě):int* a,這�(gè)聲明與int *a�(shí)一�(gè)意思,而且似乎更為清楚,a被聲明為類型為int*的指針(�(shí)則不然),這會(huì)誘導(dǎo)我們這樣聲明三�(gè)指向int型的指針�

  int* a, b, c;

  也許你會(huì)很自然的以為這條�(yǔ)句把三�(gè)變量a、b、c都聲明為指向整型的指�,但是事�(shí)上我們被它的形式愚弄�,星�(hào)�(shí)際上是表�(dá)�*a的一部分,只�(duì)這�(gè)�(biāo)�(shí)符有�,a是一�(gè)指針,但是b和c都只是普通的整型而已,要聲明三指�,這樣�(xiě)是可以的�

  int *a, *b, *c;

  從這�(gè)�(jiǎn)單的例子我們可以看出C�(yǔ)言的聲明規(guī)則多么具有迷惑�,呵呵,這也是C�(yǔ)言飽受�*的地方之一,但這決定與�(yǔ)言本身的設(shè)�(jì)哲學(xué),我們無(wú)法改�,要想用好C�(yǔ)言,我們必須掌握它的語(yǔ)法規(guī)��

  我�?cè)倏匆粋€(gè)例子�

  int fun();

  我們都知道它把f聲明為一�(gè)函數(shù),它的返回值是一�(gè)整數(shù)�

  如果這樣�(xiě)�

  int *fun();

  要想推斷出它的含義,我們必須知�*fun()是如何求值的。首先執(zhí)行的是函�(shù)�(diào)用操作符(),因?yàn)樗�?yōu)先級(jí)高于間接訪問(wèn)操作�*,所以fun是一�(gè)函數(shù),它的返回值類型是一�(gè)指向整型的指��

  再看一�(gè)更為有趣的聲明:

  int (*fun)();

  這�(gè)聲明有兩�(duì)括號(hào),每�(duì)括號(hào)的含義不同。第二對(duì)括號(hào)是函�(shù)�(diào)用操作符,但是對(duì)只起到聚組的作用。它�(dǎo)致間接訪�(wèn)在函�(shù)�(diào)用之前�(jìn)�,使fun是一�(gè)函數(shù)指針,它所指向的函�(shù)返回一�(gè)整型值�

  那么�(xiàn)在這�(gè)聲明�(yīng)該很容易分析出來(lái)�

  int *(*fun)();

  fun還是一�(gè)函數(shù)指針,只是所指向的函�(shù)返回的是一�(gè)整型指針�

  先寫(xiě)到這里,對(duì)C�(yǔ)言的聲明之旅才剛剛�(kāi)�,下回我們將在中�(jí)篇里討論更有趣的話題�

聲明�(xué)�(xí)之中�(jí)�

  C�(yǔ)言的聲明存在的的問(wèn)題就是你�(wú)法以一種人們所�(xí)慣的自然方式從左到右閱讀一�(gè)聲明,程序員必須記住特殊的規(guī)則才能推斷出int *p[3]到底是一�(gè)int類型的指針數(shù)組還是一�(gè)指向int�(shù)組的指針�

  �(duì)于這樣一�(gè)聲明,我們應(yīng)該如何分��

  ——————int f()[]�

  首先,f是一�(gè)函數(shù),其�,它的返回值是一�(gè)整型�(shù)�。貌似就是這樣�,但�(shí)際上,這�(gè)例子隱藏著一�(gè)陷阱,因?yàn)檫@�(gè)聲明是非法的,呵�,在我們的C�(yǔ)言里,函數(shù)只能返回變量�,不能返�?cái)?shù)組�

  還有一�(gè)讓人頗費(fèi)腦筋的聲明:

  ——————int f[] ()�

  這里,f�(yīng)該是一�(gè)�(shù)�,數(shù)組的元素類型是返回值為整型的函�(shù)。請(qǐng)不要�(duì)它看似正確的表面所迷惑,其�(shí)這�(gè)聲明也是非法�!因?yàn)�?shù)組元素必須具有相同的�(zhǎng)�,但是不同的函數(shù)顯然可能具有不同的長(zhǎng)度吧,呵呵�

  在被C�(yǔ)言迷幻的聲明形式欺騙兩次之�,現(xiàn)在是不是有些草木皆兵�?讓我們乘熱打�,再看一�(gè)聲明�

  ——————int (*f[]) ()�

  �(qǐng)你分析一下它的含義?首先,你能否確定它是�(duì)的還是錯(cuò)的?

  首先,我們必須找到所有的操作�,然后按照正確的次序�(zhí)行它�。這里有兩�(duì)括號(hào),它們分別具有不同的含義。�(gè)括號(hào)�(nèi)的表�(dá)�*f[]首先�(jìn)行求�,所以f是一�(gè)元素為某種類型的指針的數(shù)�;末尾的括號(hào)是函�(shù)�(diào)用操作符,所以我們可以肯定f是一�(gè)�(shù)�,數(shù)組元素的類型是函�(shù)指針,它所指向的函�(shù)的返回值是一�(gè)整型值�

  清楚了上面這�(gè)聲明,下面這�(gè)聲明�(yīng)該就比較容易分析了:

  ——————int *(*f[ ]) ( )�

  這�(gè)聲明�(chuàng)建了一�(gè)指針�(shù)�,指�?biāo)赶虻念愋褪欠祷刂禐檎椭羔樀暮瘮?shù)�

  ANSI C推薦我們使用完整的函數(shù)原型,使聲明更為明確,例如:

  int (*f) ( int, float )�

  int *(*g[]) ( int, float )�

  前者把f聲明為一�(gè)函數(shù)指針,它所指向的函�(shù)接受兩�(gè)參數(shù),分別是一�(gè)整型�(shù)和浮�(diǎn)型�,并返回一�(gè)整數(shù)�

  后者把g聲明為一�(gè)�(shù)�,數(shù)組的元素類型是一�(gè)函數(shù)指針,它所指向的函�(shù)接受兩�(gè)參數(shù),分別是一�(gè)整型�(shù)和浮�(diǎn)型�,并返回一�(gè)整型指針。盡管原型增加了聲明的復(fù)雜度,但是ANSI C還是大力提倡這�(gè)�(fēng)�,因?yàn)檫@樣可以向編譯器提供一些額外的信息�

  在中�(jí)篇的,給大家推薦一�(gè)�(shí)用的C�(yǔ)言工具:cdecl,這�(gè)程序可用于所有UNIX操作系統(tǒng),它可以將C�(yǔ)言的聲明翻譯成通俗易懂的語(yǔ)言,并可以將C�(yǔ)言聲明的語(yǔ)法轉(zhuǎn)換成為具體的C�(yǔ)言聲明�

  如果你是用的是ubuntu操作系統(tǒng),那么你只需要執(zhí)行sudo apt-get install cdecl就可以把cdecl工具安裝到你的計(jì)算機(jī)上,�(duì)于別的unix操作系統(tǒng),你同樣可以下載源碼包安裝(comp.sources.unix.newsgroup��

  在shell終端,我們執(zhí)行cdecl就可以�(jìn)入cdecl>提示�,然后輸入:explain int (*(*f)())[10]; 可以得到�

  可以看到,cdecl為我們解釋了int (*(*f)()) [10]這�(gè)聲明的含義,有了這�(gè)工具,不管我們遇到怎樣詭異的C�(yǔ)言聲明,都可以從容�(yīng)�(duì)了吧,當(dāng)�,我們可以給cdecl一�(gè)聲明的語(yǔ)�,把上面一段解釋輸入�(jìn)�,就可以看到�

  可見(jiàn),cdecl又幫我們把這段通俗的解釋轉(zhuǎn)換成為的C�(yǔ)言的聲��

  怎么�,這�(gè)工具是不是很好用,如果你的系�(tǒng)里面還沒(méi)有這�(gè)工具的話,你是不是應(yīng)該趕快安裝一�(gè)�?讓它成為你�(xué)�(xí)C�(yǔ)言的好幫手��

聲明�(xué)�(xí)之篇

  C�(yǔ)言的設(shè)�(jì)哲學(xué)要求�(duì)象的聲明形式和它的使用形式盡可能相似,比如一�(gè)int類型的指針數(shù)組被聲明為int *p[3];并�*p[i]這樣的表�(dá)式引用或者使用指�?biāo)赶虻膇nt�(shù)�(jù),所以它的聲明形式和使用形式非常相似。這樣做的好處是各種不同操作符的優(yōu)先級(jí)在“聲明”和“使用”時(shí)是一樣的,而缺�(diǎn)恰好在與C�(yǔ)言的操作符的優(yōu)先級(jí)�(guò)于復(fù)雜(�15�(jí)或者更多,取決于你怎么算),這是C�(yǔ)言�(shè)�(jì)不當(dāng)、過(guò)于復(fù)雜之��

  �(shí)際上有些�(guān)鍵字只能出現(xiàn)在聲明中,而不是使用中,比如volatile和const�,這使得聲明形式和使用形式能完全對(duì)的上�(hào)的例子越�(lái)越少�。如果想要把什么東西強(qiáng)制轉(zhuǎn)換為指向�(shù)組的指針,就不得不使用下面的�(yǔ)句來(lái)表示這�(gè)�(qiáng)制類型轉(zhuǎn)換:

  ———char (*j) [ 20 ];

  ———j = ( char ( * )[20] ) malloc(20);

  這�(gè)�(qiáng)制類型轉(zhuǎn)換看上去很滑�,星�(hào)兩邊的括�(hào)看上去可有可�(wú),但是如果去掉就�(huì)變成非法�(yǔ)句�

  涉及指針和const得聲明可能會(huì)有下面幾種不同的組合�

  ———const int * p;

  ———int const * p;

  ———int * const p;

  前兩種情�,指�?biāo)赶虻膶?duì)象是只讀�,而一種情況下指針是只讀��

  如果我們想讓對(duì)象和指針都是只讀的,那么下面兩種聲明都能做到這一�(diǎn)�

  ———const int * const p;

  ———int const * const p;

  �(jīng)�(guò)初級(jí)�、中�(jí)篇一直到前面的學(xué)�(xí)我們發(fā)�(xiàn)其實(shí)分析一�(gè)聲明就是按照操作符優(yōu)先級(jí)�(guī)則把聲明分解�(kāi)�(lái),分別解釋各�(gè)組成部分。要理解一�(gè)聲明,必須要懂得其中的優(yōu)先級(jí)�(guī)�,下面是《C專家編程》中總結(jié)的C�(yǔ)言聲明的優(yōu)先級(jí)�(guī)則:

  A聲明從它的名字開(kāi)始讀取,然后按照�(yōu)先級(jí)順序依次讀??�

  B �(yōu)先級(jí)從高到低依次是:

  B.1 聲明中被括號(hào)括起�(lái)的那部分�

  B.2 后綴操作符:括號(hào)()表示這是一�(gè)函數(shù),而方括號(hào)[]表示這是一�(gè)�(shù)��

  B.3 前綴操作符:星號(hào)*�(biāo)�(shí)“指向……的指針��

  C 如果const和(或者)volatile�(guān)鍵字的后面緊跟類型說(shuō)明符(如int,long等),那么它作用于類型說(shuō)明符,在其他情況下,const和(或)volatile�(guān)鍵字作用于它左邊緊鄰的指針星�(hào)�

  �(xiàn)�,讓我們用�(yōu)先級(jí)�(guī)則來(lái)分析C�(yǔ)言的一�(gè)較復(fù)雜的聲明�

  ———char * const *(*next) ();

  B.1  �*next)           ——next為一�(gè)指向……的指針

  B.2  �*next)()         ——next是一�(gè)函數(shù)指針

  B.3  *�*next)()         ——next是一�(gè)函數(shù)指針,這�(gè)函數(shù)返回一�(gè)指向……的指針

  C    char * const                     ——指向字符類型的常量指針

  � char * const *�*next)(�;的含義就是:next是一�(gè)函數(shù)指針,這�(gè)函數(shù)返回一�(gè)指向字符類型的常量指��

在單片機(jī)�(kāi)�(fā)中的幾�(gè)�(wèn)�

  �(kāi)�(fā)�(yīng)用中,已逐漸�(kāi)始引入語(yǔ)言,C�(yǔ)言就是其中的一�。對(duì)用慣了匯編的人來(lái)�(shuō),總�(jué)得語(yǔ)言’可控性’不好,不如匯編那樣隨心所�。但是只要我們掌握了一定的C�(yǔ)言知識(shí),有些東西還是容易做出來(lái)的,以下是筆者實(shí)際工作中遇到的幾�(gè)�(wèn)�,希望對(duì)初學(xué)C51者有所幫助�

  一、C51熱啟�(dòng)代碼的編�

  �(duì)于工�(yè)控制�(jì)算機(jī),往往�(shè)有有看門狗電�,當(dāng)看門狗動(dòng)�,使�(jì)算機(jī)�(fù)�,這就是熱啟動(dòng)。熱啟動(dòng)�(shí),一般不允許從頭�(kāi)�,這將�(dǎo)致現(xiàn)有的已測(cè)量到或計(jì)算到的值復(fù)位,�(dǎo)致系�(tǒng)工作異常。因而在程序必須判斷是熱啟動(dòng)還是冷啟�(dòng),常用的方法是:確定某內(nèi)存單位為�(biāo)志位(�0x7f位和0x7e�),啟�(dòng)�(shí)首先讀該內(nèi)存單元的�(nèi)容,如果它等于一�(gè)特定的�(例如兩�(gè)�(nèi)存單元的都是0xaa),就�(rèn)為是熱啟�(dòng),否則就是冷啟動(dòng),程序執(zhí)行初始化部份,并�0xaa賦與這兩�(gè)�(nèi)存單��

  根據(jù)以上的設(shè)�(jì)思路,編程時(shí),設(shè)置一�(gè)指針,讓其指向特定的�(nèi)存單元如0x7f,然后在程序中判�,程序如下:

  void main()

  { char data *HotPoint=(char *)0x7f;

  if((*HotPoint==0xaa)&&(*(--HotPoint)==0xaa))

  { /*熱啟�(dòng)的處� */

  }

  else

  { HotPoint=0x7e; /*冷啟�(dòng)的處�(jìn)

  *HotPoint=0xaa;

  *(++HotPoint)=0xaa;

  }

  /*正常工作代碼*/

  }

  然而實(shí)際調(diào)試中�(fā)�(xiàn),無(wú)論是熱啟�(dòng)還是冷啟�(dòng),開(kāi)�(jī)后所有內(nèi)存單元的值都被復(fù)位為0,當(dāng)然也�(shí)�(xiàn)不了熱啟�(dòng)的要求。這是為什么呢?原來(lái),用C�(yǔ)言編程�(shí),開(kāi)�(jī)�(shí)�(zhí)行的代碼并非是從main()函數(shù)的句�(yǔ)句開(kāi)始的,在main()函數(shù)的句�(yǔ)句執(zhí)行前要先�(zhí)行一段’起始代碼�。正是這段代碼�(zhí)行了清零的工�。C編譯程序提供了這段起始代碼的源程序,名為CSTARTUP.A51,打開(kāi)這�(gè)文件,可以看到如下代碼:

  .

  IDATALEN EQU 80H ; the length of IDATA memory in bytes.

  .

  STARTUP1:

  IF IDATALEN <> 0

  MOV R0,#IDATALEN - 1

  CLR A

  IDATALOOP: MOV @R0,A

  DJNZ R0,IDATALOOP

  ENDIF

  .

  可見(jiàn),在�(zhí)行到判斷是否熱啟�(dòng)的代碼之前,起始代碼已將所有內(nèi)存單元清�。如何解決這�(gè)�(wèn)題呢?好在啟動(dòng)代碼是可以更改的,方法是:修� startup.a51源文�,然后用編譯程序所附帶的a51.exe程序?qū)?startup.a51編譯,得到startup.obj文件,然后用這段代碼代替原來(lái)的起始代�。具體步驟是(設(shè)C源程序名為HOTSTART.C):

  修改startup.a51源文�(這�(gè)文件在C51\LIB目錄�)�

  �(zhí)行如下命令:

  A51 startup.a51 得到startup.obj文件。將此文件拷入HOTSTART.C所在目錄�

  將編好的C源程序用C51.EXE編譯�,得到目�(biāo)文件HOTSTART.OBJ�

  � L51 HOTSTART, STARTUP.OBJ 命令連接,得到目�(biāo)文件HOTSTART�

  � OHS51 HOTSTART 得到HOTSTART.HEX文件,即��

  �(duì)于startup.a51的修�,根�(jù)自已的需要�(jìn)�,如將IDATALEN EQU 80H中的80H改為70H,就可以�6F�7F�16字節(jié)�(nèi)存不被清��

  二、直接調(diào)用EPROM中已固化的程�

  筆者用的仿真機(jī),由6位數(shù)碼管顯示,在�(nèi)存DE00H處放顯示子程�,只要將要顯示的�(shù)放入顯示緩沖區(qū),然后調(diào)用這�(gè)子程序就可以使用�,匯編指令為:

  LCALL 0DEOOH

  在用C�(yǔ)言編程�(shí),如何實(shí)�(xiàn)這一功能�?C�(yǔ)言中有指向函數(shù)的指針這一概念,可以利用這種指針�(lái)�(shí)�(xiàn)用函�(shù)指針�(diào)用函�(shù)。指向函�(shù)的指針變量的定義格式為:

  類型�(biāo)�(shí)� (*指針變量�)();

  在定義好指針后就可以給指針變量賦值,使其指向某�(gè)函數(shù)的開(kāi)始存地址,然后用

  (*指針變量�)()即可�(diào)用這�(gè)函數(shù)。如下例�

  void main(void)

  {

  void (*DispBuffer)(); /*定義指向函數(shù)指針*/

  DispBuffer=0xde00; /*賦�*/

  for(;;)

  { Key();

  DispBuffer();

  }

  }

  �、將浮點(diǎn)�(shù)�(zhuǎn)化為字符�(shù)�

  筆者在編制�(yīng)用程序時(shí)有這樣的要求:將運(yùn)算的�(jié)果(浮點(diǎn)�(shù))存入EEPROM中。我們知�,浮�(diǎn)�(shù)在C�(yǔ)言中是以IEEE格式存儲(chǔ)的,一�(gè)浮點(diǎn)�(shù)占用四�(gè)字節(jié),例如浮�(diǎn)�(shù)34.526存為�160�26�10�66)這四�(gè)�(shù)。要將一�(gè)浮點(diǎn)�(shù)存入EEPROM,實(shí)際上就是要存這四�(gè)�(shù)。那么如何在程序中得到一�(gè)浮點(diǎn)�(shù)的組成數(shù)��

  浮點(diǎn)�(shù)在存�(chǔ)�(shí),是存儲(chǔ)連續(xù)的字節(jié)中的,只要設(shè)法找到存�(chǔ)位置,就可以得到這些�(shù)�??梢远x一�(gè)void的指�,將此指針指向需要存�(chǔ)的浮�(diǎn)�(shù),然后將此指針強(qiáng)制轉(zhuǎn)化為char�,這樣,利用指針就可以得到組成該浮�(diǎn)�(shù)的各�(gè)字節(jié)的值了。具體程序如下:

  #define uchar unsigned char#define uint unsigned intvoid FtoC(void)

  { float a;

  uchar i,*px

  uchar x[4]; /*定義字符�(shù)組,�(zhǔn)備存�(chǔ)浮點(diǎn)�(shù)的四�(gè)字節(jié)*�

  void *pf;

  px=x; /*px指針指向�(shù)組x*/

  pf=&a; /*void 型指針指向浮�(diǎn)�(shù)首地址*/

  a=34.526;

  for(i=0;i<4;i++)

  { *(px+i)=*((char *)pf+i); /*�(qiáng)制void 型指針轉(zhuǎn)成char�,�?yàn)?/

  } /*void型指針不能運(yùn)�*/

  }

  如果已將�(shù)存入EEPROM,要將其取出合并,方法也是一�,可參考下面的程序�

  #define uchar unsigned char#define uint unsigned int

  void CtoF(void)

  { float a;

  uchar i,*px

  uchar x[4]={56,180,150,73};

  void *pf;

  px=x;

  pf=&a;

  for(i=0;i<4;i++)

  { *((char *)pf+i)=*(px+i);

  }

  }

  以上所用C�(yǔ)言為FRANKLIN C51 VER 3.2�

編譯�(guò)程總�(jié)詳解

  鏈接�(guò)程要把我們編�(xiě)的一�(gè)c程序(源代碼)轉(zhuǎn)換成可以在硬件上�(yùn)行的程序(可�(zhí)行代碼),需要�(jìn)行編譯和鏈接。編譯就是把文本形式源代碼翻譯為�(jī)器語(yǔ)言形式的目�(biāo)文件的過(guò)�。鏈接是把目�(biāo)文件、操作系�(tǒng)的啟�(dòng)代碼和用到的�(kù)文件�(jìn)行組織形成最終生成可�(zhí)行代碼的�(guò)�。過(guò)程圖解如下:

C語(yǔ)言編譯過(guò)程圖解

  從圖上可以看�,整�(gè)代碼的編譯過(guò)程分為編譯和鏈接兩�(gè)�(guò)�,編譯對(duì)�(yīng)圖中的大括號(hào)括起的部分,其余則為鏈接�(guò)��

  編譯�(guò)�

  編譯�(guò)程又可以分成兩�(gè)階段:編譯和�(huì)匯編�

  編譯

  編譯是讀取源程序(字符流�,對(duì)之�(jìn)行詞法和�(yǔ)法的分析,將�(yǔ)言指令�(zhuǎn)換為功能等效的匯編代�,源文件的編譯過(guò)程包含兩�(gè)主要階段�

  �(gè)階段是預(yù)處理階段,在正式的編譯階段之前�(jìn)�。預(yù)處理階段將根�(jù)已放置在文件中的�(yù)處理指令�(lái)修改源文件的�(nèi)容。如#include指令就是一�(gè)�(yù)處理指令,它把頭文件的內(nèi)容添加到.cpp文件�。這�(gè)在編譯之前修改源文件的方式提供了很大的靈活�,以適應(yīng)不同的計(jì)算機(jī)和操作系�(tǒng)�(huán)境的限制。一�(gè)�(huán)境需要的代碼跟另一�(gè)�(huán)境所需的代碼可能有所不同,因?yàn)榭捎玫挠布虿僮飨到y(tǒng)是不同的。在許多情況下,可以把用于不同環(huán)境的代碼放在同一�(gè)文件�,再在預(yù)處理階段修改代碼,使之適�(yīng)�(dāng)前的�(huán)��

  主要是以下幾方面的處理:

  �1)宏定義指令,如 #define a  b

  �(duì)于這種偽指�,預(yù)編譯所要做的是將程序中的所有a用b替換,但作為字符串常量的 a則不被替�。還� #undef,則將取消對(duì)某�(gè)宏的定義,使以后該串的出�(xiàn)不再被替��

 ?�?)條件編譯指�,如#ifdef�#ifndef�#else�#elif�#endif��

  這些偽指令的引入使得程序員可以通過(guò)定義不同的宏�(lái)決定編譯程序?qū)δ男┐a�(jìn)行處�。預(yù)編譯程序?qū)⒏鶕?jù)有關(guān)的文�,將那些不必要的代碼�(guò)濾掉�

 ?�?� 頭文件包含指�,如#include "FileName"或�#include <FileName>等�

  在頭文件中一般用偽指�#define定義了大量的宏(最常見(jiàn)的是字符常量�,同�(shí)包含有各種外部符�(hào)的聲�。采用頭文件的目的主要是為了使某些定義可以供多�(gè)不同的C源程序使用。因?yàn)樵谛枰玫竭@些定義的C源程序中,只需加上一�#include�(yǔ)句即�,而不必再在此文件中將這些定義重復(fù)一�。預(yù)編譯程序?qū)杨^文件中的定義�(tǒng)�(tǒng)都加入到它所�(chǎn)生的輸出文件中,以供編譯程序?qū)χM(jìn)行處�。包含到c源程序中的頭文件可以是系�(tǒng)提供的,這些頭文件一般被放在 /usr/include目錄�。在程序�#include它們要使用尖括�(hào)�< >�。另外開(kāi)�(fā)人員也可以定義自己的頭文�,這些文件一般與c源程序放在同一目錄下,此時(shí)�#include中要用雙引號(hào)�""��

 ?�?)特殊符�(hào),預(yù)編譯程序可以�(shí)別一些特殊的符號(hào)�

  例如在源程序中出�(xiàn)的LINE�(biāo)�(shí)將被解釋為當(dāng)前行�(hào)(十�(jìn)制數(shù)�,F(xiàn)ILE則被解釋為當(dāng)前被編譯的C源程序的名稱。預(yù)編譯程序?qū)τ谠谠闯绦蛑谐霈F(xiàn)的這些串將用合適的值�(jìn)行替��

  �(yù)編譯程序所完成的基本上是對(duì)源程序的“替代”工�。經(jīng)�(guò)此種替代,生成一�(gè)�(méi)有宏定義、沒(méi)有條件編譯指�、沒(méi)有特殊符�(hào)的輸出文�。這�(gè)文件的含義同�(méi)有經(jīng)�(guò)�(yù)處理的源文件是相同的,但�(nèi)容有所不同。下一�,此輸出文件將作為編譯程序的輸出而被翻譯成為�(jī)器指��

  第二�(gè)階段編譯、優(yōu)化階�,經(jīng)�(guò)�(yù)編譯得到的輸出文件中,只有常�;如�(shù)字、字符串、變量的定義,以及C�(yǔ)言的關(guān)鍵字,如main,if,else,for,while,{,}, +,-,*,\等等�

  編譯程序所要作得工作就是通過(guò)詞法分析和語(yǔ)法分�,在確認(rèn)所有的指令都符合語(yǔ)法規(guī)則之�,將其翻譯成等價(jià)的中間代碼表示或匯編代碼�

  �(yōu)化處理是編譯系統(tǒng)中一�(xiàng)比較艱深的技�(shù)。它涉及到的�(wèn)題不僅同編譯技�(shù)本身有關(guān),而且同機(jī)器的硬件�(huán)境也有很大的�(guān)�。優(yōu)化一部分是對(duì)中間代碼的優(yōu)�。這種�(yōu)化不依賴于具體的�(jì)算機(jī)。另一種優(yōu)化則主要針對(duì)目標(biāo)代碼的生成而�(jìn)行的�

  �(duì)于前一種優(yōu)化,主要的工作是刪除公共表達(dá)�、循�(huán)�(yōu)化(代碼外提、強(qiáng)度削�、變換循�(huán)控制條件、已知量的合并等�、復(fù)�(xiě)傳播,以及無(wú)用賦值的刪除,等��

  后一種類型的�(yōu)化同�(jī)器的硬件�(jié)�(gòu)密切相關(guān),最主要的是考慮是如何充分利用機(jī)器的各�(gè)硬件寄存器存放的有關(guān)變量的�,以減少�(duì)于內(nèi)存的訪問(wèn)次數(shù)。另�,如何根�(jù)�(jī)器硬件執(zhí)行指令的特點(diǎn)(如流水�、RISC、CISC、VLIW等)而對(duì)指令�(jìn)行一些調(diào)整使目標(biāo)代碼比較短,�(zhí)行的效率比較�,也是一�(gè)重要的研究課題�

  匯編

  匯編�(shí)際上指把匯編�(yǔ)言代碼翻譯成目�(biāo)�(jī)器指令的�(guò)�。對(duì)于被翻譯系統(tǒng)處理的每一�(gè)C�(yǔ)言源程�,都將最終經(jīng)�(guò)這一處理而得到相�(yīng)的目�(biāo)文件。目�(biāo)文件中所存放的也就是與源程序等效的目�(biāo)的機(jī)器語(yǔ)言代碼。目�(biāo)文件由段組成。通常一�(gè)目標(biāo)文件中至少有兩�(gè)段:

  代碼段:該段中所包含的主要是程序的指令。該段一般是可讀和可�(zhí)行的,但一般卻不可�(xiě)�

  �(shù)�(jù)段:主要存放程序中要用到的各種全局變量或靜�(tài)的數(shù)�(jù)。一般數(shù)�(jù)段都是可讀,可�(xiě),可�(zhí)行的�

  UNIX�(huán)境下主要有三種類型的目標(biāo)文件�

 ?�?)可重定位文�

  其中包含有適合于其它目標(biāo)文件鏈接�(lái)�(chuàng)建一�(gè)可執(zhí)行的或者共享的目標(biāo)文件的代碼和�(shù)�(jù)�

 ?�?)共享的目標(biāo)文件

  這種文件存放了適合于在兩種上下文里鏈接的代碼和數(shù)�(jù)。種是鏈接程序可把它與其它可重定位文件及共享的目�(biāo)文件一起處理來(lái)�(chuàng)建另一�(gè) 目標(biāo)文件;第二種是動(dòng)�(tài)鏈接程序?qū)⑺c另一�(gè)可執(zhí)行文件及其它的共享目�(biāo)文件�(jié)合到一起,�(chuàng)建一�(gè)�(jìn)程映��

  �3)可�(zhí)行文�

  它包含了一�(gè)可以被操作系�(tǒng)�(chuàng)建一�(gè)�(jìn)程來(lái)�(zhí)行之的文件。匯編程序生成的�(shí)際上是種類型的目�(biāo)文件。對(duì)于后兩種還需要其他的一些處理方能得�,這�(gè)就是鏈接程序的工作了�

  鏈接�(guò)�

  由匯編程序生成的目標(biāo)文件并不能立即就被執(zhí)行,其中可能還有許多�(méi)有解決的�(wèn)��

  例如,某�(gè)源文件中的函�(shù)可能引用了另一�(gè)源文件中定義的某�(gè)符號(hào)(如變量或者函�(shù)�(diào)用等�;在程序中可能調(diào)用了某�(gè)�(kù)文件中的函數(shù),等�。所有的這些�(wèn)�,都需要經(jīng)鏈接程序的處理方能得以解決�

  鏈接程序的主要工作就是將有關(guān)的目�(biāo)文件彼此相連接,也即將在一�(gè)文件中引用的符號(hào)同該符號(hào)在另外一�(gè)文件中的定義連接起來(lái),使得所有的這些目標(biāo)文件成為一�(gè)能夠誒操作系�(tǒng)裝入�(zhí)行的�(tǒng)一整體�

  根據(jù)�(kāi)�(fā)人員指定的同�(kù)函數(shù)的鏈接方式的不同,鏈接處理可分為兩種�

  �1)靜�(tài)鏈接

  在這種鏈接方式�,函�(shù)的代碼將從其所在地靜態(tài)鏈接�(kù)中被拷貝到最終的可執(zhí)行程序中。這樣該程序在被執(zhí)行時(shí)這些代碼將被裝入到該�(jìn)程的虛擬地址空間�。靜�(tài)鏈接�(kù)�(shí)際上是一�(gè)目標(biāo)文件的集�,其中的每�(gè)文件含有�(kù)中的一�(gè)或者一組相�(guān)函數(shù)的代��

  �2� �(dòng)�(tài)鏈接

  在此種方式下,函�(shù)的代碼被放到稱作是動(dòng)�(tài)鏈接�(kù)或共享對(duì)象的某�(gè)目標(biāo)文件�。鏈接程序此�(shí)所作的只是在最終的可執(zhí)行程序中記錄下共享對(duì)象的名字以及其它少量的登記信�。在此可�(zhí)行文件被�(zhí)行時(shí),動(dòng)�(tài)鏈接�(kù)的全�?jī)?nèi)容將被映射到�(yùn)行時(shí)相應(yīng)�(jìn)程的虛地址空間。動(dòng)�(tài)鏈接程序?qū)⒏鶕?jù)可執(zhí)行程序中記錄的信息找到相�(yīng)的函�(shù)代碼�

  �(duì)于可�(zhí)行文件中的函�(shù)�(diào)�,可分別采用�(dòng)�(tài)鏈接或靜�(tài)鏈接的方�。使用動(dòng)�(tài)鏈接能夠使最終的可執(zhí)行文件比較短小,并且�(dāng)共享�(duì)象被多�(gè)�(jìn)程使用時(shí)能節(jié)約一些內(nèi)�,因?yàn)樵趦?nèi)存中只需要保存一份此共享�(duì)象的代碼。但并不是使用動(dòng)�(tài)鏈接就一定比使用靜態(tài)鏈接要優(yōu)�。在某些情況下動(dòng)�(tài)鏈接可能帶來(lái)一些性能上損��

  我�?cè)趌inux使用的gcc編譯器便是把以上的幾�(gè)�(guò)程�(jìn)行捆�,使用戶只使用一次命令就把編譯工作完�,這的確方便了編譯工作,但�(duì)于初�(xué)者了解編譯過(guò)程就很不利了,下圖便是gcc代理的編譯過(guò)程:

  從上圖可以看到:

  �(yù)編譯

  �.c 文件�(zhuǎn)化成 .i文件

  使用的gcc命令是:gcc –E

  �(duì)�(yīng)于預(yù)處理命令cpp

  編譯

  �.c/.h文件�(zhuǎn)換成.s文件

  使用的gcc命令是:gcc –S

  �(duì)�(yīng)于編譯命�   cc –S

  匯編

  �.s 文件�(zhuǎn)化成 .o文件

  使用的gcc 命令是:gcc –c

  �(duì)�(yīng)于匯編命令是  as

  鏈接

  �.o文件�(zhuǎn)化成可執(zhí)行程�

  使用的gcc 命令是: gcc

  �(duì)�(yīng)于鏈接命令是  ld

  總結(jié)起來(lái)編譯�(guò)程就上面的四�(gè)�(guò)程:�(yù)編譯、編�、匯�、鏈�。Lia了解這四�(gè)�(guò)程中所做的工作,對(duì)我們理解頭文件、庫(kù)等的工作�(guò)程是有幫助的,而且清楚的了解編譯鏈接過(guò)程還�(duì)我�?cè)诰幊虝r(shí)定位�(cuò)�,以及編程時(shí)盡量�(diào)�(dòng)編譯器的檢測(cè)�(cuò)誤會(huì)有很大的幫助��

維庫(kù)電子�,電子知�(shí),一查百��

已收錄詞�153979�(gè)

武邑�| 涞源�| 天峻�| 和平�| 孟州�| 宜城�| 静乐�| 太仆寺旗| 海宁�| 益阳�| 抚顺�| 建始�| 恩施�| 桑植�| 民和| 克东�| 诏安�| 丰城�| 淮南�| 肥城�| 永嘉�| 沂源�| 广东�| 兰溪�| 马关�| 广灵�| 晴隆�| 黄陵�| 贵港�| 滦平�| 平罗�| 民勤�| 宁强�| 石柱| 蒲江�| 扬中�| 呼伦贝尔�| 三台�| 桑植�| 连江�| 游戏|