1. 程式人生 > >嵌入式C語言實戰開發詳解(一)

嵌入式C語言實戰開發詳解(一)

一、概述

1、嵌入式開發中為什麼選擇C語言?

因為作業系統的核心都是使用的C語言,而且C語言也有如下幾個優點:

(1)出色的移植性,能在多種不同體系結構的軟/硬平臺上執行(修改量越小,移植性越好);

(2)簡潔緊湊,使用靈活的語法機制,並能直接訪問硬體(效率高);

(3)很高的執行效率

拓展:

什麼時候使用匯編什麼時候使用C語言呢?(C VS 彙編)

彙編是低階語言,不能實現複雜的功能,所以:

當對硬體做初始化——彙編

當對硬體做複雜操作——C語言

面向過程處理機制 VS 面向物件處理機制(詳情戳網址)

http://blog.csdn.net/wzhcalex/article/details/51878170

2、嵌入式開發中的地位:

(1)嵌入式Linux應用軟體開發工程設計;

(2)嵌入式Linux驅動開發工程師;

(3)嵌入式BSP開發工程師;

(4)嵌入式Kernel(核心)開發工程師;

3、精通C語言考核標準:

(1)企業筆試題;

(2)累積的程式碼量(強化程式設計訓練)

(3)良好的編碼規範(華為的編碼規範要求);

(4)行業應用的專案經驗;

4、如何學習C語言(外功與內功兼修)

(1)零基礎學習經歷過程(菜鳥如何修煉成老鳥)

(2)演算法在C語言開發

(3)《C和指標》《C語言專家程式設計》《程式設計師的自我修養》《高質量C/C++程式設計》《程式設計之美》

5、C語言的標準有哪些?

K&RC、C89、C99、C11

注:

gcc支援的C89,部分相容C99

不同的編譯器標準不一樣

很多編譯器支援的是C89

二、資料型別

1、什麼是資料型別?

資料集合的劃分,不同的資料型別對CPU的意義是不一樣的。

2、資料型別有哪些?


3、左右法則

右左法則:首先從最裡面的圓括號內未定義的識別符號開始閱讀看起,然後往右看,再往左看。每當遇到圓括號時,就應該掉轉閱讀方向。一旦解析完圓括號裡面所有的東西,就跳出圓括號。重複這個過程直到整個宣告解析完畢。

企業筆試題:

1、用變數a給出下列定義

a) 一個整型數(An integer):int a;

b) 一個指向整型數的指標(A pointer to an integer):int  *a;

c) 一個指向指標的的指標,它指向的指標是指向一個整型數(A pointer to a

pointer to an integer):int  **a;

d) 一個有10個整型數的陣列(An array of 10 integers):int a[10];

e) 一個有 10 個指標的陣列,該指標是指向一個整型數的(An array of 10

pointers to integers):int  *a[10];

f) 一個指向有10個整型數陣列的指標(A pointer to an array of 10 integers):

int  (*a)[10];

g) 一個指向函式的指標,該函式有一個整型引數並返回一個整型數(A pointer

to a function that takes an integer as an argument and returns an integer):

int  (*a)(int);

h) 一個有10個指標的陣列,該指標指向一個函式,該函式有一個整型引數並返

回一個整型數( An array of ten pointers to functions that take an integer

argument and return an integer ):  int   (*a[10])(int).

2、int  *(*(*fpl)(int))[10];

fpl:函式指標變數,該函式指標指向一個形參為int,返回值為陣列指標,該陣列指標指向一個整型指標;

     int  *(*(*arr[5])())();

arr:函式指標陣列,該數組裡的元素指向一個形參為空,返回值為函式指標的函式,該函式指標指向一個形參為空,返回值為整型的指標。

     float (*(*b())[ ])();

b:函式,形參為空,返回值為陣列指標,該指標指向一個函式指標陣列,該數組裡的元素指向一個形參為空,返回值為float的函式。

     void* (*c)(char, int(*)());

c:函式指標變數,該函式指標指向一個形參為char 、函式指標,返回值為void*,該函式指標的形參為空,返回值為int。

    void** (*d)(int *,char **(*)(char *,char **));

d:函式指標變數,指向函式形參為char,函式指標,返回值為void **,該函式指標形參為 char*,char**,返回值為char**;

    float(*(*e[10])(int *))[5];

