久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 10077|回復: 5
打印 上一主題 下一主題
收起左側

關于C語言中.h文件怎么書寫?

[復制鏈接]
跳轉到指定樓層
樓主
ID:191831 發表于 2018-12-13 04:34 來自手機 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
寫復雜程序要不同.h文件。.c.h是對應的。
.h文件應該怎么寫?應該有類似模板一樣的書寫要求吧。
求教
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復

使用道具 舉報

來自 4#
ID:155507 發表于 2018-12-13 08:41 | 只看該作者
C語言中.h和.c文件解析
    簡單的說其實要理解C文件與頭文件(即.h)有什么不同之處,首先需要弄明白編譯器的工作過程,一般說來編譯器會做以下幾個過程:

    1.預處理階段

  2.詞法與語法分析階段

  3.編譯階段,首先編譯成純匯編語句,再將之匯編成跟CPU相關的二進制碼,生成各個目標文件 (.obj文件)

  4.連接階段,將各個目標文件中的各段代碼進行絕對地址定位,生成跟特定平臺相關的可執行文件,當然,最后還可以用objcopy生成純二進制碼,也就是去掉了文件格式信息。(生成.exe文件)



  編譯器在編譯時是以C文件為單位進行的,也就是說如果你的項目中一個C文件都沒有,那么你的項目將無法編譯,連接器是以目標文件為單位,它將一個或多個目標文件進行函數與變量的重定位,生成最終的可執行文件,在PC上的程序開發,一般都有一個main函數,這是各個編譯器的約定,當然,你如果自己寫連接器腳本的話,可以不用main函數作為程序入口!!!!

  (main .c文件 目標文件 可執行文件)

  有了這些基礎知識,再言歸正傳,為了生成一個最終的可執行文件,就需要一些目標文件,也就是需要C文件,而這些C文件中又需要一個main函數作為可執行程序的入口,那么我們就從一個C文件入手,假定這個C文件內容如下:



  1.   #include <stdio.h>
  2.   #include "mytest.h"
  3.   int main(int argc, char **argv)
  4.   {
  5.     test = 25;
  6.     printf("test.................%d\n",test);
  7.   }
復制代碼

  mytest.h頭文件內容如下:

  1.   int test;
復制代碼


  現在以這個例子來講解編譯器的工作:

  1.預處理階段:編譯器以C文件作為一個單元,首先讀這個C文件,發現第一句與第二句是包含一個頭文件,就會在所有搜索路徑中尋找這兩個文件,找到之后,就會將相應頭文件中再去處理宏,變量,函數聲明,嵌套的頭文件包含等,檢測依賴關系,進行宏替換,看是否有重復定義與聲明的情況發生,最后將那些文件中所有的東東全部掃描進這個當前的C文件中,形成一個中間"C文件"

  2.編譯階段,在上一步中相當于將那個頭文件中的test變量掃描進了一個中間C文件,那么test變量就變成了這個文件中的一個全局變量,此時就將所有這個中間C文件的所有變量,函數分配空間,將各個函數編譯成二進制碼,按照特定目標文件格式生成目標文件,在這種格式的目標文件中進行各個全局變量,函數的符號描述,將這些二進制碼按照一定的標準組織成一個目標文件

  3.連接階段,將上一步成生的各個目標文件,根據一些參數,連接生成最終的可執行文件,主要的工作就是重定位各個目標文件的函數,變量等,相當于將個目標文件中的二進制碼按一定的規范合到一個文件中再回到C文件與頭文件各寫什么內容的話題上:理論上來說C文件與頭文件里的內容,只要是C語言所支持的,無論寫什么都可以的,比如你在頭文件中寫函數體,只要在任何一個C文件包含此頭文件就可以將這個函數編譯成目標文件的一部分(編譯是以C文件為單位的,如果不在任何C文件中包含此頭文件的話,這段代碼就形同虛設),你可以在C文件中進行函數聲明,變量聲明,結構體聲明,這也不成問題!!!那為何一定要分成頭文件與C文件呢?又為何一般都在頭件中進行函數,變量聲明,宏聲明,結構體聲明呢?而在C文件中去進行變量定義,函數實現呢??原因如下:

  1.如果在頭文件中實現一個函數體,那么如果在多個C文件中引用它,而且又同時編譯多個C文件,將其生成的目標文件連接成一個可執行文件,在每個引用此頭文件的C文件所生成的目標文件中,都有一份這個函數的代碼,如果這段函數又沒有定義成局部函數,那么在連接時,就會發現多個相同的函數,就會報錯

  2.如果在頭文件中定義全局變量,并且將此全局變量賦初值,那么在多個引用此頭文件的C文件中同樣存在相同變量名的拷貝,關鍵是此變量被賦了初值,所以編譯器就會將此變量放入DATA段,最終在連接階段,會在DATA段中存在多個相同的變量,它無法將這些變量統一成一個變量,也就是僅為此變量分配一個空間,而不是多份空間,假定這個變量在頭文件沒有賦初值,編譯器就會將之放入 BSS段,連接器會對BSS段的多個同名變量僅分配一個存儲空間

  3.如果在C文件中聲明宏,結構體,函數等,那么我要在另一個C文件中引用相應的宏,結構體,就必須再做一次重復的工作,如果我改了一個C文件中的一個聲明,那么又忘了改其它C文件中的聲明,這不就出了大問題了,程序的邏輯就變成了你不可想象的了,如果把這些公共的東東放在一個頭文件中,想用它的C文件就只需要引用一個就OK了!!!這樣豈不方便,要改某個聲明的時候,只需要動一下頭文件就行了

  4.在頭文件中聲明結構體,函數等,當你需要將你的代碼封裝成一個庫,讓別人來用你的代碼,你又不想公布源碼,那么人家如何利用你的庫呢?也就是如何利用你的庫中的各個函數呢??一種方法是公布源碼,別人想怎么用就怎么用,另一種是提供頭文件,別人從頭文件中看你的函數原型,這樣人家才知道如何調用你寫的函數,就如同你調用printf函數一樣,里面的參數是怎樣的??你是怎么知道的??還不是看人家的頭文件中的相關聲明啊!!!當然這些東東都成了C標準,就算不看人家的頭文件,你一樣可以知道怎么使用

