C編譯器和連結器
(1)、初次體驗
# include <stdio.h>int main(int argc, char* argv[]){printf("Hello World!\n");return 0;}
(2)、自己動手,編譯,串連c程式
安裝vc++6.0
CL hello.c /c 編譯 產生。obj檔案,.obj檔案是通用的COFF的格式,是個目標檔案,可以和其他平台互動.
CL hello.c /c /p ,就會多了1個.i檔案。然後開啟它,盡然有400多行,裡面就是stdio.h檔案內容。當然stdio.h檔案中也包含了其他檔案,所以全部展開後就400多行了。
link hello.obj link就為win平台造了個pe格式的hello.exe
如果直接在cmd命名行中 CL hello.c,編譯器給自動連結,同時產生了.obj和.exe 檔。
(3)、位操作:位操作符呢一共有6種,分別是按位與(&),按位或(|),按位異或(^),取反(~),左移(<<),右移(>>)。 (4)、數組特性
#include <stdio.h>void change(int x[]){ int temp = x[0]; x[0] = x[1]; x[1] = temp; printf("數組名的大小為:%d\n", sizeof(x));}int main(void){ int a[2] = {11, 22}; printf("交換前的數組元素為:%d\t%d\n", a[0],a[1]); change(a); printf("交換後的數組元素為:%d\t%d\n", a[0],a[1]); printf("數組名的大小為:%d\n", sizeof(a)); return (0);}
/**********************************************
交換前的數組元素為:11 22
數組名的大小為:4
交換後的數組元素為:22 11
數組名的大小為:8
***********************************************/
數組名做為函數參數的時候,傳遞過去的是這個數組的4位元組首地址,
所以數組名傳參時候的副本只記下4位元組首地址即可。因為傳遞的是地址,所以可以修改原數組的值。
(5)、數組的定址公式:
a[n] = a + sizeof(type) * n
a[n]:你所要定址的數組元素
a:數組首地址
sizeof(type):元素的大小
n:數組下標
(6)、原碼,反碼,補碼
反碼:當是正數的時候呢,它的反碼與原碼一樣。負數呢。就符號位為1,其他位都取反
[+100]反=01100100 [+0]反=00000000
[-100]反=10011011 [-0]反=11111111
注意:在反碼中,零也有兩種表示形式。
補碼:反碼加1
[+100]補=01100100 [+0]補=00000000
[-100]補=10011100 [-0]補=00000000
注意:在補碼中,零有唯一的編碼,[+0[補=[-0]補=00000000。
對於正數,原碼和反碼,補碼都是相同的
這是因為在電腦系統中,數值一律用補碼來表示(儲存)。
主要原因:
1、統一了零的編碼;
2、將符號位和其它數值位統一處理;
3、將減法運算轉變為加法運算;
4、兩個用補碼錶示的數相加時,如果最高位(符號位)有進位,則進位被捨棄。
eg:計算9-6的結果。
9-6=9+(-6)=3
採用補碼來進行計算呢?
9的補碼是0000 1001,-6的補碼是11111010
0000 1001
+1111 1010
1 0000 0011
最高位的1溢出,剩餘8位二進位表示的是3的補碼。結果為3.
(7)、全域變數,全域變數,靜態局部變數,靜態全域變數
全域變數,靜態局部變數,靜態全域變數都在靜態儲存區配置空間,而局部變數在棧裡分配空間
全域變數:定義在函數內部的變數叫局部變數。局部沒有經過任何修飾的局部變數叫自動變數 (它存在於動態資料區——棧),這個地區內的資料隨著程式的運行動態產生和釋放。 函數返回就釋放,函數要調用就產生。關鍵字:預設是atuo(一般不寫)特點:當程式執行到自動變數的範圍的時候就分配
全域變數:定義在函數外部的變數叫全域變數。全域變數只需在一個源檔案中定義,就可以作用於所有的源檔案,當然,其他不包含全域變數定義的源檔案需要用extern 關鍵字再次聲明這個全域變數。
靜態局部變數具有局部範圍,它只被初始化一次,自從第一次被初始化直到程式運行結束都一直存在。它和全域變數的區別在於全域變數對所有的函數都是可見的,而靜態局部變數只對定義自己的函數體始終可見。
靜態全域變數也具有全域範圍,它與全域變數的區別在於如果程式包含多個檔案的話,它作用於定義它的檔案裡。不能作用到其它檔案裡,即被static關鍵字修飾過的變數具有檔案範圍。這樣即使兩個不同的源檔案都定義了相同名字的靜態全域變數,它們也是不同的變數。
(8)、指標和指標變數
指標的本質:其實指標的本質是對記憶體位址的抽象,在電腦中用來表示地址加其解釋方式。
數組的元素如果是char型,那麼這個就是char型數組,如果數組元素都是int型,那麼這個就叫int型數組,
如果數組元素都是指標,那麼這個就是指標數組。
指標變數:存放一個值為其他記憶體對象地址的變數叫指標變數。
指標與地址的區別:地址是表示記憶體的編號資訊;指標有2個資訊,1個是地址資訊,1個是此地址的解釋資訊。比如:int *p表示此指標指向的地址的記憶體對象用int去解析.float *q表示此指標指向的地址的記憶體對象用float去解析。
C語言的直接引用:我們通過變數名來直接引用變數。例如a=123.編譯器會自動將變數名轉換成變數的儲存地址。然後再將123放入變數a的儲存空間中。
C語言的間接引用:首先將變數的地址存放在一個變數(指標變數),然後再通過存放變數的地址的指標變數來引用變數。
#include <stdio.h>void main(){ int a, b; int *p;//定義指標變數p p = &b;//將變數b的地址存放在變數p中 a = 3;//直接引用變數a *p = 5;//間接引用變數b}
(9)、字串和字串函數
9.1、字串格式化和輸入輸出
char word [40];scanf("%s" ,word);printf("%s" ,word);
等價於
char word [40];fgets(word ,40, stdin);fputs(word,stdout);
字串"x"和字元'x'的區別:
"x" 實際上是由兩個字元構成‘x’和'\0'
eg:
char word [40]; scanf("%s" ,word); printf("%s\n" ,word);//sizeof以位元組為單位計算資料的大小printf("%d\n" ,sizeof( word));//strlen 以字元為單位計算字串的長度printf("%d\n" ,strlen( word));
9.2、字串分類
字串常量:是指一對雙引號中的任何字元。編譯器自動會在末尾加上結束標記\0字元,字串常量屬於靜態儲存,在運行中只儲存一份。一般使用#define來定義字串常量。字串常量一般屬於靜態儲存類,靜態儲存是指,如果在一個函數中使用字串常量,即使是多次調用這個函數,該字串在整個運行過程中只有一份。
9.3、字串數組及初始化
const char m[] ="Hello World"
等價於const char m[]={'H','e'....'\0'}
注意標記結束的Null 字元。如果沒有它,得到的就是一個字元數組而不是一個字串。
(9)、指標和數組
指向數組元素的指標變數
int a[5], *p;
p = &a[0];
1.C語言中,數組名代表數組的首地址,就是第一個元素的地址。因為上面的賦值等價於:p = a;
2.指向數組元素的指標也可以定義時賦值: int *p = &a[0];或者 int *p = a;但是不能a=p;因為a是常量,p是變數,就好比x=3哦3=x的概念。
如果p的初始值是&a[0],那麼:p+i和a+i都可以表示元素a[i]的地址;*(p+i)和*(a+i)都表示指標p+i或a+i所指向的數組元素a[i]的值。
總結:引用一個數組元素可以有兩種方法:下標法:a[i];指標法:*(p+i)。
//區別:數組名是常量,儲存第一個數組的地址,指標則是變數。char heart[] = "I Love Mill";char *head = "I Love Till";//(1)都可以使用數組符號for (i=0;i<6;i++){printf("%c",heart[i]);}printf("\n");for (i=0;i<6;i++){printf("%c",head[i]);}printf("\n");//(2)都可以使用指標加法for (i=0;i<6;i++){printf("%c",*(heart+i));}printf("\n");for (i=0;i<6;i++){printf("%c",*(head+i));}printf("\n");//(3) 指標可以使用遞增運算子,而數組名不可以while(*(head)!='\0'){printf("%c",*(head++));}printf("\n");
(10)、指標函數和函數指標
返回指標值的函數
1 傳回值為指標型資料的函數,定義一般的形式為:
類型名 *函數名(參數表)
例如:int* func(int x, int y);
解釋:表示func的傳回值為指向int型資料的指標。
指向函數的指標
定義:類型名 (* 指標變數名) ();
為什麼會存在指向函數的指標:因為啊。函數作為一段程式,在記憶體中也要佔據一片儲存地區呢。
它就會有一個起始地址,即函數的入口地址,
指向函數的指標有什麼作用啊:這樣一來啊,我們就可以定義一個指標變數指向函數,然後通過指標調用函數。將函數作為參數在函數間傳遞。
間接調用與直接調用:通過函數指標來調用函數叫間接調用,通過函數名來調用叫直接調用。
例子:
int (*p)()
表示:p是一個指向函數的指標變數,此函數的傳回值為int型。
int m1(int x, int y){
.....
return
}
void main(){
int a;
int (*p)(int,int) = NULL;
p = m1;
a = p(2,3); //間接調用
}
(11)、指標數組的定義:如果數組的元素都是指標類型,那麼我們就把這種叫做指標數組。
指標數組是如下定義的:類型名字 *數組名[常量運算式]
char *aStringA[]=
{
"I'm not afraid to take a stand",
"We'll walk this road together, through the storm",
"Whatever weather, cold or warm"
};
字串在常量區,長度是12位元組, 元素交換很方便,只需要交換指標。
(12)、指向指標的指標
變數的直接引用與間接引用
通過變數名叫做直接引用,通過指標對變數的引用叫間接引用
間接引用的兩種情況
1,如果在一個指標變數中存放的是一個目標變數的地址叫做一級地址
2,如果在一個指標變數中存放的是指向目標變數的地址的指標變數的地址,那麼這個就叫做二級地址。
void main()
{
int a = 99;
int *pa = &a;
int **ppa = &a;
}
這樣是絕對不行的,因為類型不符呀。會報錯啦。
那麼再看這個程式:
void main()
{
int a = 99;
int *pa = &a;
int **ppa = &pa;
}
返回指標值的函數
(13)、容易混淆的const
關鍵字const 並不是把變數變成常量,在一個符號前加上const限定符只是表示這個符號不能被賦值,也就是它的值對於這個符號來說只是唯讀。const 最有用的地方,就是限制函數的形參,這樣該函數將不會修改實參指標所指的資料,但是其它的函數可能會修改它。
const int limit = 0;
const int *limit = &limit;
limit 是一個指向常量整形的指標,這個指標不能用於修改這個整型值,但在任何時候,這個指標本身的值是可以改變的。
const int * gra;
int const * gra;
int * const gra;
三者的區別:
最後一種情況,指標是唯讀,前兩種情況指標所指向的對象是唯讀,也就是地址儲存的整型值
(14)、C的記憶體配置方式:
記憶體劃分:
1 、棧區 (stack) 由編譯器自動分配釋放 ,存放為運行函數而分配的局部變數、函數參數、返回資料、返回地址等。其操作方式類似於資料結構中的棧。
2 、堆區 (heap) 一般由程式員分配釋放,程式員自己負責在何時用 free 或 delete 釋放記憶體 。動態記憶體的生存期由程式員決定 ,使用非常靈活,但如果在堆上分配了空間,就有責任回收它,否則啟動並執行程式會出現記憶體流失 分配方式類似於鏈表。
3 、全域區 (靜態區static) 存放全域變數、待用資料、常量。程式結束後又系統釋放,全域變數的初始化是編譯器做的。被寫在了可執行檔裡
4 、文字常量區 常量字串就是放在這裡的。 程式結束後由系統釋放。
5 、代碼區 存放函數體(類成員函數和全域函數) 的二進位代碼。可讀可執行。
float x; char place[] = 'doub .."; 系統將留出儲存float或者字串足夠的記憶體空間,你也可以明確的請求數量的記憶體 char place[20] malloc(),在程式運行時分配記憶體。 double *ptd; ptd = (double*)malloc(30*sizeof(double)); 這段代碼請求了30個double類型值的空間,並且把ptd指向該空間所在的位置。 free(),free只釋放它的參數所指向的記憶體塊。 在編譯器的時候,靜態變數的數量是固定的,在程式運行時也不會改變。自動變數使用的記憶體數量在程式執行時自動增加或者減少,但被分配記憶體所使用的記憶體數量只會增加。除非使用free();
(14)、什麼是聲明,什麼定義
在C語言中對象(函數,變數)必須有且只有一個定義,但是可以有多個聲明。extern 聲明告訴編譯器對象的類型和名稱,定義是為對象分配記憶體。
數組和指標:
指標數組:如果數組的元素都是指標類型,那麼他就是指標數組。
定義:int *arr[100]
這個int *x[100]求長度會是多少呢,在32位系統中指標是佔4位元組的,那麼這裡有100個,那就是400位元組.
eg: char *cArr[]= {"hello","world"};
cArr數組裡面放的2個4位元組指標啦.是指向字串的指標,也就是字串的首地址。
(15)、stdafx.h Standard Application Framework Extensions
所謂標頭檔先行編譯,就是把一個工程(Project)中使用的一些MFC標準標頭檔(如Windows.H、Afxwin.H)預先編譯,以後該工程編譯時間,不再編譯這部分標頭檔,僅僅使用先行編譯的結果。這樣可以加快編譯速度,節省時間。 先行編譯標頭檔通過編譯stdafx.cpp產生,以工程名命名,由於先行編譯的標頭檔的尾碼是“pch”,所以編譯結果檔案是projectname.pch。 編譯器通過一個標頭檔stdafx.h來使用先行編譯標頭檔。stdafx.h這個標頭檔名是可以在project的編譯設定裡指定的。編譯器認為,所有在指令#include "stdafx.h"前的代碼都是先行編譯的,它跳過#include "stdafx. h"指令,使用projectname.pch編譯這條指令之後的所有代碼。 因此,所有的MFC實現檔案第一條語句都是:#include "stdafx.h"。
與stdio.h的區別
我們一般用TC或vc編譯C程式的時候都要首先包含這個stdio.h標頭檔,這個標頭檔裡麵包含了scanf和printf函數的定義,如果我們不在程式開頭include這個檔案,那麼你調用上面這兩個函數就不會成功,它其實和c++中的iostream.h檔案的作用差不多的,它們一般都已經在stdafx.h檔案中被包含。