e:函式指標陣列,指向函式形參為 int *,返回值為陣列指標,該陣列元素指向float;

4、隱式型別轉化與強制型別轉化:

char < int < float < double  隱式型別轉化(自動)

程式碼示例:



這就是隱式型別轉換;

強制型別轉化:

程式碼示例:



上述程式碼中的char*就是強制型別轉化

5、資料型別的重要知識點:

位元組長度:

bit;

位元組 = 8bit;

半字 = 2個位元組 = 16bit;

字 = 4個位元組 = 32;

6、基本資料型別

char                1個位元組;

short              2個位元組;

int                  4個位元組;

long               4個位元組;

float               4個位元組;

double           8個位元組;

long long       8個位元組;

程式碼查詢:



拓展:

為什麼任何型別的指標都是4位元組?

因為指標指向地址,地址長度都是固定的。而地址長度是由作業系統決定,如果作業系統是32位的,地址長度為4位元組;如果作業系統是64位的,則地址長度為8位元組。

char 取值範圍:

無符號:0~255(2^8-1)

有符號:-128~127

有符號時:第一個為符號位:

0為整數:0 000 0000 = 0; 0 111 1111 = 127;

1為負數:負數時計算機儲存補碼

                1 000 0000  取反加1  =  -128

                1 111 1111  取反加1  = -1

補碼取反加1成原碼

例如:

列印~2:常量都是有符號的:0 000 0010 

                                    取反:1 111 1101 負值

                              取反加1:0 000 0011   =   -3

程式碼舉例:



分析:-128   1 000 0000

        取反   1 111 1111 

        加1     0 111 1111   =  127

企業筆試題:



輸出結果分析:

有符號:i = 127  a[127] = -128

              i = 128  a[128] = 127

              ...

              i = 255  a[255] = 0; 0 相當於‘\0’

              strlen(a)統計到‘\0’跳出,共255個。

無符號:i = 0   a[0] = 255;

              i = 1   a[1] = 254;

              ...

              i = 255  a[255] = 0;

              strlen(a)統計到‘\0’跳出,共255個。

有符號與無符號資料的區別:

例項解析:



分析:

有符號數和無符號數進行比較運算時(==、<、>、<=、>=)有符號數隱式轉換成了無符號數(即底層的補碼)但是此數從有符號數變成了無符號數。

7、sizeof與strlen區別(詳情請戳網址):

http://blog.csdn.net/wzhcalex/article/details/51852632

8、變數與常量

變數三大特點:

變數的資料型別:主要說明變數佔用的記憶體空間的大小,如 int型;

變數的作用域:變數的有效性範圍,如變數的使用範圍;

變數的儲存類別:變數在記憶體中的儲存方式,不同的儲存方式影響變數在記憶體中的生存週期。


MMU:虛擬地址單元 解決記憶體資源稀缺問題

(列印一個地址:列印的都是虛擬的地址)

為了保護資料的安全,作業系統會對空間做劃分:


棧空間:區域性變數、函式形參、自動變數(呼叫後釋放)

    特點:先進後出,管理許可權:系統

堆空間:malloc、ralloc、calloc 分配空間

    特點:先進先出,管理許可權:使用者

資料段:bss:儲存未初始化的全域性變數

              rodata:常量

              .data(靜態資料區):全域性變數(程式結束後釋放)、static 修飾變數

全域性變數與區域性變數的區別:

(主要從空間的分配,沒有初始化的值 什麼時候釋放等角度入手)

9、宣告與定義:

宣告:不分配記憶體空間,可以宣告多次;

定義:分配記憶體空間,只可以定義一次。

變數的宣告有兩種情況:

定義性宣告:需要建立儲存空間的,例如:int a在宣告時就已經建立了儲存空間;

引用性宣告:不需要建立儲存空間, 例如:extern int a 其中變數a是在別的檔案中定義的。

10、格式化輸出與輸入:

首先,通過程式碼來示例以下不同的資料型別的不同的輸入輸出方法:

#include<stdio.h>

