前言:
2019 手機直播賣貨火了起來,手機掃描文件,人臉識別等好多技術也越來越火。方便人們的技術以後會涉及生活的更多地方。因此,這種技術以後是種趨勢,會需要很多研發人才。作爲安卓開發者,選擇音視頻開發方向或者NDK是個不錯的選擇。而這個方向學習 c/c++ 是必須的,這裏我們先學習c語言。
c語言入門
1.C語言的編譯過程:
- 預處理 預處理階段主要處理include和define等。它把#include包含進來的.h 文件插入到#include所在的位置,把源程序中使用到的用#define定義的宏用實際的字符串代替
- 編譯 編譯階段,編譯器檢查代碼的規範性、語法錯誤等,檢查無誤後,編譯器把代碼翻譯成彙編語言
- 彙編 彙編階段把 .s文件翻譯成二進制機器指令文件.o,這個階段接收.c, .i, .s的文件都沒有問題
- 鏈接 鏈接階段,鏈接的是其餘的函數庫,比如我們自己編寫的c/c++文件中用到了三方的函數庫,在連接階段就需要連接三方函數庫,如果連接不到就會報錯
可以通過以下命令進行操作分佈實現 :
// 將導入所有的.h文件(包括.h中引用的文件)
// #define定義的宏用實際的字符串代替
gcc -E main.c -o main.i
//檢查代碼的正確性
//編譯成彙編語言
gcc -s main.i -o main.s
//將彙編語言翻譯成二進制機器碼
gcc -c main.s -o main.o
// 添加 我們引用的所有 靜態庫(.a)
gcc main.o -o main.exe
2.數據類型
整數類型
上表是不同整數類型所佔的存儲大小和取值範圍。這裏需要我們注意的是:不同的編譯器下,數據存儲的大小可能不同。
浮點型
以上就是整數和浮點數的存儲大小和他們的精度,我們可以通過代碼 sizeof 查看不同類型的存儲大小。
//char
printf("char 的存儲大小:%d\n", sizeof(char));
printf("unsigned char 的存儲大小:%d\n", sizeof(unsigned char));
printf("signed char 的存儲大小:%d\n", sizeof(signed char));
//int
printf("int 的存儲大小:%d\n", sizeof(int));
printf("unsigned int 的存儲大小:%d\n", sizeof(unsigned int));
printf("signed int 的存儲大小:%d\n", sizeof(signed int));
//short
printf("short 的存儲大小:%d\n", sizeof(short));
printf("unsigned short 的存儲大小:%d\n", sizeof(unsigned short));
printf("signed short 的存儲大小:%d\n", sizeof(signed short));
//long (這裏還有個 long long)
printf("long 的存儲大小:%d\n", sizeof(long));
printf("unsigned long 的存儲大小:%d\n", sizeof(unsigned long));
printf("signed long 的存儲大小:%d\n", sizeof(signed long));
printf("long long 的存儲大小:%d\n", sizeof(long long));
//double
printf("double 的存儲大小:%d\n", sizeof(double));
printf("double 最大值:%e\n", DBL_MAX); // #include <float.h>
printf("double 最小值:%e\n", DBL_MIN);
printf("double 精度:%e\n", DBL_DIG);
//float
printf("float 的存儲大小:%d\n", sizeof(float));
printf("float 最大值:%e\n", FLT_MAX);
printf("float 最小值:%e\n", FLT_MIN);
printf("float 精度:%e\n", FLT_DIG);
//long double
printf("long double 的存儲大小:%d\n", sizeof(long double));
printf("long double 最大值:%e\n", LDBL_MAX);
printf("long double 最小值:%e\n", LDBL_MIN);
printf("long double 精度:%e\n", LDBL_DIG);
在這裏,我們需要解釋一下爲什麼char的取值範圍是~128-127。首先,在計算機中表示數字的規則:所有的數字由該數的補碼錶示。那什麼是補碼,原碼和反碼又是什麼?
正數的 原碼,反碼,補碼都一樣,即該數的二進制數;
負數的 原碼:該數的二進制數;
反碼:用該數的原碼求反碼,符號位不變,其他位求反。即 1變0 0變1;
補碼: 用該數的反碼末位+1;
所以:char 1字節=8位,正數補碼就是從 0000 0000開始到 0111 1111 即:0-127; 負數補碼是從1111 1111到 1000 0000 即 -128 ~ -1。(-128 沒有正碼 反碼)
那爲什麼使用補碼錶示數呢?符號位可以和其他位統一處理,減法可以按照加法計算。
格式佔位符:
3.指針:
指針:一種特殊的變量,它保存着另一個變量的內存地址。我們可以通過它來直接操作內存數據。 最爲特殊的變量,指針在使用之前需要先聲明。
type * name=NULL;
type: 可以是任意的c語言數據類型;
name:是指針變量的名字; 例如: ptr_i;
NULL: 我們在聲明指針時,最好賦值爲NULL。不然的話該指針在賦值之前會默認一個隨機的地址,直接操作對系統不友好;
4.指針的使用:
爲了更加全面瞭解指針,我們看一下以下代碼:
// 聲明指針時,賦值位NULL是一個良好的習慣
int *ptr_i = NULL;
int *ptr_i1;
//打印指針的初始值-----0000000000000000
printf("prt_i 的NULL地址:%p\n", ptr_i);
//ptr_i1每次的指向發生變化 ---- 隨機的值
printf("prt_i1 的隨機地址:%p\n", ptr_i1);
//聲明變量
int i = 10;
//將變量i的地址賦值給 ptr_i指針
ptr_i = &i;
// &加變量名,該變量的內存地址
printf("i 的內存地址:%p\n", &i);
// prt_i 的值(即 它指向的地址)
printf("ptr_i 指向的地址:%p\n", ptr_i);
// ptr_i 自身的地址ang
printf("ptr_i 自身的地址:%p\n", &ptr_i);
// ptr_i 指向地址的值
printf("ptr_i 指向的變量的值 %d\n", *ptr_i);
ptr_i = NULL;//最終釋放指針,不然會變成 懸空指針和野指針一樣危險
打印出來的值:
prt_i 的NULL地址:0000000000000000
prt_i1 的隨機地址:0000000000000010
i 的內存地址:000000000061FE0C
ptr_i 指向的地址:000000000061FE0C
ptr_i 自身的地址:000000000061FE10
ptr_i 指向的變量的值 10
注意:使用完指針,置空指針; ptr_i = NULL;//最終將指針置空,不然會變成 懸空指針和野指針一樣危險;
& 用於取一個變量的地址;
* 是一個間接尋址符 用於訪問指針所指向的地址的值;
5.指針的大小:
指針的大小:32位的系統,指針佔4個字節;64位的系統指針佔8個字節。指針的大小和指針類型無關,與系統有關。
printf("char 指針的大小:%d\n", sizeof(char *));
printf("int 指針的大小:%d\n", sizeof(int *));
printf("void 指針的大小:%d\n", sizeof(void *));
printf("long long 指針的大小:%d\n", sizeof(long long *));
char 指針的大小:8
int 指針的大小:8
void 指針的大小:8
long long 指針的大小:8
6.通用指針:
任意一種類型的指針都可以轉化成通用指針,指向的地址不會發生改變,但是指針類型丟失。
int *prptr_i = NULL;
void *ptr_v = NULL;
//將int類型的指針,賦值給void*通用指針
ptr_v = ptr_i;
printf("void指向的值: %d\n", *(int *) ptr_v);
printf("void*的自身地址: %p\n", &ptr_v);
printf("void存儲的地址: %p\n", ptr_v);
printf("ptr_i 指向的地址:%p\n", ptr_i);
prptr_i = NULL;
ptr_v = NULL;
打印結果:void指向的值: 10
void*的自身地址: 000000000061FE00
void存儲的地址 000000000061FE0C
ptr_i 指向的地址:000000000061FE0C
ptr_v 和 ptr_i 是兩個指針,指向了同一地址。但是,ptr_v沒有了指針類型。 在使用指向地址的值時,必須強轉指針類型。
7.指針的算術運算:
上面我們可以看到無論什麼類型的指針,它的指針大小在一樣的系統下都是一樣的。那麼指針類型有什麼用呢?
在C語言中,我們可以對指針進行算術運算(+ ,-,++,--,+=,-=),也就是指針可以進行加減運算。具體規則如下:
1.指針的每一次遞增,它其實會指向下一個元素的存儲單元。
2.指針的每一次遞減,它都會指向前一個元素的存儲單元。
3.指針在遞增和遞減時跳躍的字節數取決於指針所指向變量數據類型長度,比如 int 就是 4 個字節。
int n = 1000;
int *ptr_n = &n;
printf("ptr_n = %p\n", ptr_n);
printf("ptr_n+1 = %p\n", ptr_n + 1);
printf("ptr_n+2 = %p\n", ptr_n + 2);
short m = 1000;
short *ptr_m = &m;
printf("ptr_m = %p\n", ptr_m);
printf("ptr_m+1 = %p\n", ptr_m + 1);
printf("ptr_m+2 = %p\n", ptr_m + 2);
打印結果:ptr_n = 000000000061FDEC
ptr_n+1 = 000000000061FDF0
ptr_n+2 = 000000000061FDF4
ptr_m = 000000000061FDEA
ptr_m+1 = 000000000061FDEC
ptr_m+2 = 000000000061FDEE
可以發現 int類型的指針每次增加的步長是4,short的每次是2.正好是該類型的存儲大小。
8.指針與一維數組:
c語言中數組聲明和java中沒有多大區別。
1. int array[]={1,2,3,4,5}; array 其實就是一個指針,指向數組座標爲0的元素的地址。
int array[] = {1, 2, 3, 4, 5};
int *ptr_array = array; //這裏不需要& 即可賦值,因爲array就是指針
printf("ptr_array=%p\n", ptr_array);
printf("array=%p\n", array);
printf("&array[0]=%p\n", &array[0]);
打印結果:ptr_array=000000000061FDC0
array=000000000061FDC0
&array[0]=000000000061FDC0
2.獲取數組中元素的值的方法
例如:獲取座標爲2 的數組元素的方法: array[2] ; *(array+2) ; ptr_array[2] ; *(ptr_array+2)
printf("*(ptr_array+2) = %d\n", *(ptr_array + 2));
printf("ptr_array[2] = %d\n", ptr_array[2]);
printf("*(array+2) = %d\n", *(array + 2));
printf("array[2] = %d\n", array[2]);
9.指針數組與數組指針:
printf("array+1 = %p\n", array + 1);
printf("&array+1 = %p\n", &array + 1);
先看上面這兩行代碼,它兩的打印結果卻不一樣。
array+1 = 000000000061FDC4
&array+1 = 000000000061FDD4
array +1 :增加了4字節,&array+1增加了20字節。
這是因爲 array 是指向第一個元素的指針;
而 &array則爲:int (* ptr_array)[5] = &array; 這是數組指針,增加一位,指向的地址增加的是數組的大小。
指示點:
1. 數組指針的模板: int (* ptr_array) [ number];
2. 指針數組: int * ptr_array [number] ; 存儲指針的數組。
3. 符號優先級:()> [ ] > *