硬件中最小的單位:高低電平余掖;也就是軟件中的位寸爆。
一、關(guān)鍵字
關(guān)鍵字:編譯器預(yù)先定義的具有一定意義的字符串盐欺。
它就是字符串赁豆,只不過被賦予了特定的意義。
C語言有32個(gè)關(guān)鍵字冗美。
如:sizeof
是關(guān)鍵字魔种,而不是函數(shù)。
我們沒有實(shí)現(xiàn)sizeof
粉洼,而是編譯器規(guī)定好的供我們查看內(nèi)存空間容量的一個(gè)工具节预。
printf
是標(biāo)準(zhǔn)庫里面的一個(gè)函數(shù),借助于操作系統(tǒng)属韧。如果嵌入式開發(fā)中沒有OS安拟,直接進(jìn)行裸板操作,就不能調(diào)用該函數(shù)挫剑。但是仍然可以使用sizeof()
查看內(nèi)存空間容量大小去扣。
二、數(shù)據(jù)類型
計(jì)算機(jī):CPU與很多資源樊破,中間通過數(shù)據(jù)總線和地址總線相連愉棱。
C語言的操作對(duì)象/目標(biāo):資源/內(nèi)存(硬件資源(顯存)、LCD緩存哲戚、LED燈奔滑、其他的串口和I2C設(shè)備等)。
C語言如何描述這些資源的屬性顺少?
通過資源屬性的大小——即數(shù)據(jù)類型關(guān)鍵字:它是限制內(nèi)存(土地)大小的關(guān)鍵字朋其。
因此王浴,要記住每個(gè)數(shù)據(jù)類型的大小。
涉及到數(shù)據(jù)類型大小梅猿,一般情況下都不是定值氓辣,與編譯器有關(guān),由編譯器決定袱蚓。
1钞啸、char
硬件芯片操作的最小單位。bit:1和0喇潘;高低電平体斩;位。
char buf[x];
能更好地處理硬件所發(fā)送或接收的數(shù)據(jù)颖低。
如果軟件操作的最小單位和硬件一樣絮吵,那開發(fā)量會(huì)非常龐大。因此軟件采用了集合方式忱屑,把一組bit當(dāng)作軟件操作的單位:8bit == 1Byte (1B),字節(jié)
ASCII碼表:在整個(gè)計(jì)算機(jī)的鍵盤中蹬敲,用8bit不同的狀態(tài)可以描述所有的鍵值。
2想幻、int
其大小就是編譯器最優(yōu)的處理大小粱栖,即系統(tǒng)一個(gè)周期所能接收的最大處理單位。
比如:
- 32位系統(tǒng)脏毯,一個(gè)周期能處理的數(shù)據(jù)是32bit闹究。則:32b(4B)就是int的大小,此時(shí)編譯器最優(yōu)食店。
- 16bit的單片機(jī)系統(tǒng)渣淤,則int大小就是2B(16bit),此時(shí)編譯器最優(yōu)吉嫩。
如果是數(shù)字的處理(循環(huán)价认、計(jì)數(shù)器等),用int比用char好自娩。
3用踩、short
如果沒有特殊要求,用int而不用short忙迁;除非對(duì)空間有非常強(qiáng)烈的限制脐彩,比如在32位電腦中,必須要求空間是16bit處理姊扔,則使用short惠奸。
4、long恰梢、long long
它們是C語言可擴(kuò)展的類型佛南,要看編譯器是否支持梗掰。
三、自定義數(shù)據(jù)類型
C編譯器默認(rèn)定義的內(nèi)存分配不符合實(shí)際資源的形式嗅回。
自定義=基本元素的集合
1及穗、struct:元素之間的合集
struct myabc
{
unsigned int a;
unsigned int b;
unsigned int c;
unsigned int d;
};
// 這是聲明,只是告知編譯器妈拌,具有內(nèi)存空間定義的能力拥坛,但是還沒有進(jìn)行定義蓬蝶。
struct myabc mybuf; // 這是定義
注意:結(jié)構(gòu)體中順序是有要求的尘分。
2、union:共用起始地址的一段內(nèi)存
在一些技巧型的代碼中會(huì)用到丸氛。
union myabc{
char a;
int b;
}; // 聲明
union myabc abc; // 定義
3培愁、enum:枚舉(整型常數(shù)的集合)
定義常數(shù):
#define MON 0
#define TUE 1
#define WED 2
enum與define是一致的,只不過enum可以更好地描述一個(gè)對(duì)象集合缓窜。
enum 枚舉名稱{常量列表};
enum abc {MOD, TUE, WED}; //不管定義多少個(gè)定续,一次只取一個(gè),存在32bit的空間中禾锤。
用處:比如私股,一個(gè)芯片中有一些數(shù)字1、2恩掷、3倡鲸,另一個(gè)芯片中也有一些數(shù)字1、2黄娘、3峭状,但是代表不同的芯片不同的含義,因此兩個(gè)芯片分別定義一個(gè)enum逼争,就可以用一些獨(dú)有的名字來代表1优床、2、3誓焦,讓人一看就明白胆敞。
enum就是一個(gè)讓程序員與程序員交流上更加便利的一種打包的常量集合的概念杂伟。
例子:
/*001.c*/
#include <stdio.h>
enum abc {MOD=100, TUE, WED};
int main()
{
enum abc a1=MOD;
printf("the a1 is %u:%d\n",sizeof(a1),a1);
printf("the %d\n",MOD);
return 0;
}
/*002.c*/
#include <stdio.h>
enum abc {MOD=100, TUE, WED};
int main()
{
enum abc a1 = 800;
printf("the a1 is %u:%d\n",sizeof(a1),a1);
printf("the %d\n",MOD);
return 0;
}
4移层、typedef
見:《typedef(C語言)》
四示罗、類型修飾符
對(duì)內(nèi)存資源在內(nèi)存中的存放位置的限定桨昙。
對(duì)于嵌入式開發(fā)工程師而言艘虎,內(nèi)存分配的具體位置實(shí)際上與某些代碼和流程非常有關(guān)系雕沉。
在內(nèi)核和驅(qū)動(dòng)編程中脑奠,類型修飾符會(huì)被大量地使用摄欲。
要對(duì)類型修飾符有比較深入的總結(jié)才行摸吠。
1侮叮、auto
是一種默認(rèn)情況,分配的區(qū)域是在可讀可寫的內(nèi)存上帽驯。
auto int a;
auto long b;
區(qū)域如果在{ }
中龟再,則是棧空間尼变。
2利凑、register
分配的區(qū)域是在寄存器上。
因此嫌术,register可以定義一些快速訪問的變量哀澈。即,如果某個(gè)變量訪問的頻率很高度气,就使用register修飾割按,可以盡量放在寄存器中。
寄存器:CPU緩存器
內(nèi)存:內(nèi)部存儲(chǔ)器
register int a;
// 編譯器不是一定會(huì)把a(bǔ)放在寄存器中
// 編譯器會(huì)盡量地安排CPU的寄存器去存放a磷籍。但如果寄存器空間不足時(shí)适荣,a還是會(huì)被存放在存儲(chǔ)器中
register只是告訴編譯器盡量去安排,但是如果確實(shí)寄存器空間不足時(shí)院领,它是沒有任何辦法的弛矛,所以它沒辦法一定能確定存儲(chǔ)的位置,因此比然,&
取地址符號(hào)對(duì)register不起作用丈氓。
中看不中用!
// 003.c
#include <stdio.h>
int main()
{
register int a;
a = 0x10;
printf("a is %d\n", a);
return 0;
}
// 004.c
#include <stdio.h>
int main()
{
register int a;
a = 0x10;
printf("a is %d\n", &a);
return 0;
}
register雖然功能不是很強(qiáng)大谈秫,它無法確定一定會(huì)存儲(chǔ)在寄存器上扒寄;但是當(dāng)程序員看到register修飾的變量后,就能夠知道一點(diǎn):該變量的訪問一定很頻繁拟烫!
3该编、static
在多文件工程編程中,在內(nèi)核中硕淑,會(huì)大量地看到static在用课竣。
在嵌入式C中的3種應(yīng)用場(chǎng)景(修飾3種數(shù)據(jù)):
(1)修飾函數(shù)內(nèi)部的變量:局部變量
int fun()
{
int a; ===> static int a;
}
(2)修飾函數(shù)外部的變量:全局變量
int a; ===> static int a;
int fun()
{
}
(3)修飾函數(shù)
int fun(); ===> static int fun();
函數(shù)也可以理解成是一種變量。
// 005.c
#include <stdio.h>
int main()
{
printf("main is %d\n", main);
return 0;
}
4置媳、extern
外部聲明于樟。主要在全局變量和函數(shù)中使用。
5拇囊、const
本意:用于定義常量(經(jīng)const修飾就不能改變了)
實(shí)際:只讀的變量(這個(gè)變量還是可以通過某些方法來改變的)迂曲。
const是C語言的軟肋!中看不中用A认路捧!
const int a = 100;
// 編譯器不允許進(jìn)行顯式地修改
a = 200;
// 但是還是有辦法來對(duì)a進(jìn)行修改的
-
內(nèi)存泄漏
char *p; // 這2個(gè)等價(jià) const char *p; // 建議用它 char const *p; // 建議p指向的內(nèi)存空間关霸,內(nèi)容是只讀的。 // p是1字節(jié)1字節(jié)(char)去讀的杰扫。p可以指向任意空間队寇,但是只要指向了,就建議它的內(nèi)容是只讀的章姓。 // ——可理解為:字符串佳遣。 // 這2個(gè)等價(jià) char * const p; // 建議用它 char *p const; // 建議p永遠(yuǎn)指向第一次指向的地址。它指向固定地址凡伊,但是地址里面的內(nèi)容是可以變的零渐。 // 一般這種定義,都是硬件資源的定義窗声。比如相恃,緩存地址是焊好的,或者芯片做的時(shí)候就做死了笨觅,但是里面的內(nèi)容是可以變的。 const char * const p; // 限制更加嚴(yán)格耕腾。它指向的地址不能變见剩,里面的內(nèi)容也不能變。 // 工程中使用得不多扫俺,硬件中描述比較多苍苞。比如,描述ROM設(shè)備
例1:
// 001.c
#include <stdio.h>
int main()
{
char *p = "hello world\n"; // 雙引號(hào)表示整型常量狼纬,里面的內(nèi)容是不可變的
printf(" the first world is %x\n", *p);
*p = 'a';
printf("p is %s\n", p);
}
例2:
// 002.c
#include <stdio.h>
int main()
{
char buf[] = {"hello world\n"};
char *p = buf;
printf("the first world is %x\n", *p);
*p = 'a';
printf("p is %s\n", p);
}
例3:
// 003.c
#include <stdio.h>
int main()
{
const char *p = "hello world\n"; // 直接用const修飾只讀內(nèi)容
printf(" the first world is %x\n", *p);
*p = 'a';
printf("p is %s\n", p);
}
例4:
// 004.c
#include <stdio.h>
int main()
{
const int a = 0x12345678;
int b = 0x11223344;
int *p = &b;
*(p+1) = 0x100; // p+1:a的地址
printf("a is %x\n", a);
}
const只是建議盈简,不是強(qiáng)制凑耻。只是編譯時(shí)不會(huì)變,運(yùn)行時(shí)如果通過越界訪問柠贤,也可以改變香浩。
6、volatile
它不是限定內(nèi)存存放位置的修飾符臼勉,而是告知編譯器編譯方法的修飾符邻吭。
告知編譯器:不優(yōu)化編譯。
volatile的使用與地址關(guān)系比較大宴霸;在嵌入式和驅(qū)動(dòng)開發(fā)中囱晴,與硬件打交道比較多岸裙。在上層開發(fā)中,幾乎看不到這個(gè)關(guān)鍵字速缆。
volatile修飾變量的值的修改降允,不僅僅可以通過軟件,也可以通過其他方式(硬件外部的用戶)艺糜。
例子:鍵盤按下剧董,值改變,LCD亮破停。
volatile char *p;
*p == 0x10; // 會(huì)一直從里面讀翅楼,而不會(huì)優(yōu)化掉。