C语言重难点
C語言
編譯過程
GCC概述
GCC最初的全名是GNU C Compiler
隨著GCC支持的語言越來越多,它的名稱變成了GNU Compiler Collection
翻譯官 翻譯組織
文件后綴名 gcc
.c
gcc -o output
gcc -o 輸出的文件名 輸入文件名
gcc -v -o
c語言的編譯過程
預處理—編譯—匯編—鏈接
預處理: .I 文件 替換過程,沒有include define等
編譯: 匯編文件 .s
匯編: 生成目標文件 .o
鏈接: 鏈接資源 build
gcc的語法所對應的內容
預處理:cpp -o a.i 001.c 【gcc -E】
編譯 :/usr/lib/gcc/i486-linux-gnu/4.4.3/cc1 -o a.s 001.c 【gcc -S】
匯編:as -o a.o a.s 【gcc -c】
鏈接:/usr/lib/gcc/i486-linux-gnu/4.4.3/collect2 -o build a.o+… 【gcc -o】 gcc -o build 001.c
【問題】
define include是關鍵字嗎? 是預處理命令
預處理
Include
< > 尖括號是系統中的
" "是自定義的
#include 包含頭文件
#define 宏 替換 不進行語法檢查
#ifdef #else #endif 條件預處理
#include <stdio> #define ABC int main(){#ifdef ABCprintf("=====%s=====",——FILE__);#endifprintf("hello world\n“);return 0; }發行版本不希望別人看到自己的代碼結構,可以設置#ifdef #else#endif ,來調節是否打印。
gcc -DABC == #define ABC
預定義宏(兩個下劃線)
_FUNCTION_ :函數名
_LINE_: 行號
_FILE_ : 文件名
宏體# ## 區別:
# 字符串化
## 連接符號
#include <stdio> #define ABC(x) #x #define DAY(x) myday##xint main() {int myday1 = 10;int myday2 = 20;printf(ABC(ab\n));printf("the day is %d\n",DAY(1));return 0; }結果:
ab
10
DAY(1)調用的是myday1,通過#連接myday
關鍵字
數據類型
硬件芯片操作的最小單位:
bit 1 0
軟件操作的最小單位: 8bit == 1B
網速之間的轉換:
? 4M 4Mbit Kbit/s KB/S
char
硬件芯片操作的最小單位: bit
軟件操作的最小單位: char
8bit = 1B
硬件處理的最小的單位: char
最大的是256
int
大小: 根據編譯器來決定。
編譯器最優的處理大小:
系統一個周期,所能接受的最大處理單位 int
32bit的系統,4B 恰好能接收int類型
16bit的系統 , 2B int
300L 后面的L表示long類型的,可以表示一個很大的數,
long short
特殊長度
unsigned、signed
無符號:數據
有符號:數字
區別:內存空間的最高字節是符號位還是數據
float、double
浮點數
float 代表 4B
double代表 8B
浮點型常量
1.0f 是float類型
1.1 是double 的類型
void
一般是聲明標志,不是使用標志
void fun() 表示函數不反悔。
自定義數據類型
C編譯器默認定義的內存分配不符合實際資源的形式(例如定義寄存器的大小)
struct 、union、enum、typedef
struct
元素之間的和
struct myabc{unsigned int a;unsigned int b;unsigned int c; }; struct myabc mybuf;在結構體最后加分號,abc的順序不同,可能結構的大小也不相同(涉及字節對齊的問題)
union
共用起始地址的一段內存(共用體)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-o8qu2Rvm-1606119305231)(file:///C:/Users/15890/AppData/Local/Packages/Microsoft.Office.OneNote_8wekyb3d8bbwe/TempState/msohtmlclip/clip_image001.png)]
定義b,a可能發生改變。
enum
enumerate 一一列舉
被命名的整型常數的集合
#define MON 0 #define TUE 1 #define WED 2 /* 上述的宏可以表示為 */ enum abc{ MOD = 0,TUE,WED }enum 枚舉名稱{ 常量列表 };
#include <stdio.h>enum week{ Monday = 0 ,Tuesday =1 ,Wednesday = 2,Thursday,Friday,Saturday,Sunday }; int main() {enum weel a1; /* 定義了這樣的空間 */printf("the a1 is %lu\n",sizeof(a1));printf ("the %d\n",WED);return 0; }運行結果:
4
2
枚舉變量就是個整數,只是這個整數可以取這集合面所有的值
enum DAY{}day;定義枚舉類型同時定義枚舉變量;
enum {}day; 可以省略名稱,直接定義枚舉變量;
typedef
數據類型的別名
typedef int a_t; /* a是一個int類型的外號 */ a_t mysize;雜項
Sizeof
它是關鍵字
編譯器給我們查看內存空間容量的一個工具
return
:返回函數
邏輯結構
cpu是順序執行程序
PC 指針分支 --> 選擇
代碼周期性的循環,節約內存。
條件選擇
if else 、switch、case、default、
循環
do、while(條件)、for(次數)、
循環中的控制符
continue、break、goto(在同一個函數中可以跳)
switch(整形數)
{case 整形數i:
}
類型修飾符
對內存資源存放位置的限定
auto、register、static、const、extern、volatile
有的可讀可寫,有的可讀,有的可寫
Auto
可讀可寫的內存空間 默認平時就是 auto char a;如果放入大括號里,就是存放在棧里;
register
register int a;限定變量定義在寄存器上的修飾符; 寄存器速度快,內存上面慢
編譯器會盡量的安排CPU的寄存器取存放這個a,如果寄存器不足,a還是放在存儲器中。
**&**這個符號對register不起作用;
內存(存儲器) 寄存器
0x100 R0,R2
static
靜態變量
**修飾3種數據:**函數內部的變量;函數外部的變量;函數的修飾符;
修飾3種數據:
1)、函數內部的變量
2)、函數外部的變量
int a; ==> static int a; int fun() {}3)、函數的修飾符
int fun(); ==> static int fun();const
常量定義、只讀的變量,不能修改
可以通過指針的技巧去修改;
extern
外部申明;也是再全局變量中使用;
volatile
告知編譯器編譯不優化
修飾變量的值的修改,不僅僅可以通過軟件,也可以通過其他方式(硬件外部的用戶)
int a = 100;while( a==100 );mylcd();[a] 表示 a 的地址
f1: LDR R0,[a] f2: CMP R0,#100 f3: JMPeq f1 ----> JMPEQ f2 f4: mylcd();修飾變量的值的修改,不僅僅通過軟件,也可以通過其他方式。例如: 如果外界接了鍵盤來鍵入a,則就會發生錯誤;
運算符
算術運算符
+、-、*、/、%
* 、/ 大部分是不支持的。
CPU可以在一個周期處理+ ,但是 * 在一個周期處理不了,需要利用軟件進行模擬實現乘法
% 取模,4%3 = 1; 1%3=1;
應用場景:
任意一個N,我們模上M過后,得到M進制的個位數;
取一個范圍的數;
? eg:給一個任意的數,得到一個1到100以內的數:(m%100)+1
循環數據結構的下標。
邏輯運算
真與假 返回結果為 1 和 0
|| 、 & 、
A || B ≠ \neq ?= B || A 不相等
#include <stdio.h> int main() {int a =10;int res;res = ((a==10) || printf("======\n"));printf("the res is %d\n",res);return 0; }結果:1
a若足,則不看式子后面的內容了,若把a != 10,則會輸出 ====
< 、>= 、 <= 、 >、!、? :
位運算
<< 、 >>
左移 : 乘法 *2 二進制下的移位
m << 1; m × 2 \times2 ×2
m << n m × 2 n \times2^n ×2n
[數據、數字]
-1 *2 -2:
8bit (負數先取反,再加一)
1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0
1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1
1 1 1 1 1 1 1 1 === -1 1 1 1 1 1 1 1 0 == -2
負數也滿足
右移:除以2
m >> n m/2^n
符號變量有關 有符號數整數填1,無符號數填0.
如果a為負數,永遠也不會為0;
如果a為正數,可以為0;
& 、 |
A & 0 ----> 0
&: 屏蔽
int a = 0x1234;
a & 0xff00; 屏蔽低8bit,取出高8bit
&:取出
A & 1 —> A
&:清零器 clr
|
A | 0 = A
保留
A | 1 = 1
設置為高電平的方法,設置set
設置一個資源的bit5為高電平,其他位不變
int a;
a = (a | (0x1<<5)); =====> a | (0x1<<n)
清除第五位
int a;
a = a & 0 1 1 1 1 1 31 a & 31 31 : 32bit
~(0x1<<5)
a = a & ~(0x1<<5); =====> a = a & (~(0x1<<n));
我們想資源456bit設置為101?
^
相同位為假
a = a ^ b
b = a ^ b
a = a ^ b
可以交換 a ,b的值
內存訪問
( ) 限制符號
? 函數訪問符號
[ ] 數組,內存訪問的 ID 符號
{ } 函數體的限制符
–> 地址訪問
. 變量訪問
C語言內存空間的使用
指針
指針的概述
內存類型資源地址、門牌號的代名詞
指針變量 : 存放指針這個概念的盒子
int a;
int *p;
char *p;
C語言編譯器對指針這個特殊的概念,有2個疑問?
1、分配一個盒子,盒子要多大?
在32bit系統中,指針就4個字節
2、盒子里存放的地址 所指向 內存的讀取方法是什么?
? 聲明,得到大小,一次讀多少字節。確定位置
指針指向內存空間,一定要保證合法性
浮點數在內存中表現形式很奇怪
浮點數讀取,指針定義為unsigned,無符號類型。
指針修飾符
const
常量、只讀【不能變】
雙引號就是常量,不可改變 const char型
內存屬性:
1、內存操作的大小
2、內存的變化性,可寫可讀
char *p;
const char *p; 【T】 字符串 “ hello world” “aaa”
char const *p;
指向的內容不可以變,指針可以指向其他的地址 字符串
char * const p; 【T】 硬件資源 LCD
char *p const;
指向的內容可以改變,指針不能指向別的地方 硬件資源 LCD
const char * const p ROM
第三種,都不可以變 ROM
volatile
防止優化指向內存地址
char *p;
volatile char *p ;*p == 0x10 與之前的相同:while( *p == 0x10 );xxxx;typedef
char *p;
什么類型 變量名稱;
xxx a;
int (*p[10])(int ,void (*p)(int));char *name_t : name_t是一個指針,指向了一個char類型的內存 typedef char *name_t; name_t是一個指針類型的名稱,指向了一個char類型的內存name_t abc;指針運算符
++ 、–、+、-
int a = 100;
a+1
int *p = xxx [0x12]
p+1 [0x12 + 1*(sizeof(*p))]
指針的加法、減法運算,實際上加的是一個單位,單位的大小可以使用sizeof(p[0])
int *p p+1
char *p p+1
p++ p-- : 更新地址
[ ]
變量名[ n ]
n:ID 標簽
地址內容的標簽訪問方式
取出標簽里的內存值
操作邏輯符
>= 、<= 、== 、 !=
int *p1;
int *p2;
p1 > p2
*p1 > *p2
--------- == !=
1、跟一個特殊值進行比較 0x0 : 地址的無效值,結束標志
if( p == 0x0)
NULL
2、指針必須是同類型的比較才有意義
char *
int *
多級指針
int **p;
存放地址的地址空間
char **p;
p[0] p[1] … p[n]
p[m] == NULL ----> 結束了
數組
數組的定義
內存的一種分配形式
定義一個空間:
1、大小
2、讀取方式
數據類型 數組名[m] m 的作用域是在申請的時候
數組名是一個常量符號,一定不要放到=的左邊 ,舉例。
char buf[100];
buf = “hello world”;
數組名不能 a++,數組名就是數組的常量標簽。
int a[100];
越界
a[10]
數組空間的初始化
空間的賦值
按照標簽逐一處理
int a[10]; [ 0 - 9 ]
a[0] = xx
a[1] = yy;
程序員這樣賦值,工作量比較大,能不能讓編譯器進行一些自動處理,幫助程序員寫如上的程序
----》空間定義時,就告知編譯器的初始化情況,空間的第一次賦值,初始化操作
int a[10] = 空間;
C語言本身,CPU內部本身一般不支持空間和空間的拷貝
int a[10] = {10,20,30};
====> a[0] = 10; a[1] = 20; a[2] = 30; a[3]=0;
數組空間的初始化 和 變量的初始化 本質不同,尤其在嵌入式的裸機開發中,空間的初始化往往需要庫函數的輔助
char
char buf[10] = {‘a’,‘b’,‘c’}; 3個字節
buf當成普通內存來看,沒有問題
buf當成一個字符串來看,最后加上一個’\0’ 字符串的重要屬性,結尾一定有個’\0’
第二次內存初始化,賦值?
逐一處理
buf[0] = ‘h’ buf[1] = ‘e’ … buf[n] = ‘d’, buf[n+1] = 0; (不要忘了0)
strcpy、strncpy
一塊空間,當成字符空間,提供了一套字符拷貝函數
字符拷貝函數的原則:
內存空間和內存空間的逐一賦值的功能的一個封裝體
一旦空間中出現了0這個特殊值,函數就即將結束。
非字符串空間
字符空間
ASCII碼編碼來解碼的空間,----》給人看
%s abc ‘a ’ ‘b’ ’ c’
\0作為結束標志
非字符空間
數據采集 0x00 - 0xff 8bit
開辟一個存儲這些數據盒子
? char buf[10]; ----> string 字符
unsigned char buf[10];----> data 數據
buf = “abc”;
unsigned char *p = sensor_base;
只管逐一拷貝,結束在哪里?只能定義個數
拷貝三要素:
1、src
2、dest
3、個數
memcpy
int buf[10]; int sensor_buf[100];memcpy(buf,sensor_buf,10*sizeof(int));unsigned char buf1[10]; unsigned char sensor_buf[100];// 00 00 00 23 45 78 strncpy(buf,sensor_buf,10) memcpy(buf,sensor_buf,10*sizeof(unsigned char));指針數組
int a[100] char * a[100]; //100個地址sizeof(a) = 100 * 4;int b; sizeof(b)char **a; //地址里面還是地址多維數組
定義一個指針,指向int a[10]的首地址
定義一個指針,指向int b[5][6]的首地址
二維數組是一塊一塊讀的,
int *p1 = a; int **p2 = b; //無效的指針類型int *p; int *p[5];int a; int a[5];int *p[5]; //p是5個空間,5個空間里存int* 與我們定義的一塊一塊讀是不一樣的。 int (*p)[5]; //5個int指針 和int a[5] 相似結構體
字節對齊
strcuct abc {char a; int b};1 + 4 = 5
效率,希望犧牲一點空間換取時間的效率
最終結構體的大小一定是4的倍數
結構體里成員變量的順序不一致,也會影響到他的大小
位域
內存的分布圖
int a; 默認方式
編譯 —》匯編 —》鏈接
*.o build
4G的大小
內核空間 應用程序不許訪問 (占用1G)
棧空間 局部變量 RW
運行時的堆空間 malloc
全局的數據空間 (初始化的,未初始化) static RW data bss
只讀數據段 “hello world” 字符串常量 R TEXT
代碼段 code R TEXT
0x0 :
局部變量;
函數結束后就會“銷毀”失效了。
局部的都在棧上
全局未初始化—bss,初始化—data
static 的變量也在全局 ,但是出了大括號就沒用了(訪問的局部變量),還是存在在全局變量的區域
生成可執行文件,一般打包最后一個段中的內容。 text在代碼段會代入build文件中,占據內存,release版本會把字符進行處理。
棧空間
運行時,函數內部使用的變量,函數一旦返回,就釋放,生存周期是函數內
堆空間
運行時,可以自由,自我管理的分配和釋放的空間,生存周期是由程序員來決定
分配:
malloc(),一旦成功,返回分配好的地址給我們,只需要接受,對于這個新地址的讀法,由程序員靈活把握,輸入參數指定分配的大小,單位就是B。
釋放:
free§;
只讀空間
靜態空間,整個程序結束時釋放內存,生存周期最長
C語言函數的使用
函數概述
一堆代碼的集合,用一個標簽去描述它
函數 數組,函數具備3要素: int *p; int a[100];
1、函數名 (地址)
2、輸入參數
3、返回值
在定義函數時,必須將3要素告知編譯器。
int fun(int,int,char)
{ xxx }
如何用指針保存函數那?
char *p;
char (*p)[10];
int (*p)(int,int,char);
定義函數,調用函數
int fun(int a,char b) {xxxx } int main() {fun(10,2); }char buf[100]; buf[10];輸入參數
承上啟下的功能
調用者:
函數名(要傳遞的數據) //實參
被調者:
函數的具體實現
函數的返回值 函數名(接收的數據) //形參
{
xx xxx
}
實參 傳遞給 形參
傳遞的形式:
拷貝
值傳遞
特點:
void fun(int a) {a = xx;a = sha md5 yy }int main() {int a = 20;fun(a);printf a ==? }上層調用者 保護自己空間值不被修改的能力
地址傳遞
上層,調用者 讓下層 子函數 修改自己空間值的方式,類似結構體這樣的空間,函數與函數之間調用關系—> 連續空間的傳遞 (不傳地址的話,只是 拿來看一下,并不改變本身的值)
void fun(char a); int main() {char a = 'e';fun(a);a == 'c' } void fun(char *b);int a = 10;fun(&a);a ===? 10int a; scanf("%d",a); scanf("%d",&a)將a的值傳遞給函數,不能保證a還為10
連續空間的傳遞
1、數組
數組名 — 標簽
實參:
int abc[10];
fun(abc)
形參:
void fun(int *p) void fun(int p[10])
2、結構體
結構體變量
struct abc{int a;int b;int c;};
struct abc buf;
實參:
fun(buf); fun(&buf)
形參:
void fun(struct abc a1) void fun(struct abc *a2)
**" " —> 初始化const char *
承上啟下的功能
調用者:
函數名(要傳遞的數據) //實參
被調者:
函數的具體實現
函數的返回值 函數名(接收的數據) //形參
{
xx xxx
}
實參 傳遞給 形參
傳遞的形式:
拷貝
值傳遞
特點:
void fun(int a) {a = xx;a = sha md5 yy }int main() {int a = 20;fun(a);printf a ==? }上層調用者 保護自己空間值不被修改的能力
地址傳遞
上層,調用者 讓下層 子函數 修改自己空間值的方式,類似結構體這樣的空間,函數與函數之間調用關系—> 連續空間的傳遞 (不傳地址的話,只是 拿來看一下,并不改變本身的值)
void fun(char a); int main() {char a = 'e';fun(a);a == 'c' } void fun(char *b);int a = 10;fun(&a);a ===? 10int a; scanf("%d",a); scanf("%d",&a)將a的值傳遞給函數,不能保證a還為10
連續空間的傳遞
1、數組
數組名 — 標簽
實參:
int abc[10];
fun(abc)
形參:
void fun(int *p) void fun(int p[10])
2、結構體
結構體變量
struct abc{int a;int b;int c;};
struct abc buf;
實參:
fun(buf); fun(&buf)
形參:
void fun(struct abc a1) void fun(struct abc *a2)
" " —> 初始化const char *
char buf[10] —> 初始化char *
總結
- 上一篇: c语言爱心灯制作步骤,如何使用LED和5
- 下一篇: dedecms 织梦后台系统配置参数空白