一. 分析技術大綱。
模塊一:linux基礎編程
C語言 + linux基礎 + 開發板使用 + 交叉工具鏈 + 文件IO + 鏈表。
系統編程 + 網絡編程
模塊二:linux高級編程
C++ QT 數據庫,串口編程,音視頻。
模塊三:
STM32 + linux驅動。
二. C語言大綱
- main函數使用事項,變量在內存中變化,基本數據類型,數據類型在內存中儲存方式,運算符號。
- ASCII碼,表達式,語句,逗號表達式,三目運算,控制流。
- 函數的調用,函數聲明,函數實參與形參關係,函數返回值,函數書寫規則,函數名定義,函數調用返回的位置。
- 特殊函數:遞歸函數,回調函數,內聯函數,變參函數。
- 數組定義,數組賦值,數組下標,整型數組,字符數組,指針數組,二維數組。
- 指針定義,指針賦值,指針解引用,空指針,野指針,通用類型指針,整型指針,字符指針,二級指針,數組指針,函數指針,const指針
- 結構體定義,結構體變量,結構體指針,結構體變量與指針如何訪問成員的,如何計算結構體的佔用內存空間大小,結構體數組。
- 聯合體使用,枚舉類型,宏定義,頭文件書寫,條件編譯,拆分多個.c文件,編譯過程。
- 堆區申請與釋放,字符串函數使用。
三. C語言程序框架 – main函數使用
-
C語言程序入口: main函數 -> main() -> 程序從這個函數開始執行。
特點:
1)程序必須以main作爲主函數的命名。
2)在程序中,main函數可以在任意位置,都是被第一個執行。並不是第一個函數就會被第一個執行。
3)main函數有且僅有一個。 -
main函數的基本框架。
main() -> main函數的函數頭
{ -> main函數的函數體
}
- 在linux下,任何函數就像一個任務,任務都會有返回值。
返回值類型?
返回值類型 main()
例子: int main() -> 無論這個函數將來成功,還是失敗,都會返回一個int類型的數據給我。
返回值的值如何返回? -> return就可以將值返回,這個值必須與返回值類型匹配。
return + 返回值結果
例子:
int main()
{
return 0;
}
main函數的返回(在main函數執行了return語句)就意味着程序的退出!
4. 在linux中運行程序時,main函數需要命令行給他傳遞一些參數。
例如:
./main_test aaa bbb -> 執行main_test時,會傳遞兩個額外的參數給程序
1)main函數如何接受額外命令行參數? -> 使用到兩個形式參數argc,argv。
argc -> argument count ->參數數目
argv -> argument value ->參數的值
2)運行程序時,默認接收兩個參數。
int main()
int main(void)
int main(int argc,char *argv[]) -> 願意接收命令行傳遞
例子:
./xxx aaa -> argc=2 argv[0]:"./xxx" argv[1]:“aaa”
./xxx aaa bbb -> argc=3 argv[0]:"./xxx" argv[1]:“aaa” argv[2]:“bbb”
./xxx aaabbb -> argc=2 argv[0]:"./xxx" argv[1]:“aaabbb”
注意:
- %d ->以十進制輸出數據 %s 以字符串形式輸出
- ./xxx也是算一個參數的,屬於argv[0]
- 沒有給參數賦值時,就會給參數賦值爲NULL/隨機值
- 參數與參數之間使用空格分開!
練習:修改程序,實現無論輸入多少個參數,都會打印出所有的參數。
#include <stdio.h>
int main(int argc,char *argv[]) // -> 函數頭(函數名字+函數形式參數列表)
{ // -> 函數體(函數的實現功能過程)
/* 參數有多少個,就循環多少次 */
int i;
for(i=0;i<argc;i++)
{
printf("argv[%d]:%s\n",i,argv[i]);//會將第一個./xxx也打印出來,所以最好i + 1
}
return 0; //程序運行到這裏,就返回一個0作爲函數執行的結果。
}
- 頭文件
1)程序中一定要寫頭文件嗎?
不一定,當我們在程序中,調用一個函數之前,必須先聲明該函數,聲明函數的表達式就是在頭文件中,也就是說,當我們調用了某些函數時,就需要包含對應的頭文件。
2)頭文件作用?
對函數進行聲明,在函數調用之前首先一定要聲明該函數。
3)函數對應的頭文件不需要背,只需要通過man手冊進行查詢即可。
如果說遇到函數的頭文件不知道是哪個?
man 3 printf -> man: 使用man手冊
3: 代表查看庫函數
printf: 代表你需要查詢的東西
SYNOPSIS -> 使用格式
#include <stdio.h> -> 該函數對應的頭文件
4)如果調用了函數,但是不包含對應的頭文件,那麼會怎樣?
main_test.c: In function ‘main’:
main_test.c:10:3: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
//未聲明
編譯警告與編譯出錯區別:
編譯警告: 能生成可執行程序。
編譯出錯: 不能生成可執行程序。
6. 註釋 (註釋不一定要寫,但是寫了註釋,原則:精簡,易懂)
// -> 單行註釋
/* -> 多行註釋
…
…
*/
7. 縮進與空格
1)每一個複合語句{}都需要進行縮進
int main(int argc,char **argv[]*) // -> 函數頭(函數名字+函數形式參數列表)
{ -> 第一次縮進 // -> 函數體(函數的實現功能過程)
/* 參數有多少個,就循環多少次 */
int i;
/* xxxxx */
for(i=0;i<argc;i++)
{ -> 第二次縮進
printf("argv[%d]:%s\n",i,argv[i]);
}
return 0; //程序運行到這裏,就返回一個0作爲函數執行的結果。
}
2)什麼時候需要空格?
頭文件與main函數之間
功能模塊之間
return語句之前
8. 編譯程序
gcc main_test.c -o main_test
9. 運行
./main_test
四. 數據類型
1. 什麼是數據類型?
數據類型描述了一個變量究竟是存放什麼類型的數據。
數據類型分爲基本數據類型和非基本數據類型。
基本數據類型: char short int long float double
非基本數據類型:就是用戶自定義類型,例如數組,指針,結構體…
- 研究下基本數據類型在內存中佔用的空間?
char -> 字符 ‘a’,‘x’
short -> 短整型
int -> 整型
long -> 長整型
float -> 單精度浮點型
double -> 雙精度浮點型
程序在內存上運行時,會不斷地申請空間,不同數據類型會佔用不同的空間。
究竟這些基本數據類型佔用多少個字節?
計算內存空間大小: sizeof()
#include <stdio.h>
int main(int argc,char *argv[])
{
printf("%d\n",sizeof(char));//1
printf("%d\n",sizeof(short));//2
printf("%d\n",sizeof(int));//4
printf("%d\n",sizeof(long));//4 -> 32位:4 不同電腦位數長度不一樣 64位:8
printf("%d\n",sizeof(float));//4
printf("%d\n",sizeof(double));//8
return 0;
}
結論: 基本數據類型佔用空間的大小由編譯系統來決定的。
五. 如何定義變量?
- 公式: 數據類型 + 變量名
數據類型: 從基本數據類型中選擇一個,也可以從非基本數據類型中選擇。
變量名:有一套定義的規則
1)只能由字母,數字,下劃線組成。 a+5
2)不能以數字開頭。 5a
3)不能與系統的關鍵字重名。 return For->正確 for->錯誤
例子: int a;
定義了一個整型變量 -> 錯誤!
如何描述一個變量
在內存中連續申請4個字節,然後使用變量a間接訪問這片內存空間。
舉例子:
int main(int argc,char *argv[])
{
int a; -> 申請4個字節
return 0; -> 釋放掉這4個字節
}
2. 內存分配原則? -> 連續空閒不確定。
1)分配內存空間時,內存一定是連續的。
2)分配內存空間時,一定是空閒(之前的變量已經申請過的空間就不會再被申請到)
3)分配內存空間時,位置是不確定。
六. 運算符
/ + - * % = & && | || == != ++ – += -= *= /= %/
= -> 賦值,把等於號右邊的值賦值給左邊的值。
/ * + -
使用運算符需要注意的地方
% -> 等號的兩邊不能是小數
== -> 判斷==的右邊的值是否等於左邊的值
!= -> 判斷!= 的右邊的值是否不等於左邊的值
& -> 位與
&& -> 邏輯與
| -> 位或
|| -> 邏輯或
邏輯運算原則:非0即真。
++: 自身加1,再把結果賦值給自身
–: 自身減1,再把結果賦值給自身
a++:先運算整個表達式,再加1
++a:先加1,再運算整個表達式
a–:先運算整個表達式,再減1
–a:先減1,再運算整個表達式
a=10;
+=: a+=5; // a = a + 5 //15
a=10;
-=: a-=5; // a=a-5 //5
a=10;
= a=5// a=a5 // a=105=50
a=10;
/= a/=5 // a=a/5 // a=10/5=2
a=10;
%= a%=5 // a=a%5 // a = 10%5=0
七. 變量的賦值,以及作用域,生命週期。
- 變量的賦值
使用"="對變量進行賦值。把等號右邊的值賦值給左邊的值。
1)定義變量的同時初始化。
int a = 100;
2)先定義,後初始化
int a; -> 先申請空間,然後使用變量a間接訪問內存空間,再賦值一個隨機值。
a = 100; -> 把100存放到a所代表的空間中
- 變量生命週期與作用域?
生命週期:這個變量從什麼時候開始出現在內存空間中到什麼時候從內存釋放掉的這個過程。
作用域:這個變量在程序中能夠用到的地方。
局部變量 Vs 全局變量
1)什麼是局部變量?什麼是全局變量?
在程序中函數體內定義的變量,就是局部變量,在函數體外部的定義的變量,就是全局變量。
全局變量在使用的時候一定要從上往下,作用域只能往下,不能往上
int b; -> 全局變量
int main(int argc,char *argv[])
{
int a; -> 局部變量
return 0;
}
2)兩者在內存空間中申請的位置?
#include <stdio.h>
int c = 10; -> 10在rodata段,變量c的空間在data段
int d; -> 變量d的空間在bss段
int main(int argc,char *argv[])
{
int a; -> 棧區
int b = 100; -> 棧區,100在rodata段
return 0;
}
3)初始化值
局部變量未初始化 -> 隨機值
全局變量未初始化 -> 0
4)生命週期
局部變量在定義在該變量的函數結束時,就會釋放空間。
全局變量一開始就會申請,程序結束,全局變量纔會釋放。
5)作用域
局部變量作用範圍就是函數的內部,全局變量作用域就是整個程序。
思考題:
int main(int argc,char *argv[])
- 形式參數屬於局部變量,還是全局變量?
局部變量,當該函數返回時,該函數中的形式參數也需要釋放。
int fun()
{
int a;
}
int main(int argc,char *argv[])
{
int a;
fun();
}
-
能不能在不同的函數中申請相同的變量名的變量。
能。 -
全局變量作用域只能往下,不能往上。
練習: 畫內存分析圖,求出以下程序的結果。
int my_fun(int z)
{
z = z/5;
return z;
}
int fun(int x,int y)
{
int c;
c = x + y;
my_fun( c );
return c;
}
int main(int argc,char *argv[])
{
int a = 10,b = 5;
int ret = fun(a,b);
ret = my_fun(ret);
printf("ret = %d\n",ret); //3
return 0;
}