int main()
{
    int i;

    int num;

    char ch;

    float f_num;

    double d_num;

    int a[3];

    char src[100];

    scanf("%d",&num);
    printf("num = %d\n",num);

    getchar();
    scanf("%c",&ch);
    printf("ch = %c\n",ch);

    scanf("%f",&f_num);
    printf("f_num = %f\n",f_num);

    scanf("%lf",&d_num);
    printf("d_num = %lf\n",d_num);

    printf("please input a[3]:\n");
    for(i = 0; i < 3; i++)
    {
        scanf("%d",&a[i]);
    }
    for(i = 0; i < 3; i++)
    {
        printf("a[%d] = %d\n",i,a[i]);
    }

    scanf("%s",src);
    printf("src[100] = %s\n",src);

    return 0;
}
執行結果:



在獲取字元是用一個getchar()清空快取區

以下是getchar()的作用(轉自網路)

1.從緩衝區讀走一個字元,相當於清除緩衝區  

2.前面的scanf()在讀取輸入時會在緩衝區中留下一個字元'\n'(輸入完s[i]的值後按回車鍵所致),所以如果不在此加一個getchar()把

這個回車符取走的話,gets()就不會等待從鍵盤鍵入字元,而是會直接取走這個“無用的”回車符,從而導致讀取有誤 

3. getchar()是在輸入緩衝區順序讀入一個字元(包括空格、回車和Tab) getchar()使用不方便,解決方法:  

(1)使用下面的語句清除回車:  while(getchar()!='\n');  

2)用getche()或getch()代替getchar(),其作用是從鍵盤讀入一個字元(不用按回車),注意要包含標頭檔案<conio.h>

三種獲得字串的方法:scanf    gets     getchar

scanf 與 gets 的區別(以下轉自網路):

gets(s)函式與 scanf("%s",&s) 相似,但不完全相同,使用scanf("%s",&s) 函式輸入字串時存在一個問題,就是如果輸入了空格會認

為字串結束,空格後的字元將作為下一個輸入項處理,但gets()函式將接收輸入的整個字串直到遇到換行為止。  

 1.scanf()  所在標頭檔案:stdio.h  

語法:scanf("格式控制字串",變數地址列表);  

接受字串時:scanf("%s",字元陣列名或指標);  

2.gets()  所在標頭檔案:stdio.h  

語法:gets(字元陣列名或指標); 

 兩者在接受字串時:  

1.不同點:  scanf不能接受空格、製表符Tab、回車等;  而gets能夠接受空格、製表符Tab和回車等;  

2.相同點:  字串接受結束後自動加'\0'。

使用gets獲取字串程式碼:

#include<stdio.h>

int main()
{
    char src[100];

    printf("input a string:");
    gets(src);

    printf("src = %s\n",src);


    return 0;
}
使用getchar()獲取字串:
#include<stdio.h>

int main()
{
    char ch;
    char src[10];

    int i = 0;

    while((ch = getchar()) != '\n')
    {
        src[i] = ch;
	i++;

	if(i == 9)
	{
	     printf("error!\n");
	     exit(1);
	}

	src[i] = '\0';
    
    }

    return 0;
}



如何讓scanf不遇到空格結束呢?

在%前面加空格 

詳情請戳網址:http://blog.csdn.net/mishifangxiangdefeng/article/details/7163002

(技術大牛寫的帖)

獲取陣列方法:

1、

#include<stdio.h>

int main()
{
    int a[3];
    int i;
    int *p = a;

    for(i = 0; i <= 2; i++)
    {
        scanf("%d",p++);
    }

    p = a;

    for(i = 0;i < 3; i++)
    {
        printf("a[%d] = %d\n",i,*(p + i));
    }
        
	return 0;

}
</pre><pre name="code" class="html">2、
#include<stdio.h>

int main()
{
    int a[3];
    int i;
    
    for(i = 0; i <= 2; i++)
    {
        scanf("%d",&a[i]);
    }

    p = a;

    for(i = 0;i < 3; i++)
    {
        printf("a[%d] = %d\n",i,a[i]);
    }
        
	return 0;

}
3、
#include<stdio.h>

int main()
{
    int a[3];
    int i;
   
    for(i = 0; i <= 2; i++)
    {
        scanf("%d",a+i);
    }

    p = a;

    for(i = 0;i < 3; i++)
    {
        printf("a[%d] = %d\n",i,*(a + i));
    }
        
	return 0;

}

第一版本程式碼中
scanf("%d",p++);

不可以用a++ 是因為a是常量不可以自加,*p = a 指標可以自加 p++

printf是行緩衝,我們用以下程式碼驗證:

1、



2、



3、



4、