評分

參與人數 1黑幣 +100 收起 理由
admin + 100 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

來自 5#
ID:155507 發表于 2018-12-13 08:45 | 只看該作者
c語言中.c和.h文件的困惑

  本質上沒有任何區別。 只不過一般:.h文件是頭文件,內含函數聲明、宏定義、結構體定義等內容

  .c文件是程序文件,內含函數實現,變量定義等內容。而且是什么后綴也沒有關系,只不過編譯器會默認對某些后綴的文件采取某些動作。你可以強制編譯器把任何后綴的文件都當作c文件來編。

  這樣分開寫成兩個文件是一個良好的編程風格。

回復

使用道具 舉報

來自 6#
ID:155507 發表于 2018-12-13 09:02 | 只看該作者
合理地使用.h文件能夠很好地理清項目工程的結構和提高編譯的效率。頭文件主要是對函數、全局變量的聲明和一些宏的定義,.h文件是不參與編譯的,#include宏的作用就是預處理的時候在使用這句話的地方用.h文件的內容替換掉這句話。

   聲明的作用也只是告訴編譯器,某個函數或者變量符合在調用之前在程序的某處已經定義過,編譯的時候不報錯,#if !defined#endif 兩個指令主要哦是為了避免在同一個編譯模塊(一個.c文件)中避免重復包含同一個.h文件。

回復

使用道具 舉報

沙發
ID:94031 發表于 2018-12-13 06:39 | 只看該作者
可以參看例程,仿照別人的來寫。
回復

使用道具 舉報

板凳
ID:164602 發表于 2018-12-13 07:58 | 只看該作者
其實,頭文件的功能就是申明資源,特別是多個C文件的時候,簡單地說,就像是庫文件。
例如:reg51.h頭文件,就是單純的申明資源,系統的其它頭文件,還有申明函數的。
如果自己編寫頭文件,就可以包含這兩個方面的內容了,例如:
發下是1602器件的頭文件
#ifndef __LCD_H_
#define __LCD_H_
/**********************************
當使用的是4位數據傳輸的時候定義,
使用8位取消這個定義
**********************************/
#define LCD1602_4PINS

/**********************************
包含頭文件
**********************************/
#include<reg51.h>

//---重定義關鍵詞---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint
#define uint unsigned int
#endif

/**********************************
PIN口定義
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;

/**********************************
函數聲明
**********************************/
/*在51單片機12MHZ時鐘下的延時函數*/
void Lcd1602_Delay1ms(uint c);   //誤差 0us
/*LCD1602寫入8位命令子函數*/
void LcdWriteCom(uchar com);
/*LCD1602寫入8位數據子函數*/       
void LcdWriteData(uchar dat)        ;
/*LCD1602初始化子程序*/               
void LcdInit();                                                  
#endif

它必須配合1602的C文件同時使用
#include"lcd.h"

/*******************************************************************************
* 函 數 名         : Lcd1602_Delay1ms
* 函數功能                   : 延時函數,延時1ms
* 輸    入         : c
* 輸    出         : 無
* 說    名         : 該函數是在12MHZ晶振下,12分頻單片機的延時。
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //誤差 0us
{
    uchar a,b;
        for (; c>0; c--)
        {
                 for (b=199;b>0;b--)
                 {
                          for(a=1;a>0;a--);
                 }      
        }
           
}

/*******************************************************************************
* 函 數 名         : LcdWriteCom
* 函數功能                   : 向LCD寫入一個字節的命令
* 輸    入         : com
* 輸    出         : 無
*******************************************************************************/
#ifndef         LCD1602_4PINS         //當沒有定義這個LCD1602_4PINS時
void LcdWriteCom(uchar com)          //寫入命令
{
        LCD1602_E = 0;     //使能
        LCD1602_RS = 0;           //選擇發送命令
        LCD1602_RW = 0;           //選擇寫入
       
        LCD1602_DATAPINS = com;     //放入命令
        Lcd1602_Delay1ms(1);                //等待數據穩定

        LCD1602_E = 1;                  //寫入時序
        Lcd1602_Delay1ms(5);          //保持時間
        LCD1602_E = 0;
}
#else
void LcdWriteCom(uchar com)          //寫入命令
{
        LCD1602_E = 0;         //使能清零
        LCD1602_RS = 0;         //選擇寫入命令
        LCD1602_RW = 0;         //選擇寫入

        LCD1602_DATAPINS = com;        //由于4位的接線是接到P0口的高四位,所以傳送高四位不用改
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;         //寫入時序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;

//        Lcd1602_Delay1ms(1);
        LCD1602_DATAPINS = com << 4; //發送低四位
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;         //寫入時序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 數 名         : LcdWriteData
* 函數功能                   : 向LCD寫入一個字節的數據
* 輸    入         : dat
* 輸    出         : 無
*******************************************************************************/                  
#ifndef         LCD1602_4PINS                  
void LcdWriteData(uchar dat)                        //寫入數據
{
        LCD1602_E = 0;        //使能清零
        LCD1602_RS = 1;        //選擇輸入數據
        LCD1602_RW = 0;        //選擇寫入

        LCD1602_DATAPINS = dat; //寫入數據
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;   //寫入時序
        Lcd1602_Delay1ms(5);   //保持時間
        LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)                        //寫入數據
{
        LCD1602_E = 0;          //使能清零
        LCD1602_RS = 1;          //選擇寫入數據
        LCD1602_RW = 0;          //選擇寫入

        LCD1602_DATAPINS = dat;        //由于4位的接線是接到P0口的高四位,所以傳送高四位不用改
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;          //寫入時序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;

        LCD1602_DATAPINS = dat << 4; //寫入低四位
        Lcd1602_Delay1ms(1);

        LCD1602_E = 1;          //寫入時序
        Lcd1602_Delay1ms(5);
        LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 數 名       : LcdInit()
* 函數功能                 : 初始化LCD屏
* 輸    入       : 無
* 輸    出       : 無
*******************************************************************************/                  
#ifndef                LCD1602_4PINS
void LcdInit()                                                  //LCD初始化子程序
{
        LcdWriteCom(0x38);  //開顯示
        LcdWriteCom(0x0c);  //開顯示不顯示光標
        LcdWriteCom(0x06);  //寫一個指針加1
        LcdWriteCom(0x01);  //清屏
        LcdWriteCom(0x80);  //設置數據指針起點
}
#else
void LcdInit()                                                  //LCD初始化子程序
{
        LcdWriteCom(0x32);         //將8位總線轉為4位總線
        LcdWriteCom(0x28);         //在四位線下的初始化
        LcdWriteCom(0x0c);  //開顯示不顯示光標
        LcdWriteCom(0x06);  //寫一個指針加1
        LcdWriteCom(0x01);  //清屏
        LcdWriteCom(0x80);  //設置數據指針起點
}
#endif
觀察注意到:C文件中的資源、函數,都是通過頭文件申明的,這樣才能在主C文件中,調用到1602的C文件中的函數
你看主程序是這樣的:
#include<reg51.h>       
#include"lcd.h"

unsigned char PuZh[]=" Pechin Science ";

/*******************************************************************************
* 函 數 名         : main
* 函數功能                   : 主函數
* 輸    入         : 無
* 輸    出         : 無
*******************************************************************************/
void main(void)
{
        unsigned char i;
        LcdInit();
        for(i=0;i<16;i++)
        {
                LcdWriteData(PuZh[i]);       
        }
        while(1)
        {
        }                               
}

知道了自編頭文件的作用,就可以按照上述格式,編寫自己的其它器件的C文件和頭文件了。

評分

參與人數 1黑幣 +80 收起 理由
admin + 80 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 亚洲精品资源 | 天天躁日日躁狠狠的躁天龙影院 | 日本精品一区二区三区视频 | 伊人免费在线观看 | 天天干,夜夜操 | 欧美亚洲国语精品一区二区 | 天堂久久天堂综合色 | 浮生影院免费观看中文版 | 精品国产一区二区在线 | 日韩国产专区 | 国产成人黄色 | 色偷偷888欧美精品久久久 | 久久久精品网站 | 国产一区二区av | 欧美精品一区二区三区蜜桃视频 | 日韩国产精品一区二区三区 | 欧美一区免费 | 亚洲高清久久 | 国产女人叫床高潮大片免费 | 国产成人免费网站 | 国产视频一区二区 | 最新国产在线 | 九九亚洲精品 | 午夜精品一区二区三区在线观看 | 成人欧美一区二区三区黑人孕妇 | 亚洲精品白浆高清久久久久久 | 玖草资源 | av超碰| 色婷婷综合久久久中字幕精品久久 | 国产不卡一区 | 久久久久久久久久一区二区 | 91在线一区二区 | 中文字幕第一页在线 | 日韩欧美在线一区 | 亚洲国产精品一区二区第一页 | 毛片网站在线观看 | 亚洲一区二区中文字幕在线观看 | 91精品国产一区二区三区 | 亚洲精品一区二区三区蜜桃久 | av首页在线 | 视频在线h|