C语言深度解剖
1、定義和聲明
定義:創(chuàng)建了對(duì)象并且為該對(duì)象分配內(nèi)存并給該對(duì)象取上名字(該名字就是變量名或者對(duì)象名),注意:名字一旦和這塊內(nèi)存匹配起來(lái),就同生共死,不離不棄,并且這塊內(nèi)存的位置也不能被改變。 聲明:沒有分配內(nèi)存,只是告訴編譯器,這個(gè)名字已經(jīng)被預(yù)定了,別的地方不能再使用它作為變量名和對(duì)象名。extern int a; //聲明 int a; //定義2、皇帝身邊的小太監(jiān)——寄存器register
這個(gè)關(guān)鍵字請(qǐng)求編譯器盡可能地將變量存在CPU內(nèi)部寄存器中,而不是通過(guò)“內(nèi)存尋址訪問(wèn)”,因此可以提高效率。 一個(gè)CPU的寄存器數(shù)量有限,也就幾個(gè)或者幾十個(gè),因此不可能把所有變量全放入到寄存器。【舉例】
小太監(jiān)是皇帝和大臣之間數(shù)據(jù)傳遞的橋梁。 寄存器是CPU和內(nèi)存之間數(shù)據(jù)傳遞的橋梁。 數(shù)據(jù)從內(nèi)存里拿出來(lái)先放到CPU內(nèi)部寄存器,然后CPU再?gòu)募拇嫫骼镒x取數(shù)據(jù)來(lái)處理,CPU處理完數(shù)據(jù)后同樣把數(shù)據(jù)通過(guò)寄存器存放到內(nèi)存中,CPU不直接和內(nèi)存打交道。 寄存器其實(shí)就是一塊一塊小的存儲(chǔ)空間,只不過(guò)存取速度要比內(nèi)存快得多。【注意】
因?yàn)?span id="ozvdkddzhkzd" class="hljs-keyword">register變量可能不存放在內(nèi)存中,所以不能用取址運(yùn)算符&來(lái)獲取register變量的地址3、static在C中的幾個(gè)含義
(1)修飾變量:
__________________________作用域
static全局變量:從定義之處開始,到文件結(jié)尾處結(jié)束(限制在定義的文件中,即使使用關(guān)鍵字extern也不能把作用域擴(kuò)展到別的文件);在定義之處前面的代碼行也不能使用它,但是可以用extern擴(kuò)展到前面的某個(gè)代碼行處。
static局部變量:在函數(shù)體里面定義,就只能在該函數(shù)體里使用。
(2)修飾函數(shù)
static不是指存儲(chǔ)方式,而是對(duì)函數(shù)的作用域僅局限于本文件,該函數(shù)不能被其他文件訪問(wèn)。
4、考點(diǎn):與“零值”進(jìn)行比較
① bool變量與“零值”進(jìn)行比較 if(bool == 0) if(bool == 1)//不好,讓人誤以為是整型變量 if(bool == FALSE) if(bool == TRUE)//TRUE的值不一定都是1 if(!bool) if(bool) //最好的寫法 ②float變量與“零值”進(jìn)行比較 if(float == 0.0) if(float != 0.0)//不好,沒有精度 if((float >= -EPSION)&&(float <= EPSION))//EPSION為定義好的精度,好 ③指針變量與“零值”進(jìn)行比價(jià) if(p == 0) if(p != 0) //不好,讓人誤以為是整型變量 if(!p) if(p) //不好,讓人誤以為是bool變量 if(NULL ==p ) if(NULL != p) //好5、switch
每個(gè)case不要忘記加break switch case禁止使用return語(yǔ)句 case后面只能是整型、字符型、枚舉類型。6、在多層循環(huán)中,長(zhǎng)的循環(huán)的放在內(nèi)部,短的循環(huán)放在外部。
7、void關(guān)鍵字
8、return語(yǔ)句注意事項(xiàng)
return語(yǔ)句不可返回指向“棧內(nèi)存”的“指針”,因?yàn)樵搩?nèi)存在函數(shù)體結(jié)束時(shí)被自動(dòng)釋放。 例如:char* Fun(){char str[100];... ...return str; //在函數(shù)結(jié)束后,str會(huì)自動(dòng)釋放,導(dǎo)致未知的錯(cuò)誤,程序崩潰}9、const修飾只讀的變量必須在定義時(shí)進(jìn)行初始化
const int aa; //編譯失敗,必須初始化 const int aa = 100;//編譯成功 const與#define的區(qū)別 (1)const是在編譯期間確定值,#define是在預(yù)處理階段 (2)const有類型 (3)const在程序運(yùn)行時(shí)只有一個(gè)備份,而#define在內(nèi)存中有若干備份,因此const更加節(jié)省空間。10、柔性數(shù)組
(1)結(jié)構(gòu)體中的柔性數(shù)組成員前面必須至少有一個(gè)其他成員struct st{int a;char b;char ch[]; //等價(jià)于char ch[0];} (2)柔性數(shù)組不屬于sizeof(結(jié)構(gòu)體)的一部分11、enum枚舉類型
enum 枚舉類型名稱 {枚舉常量1;... ...枚舉常量n; }枚舉變量名;枚舉類型和#define的區(qū)別 (1)枚舉類型是在編譯時(shí)候確定其值 (2)可以調(diào)試枚舉常量,但是不能調(diào)試#define常量 (3)sizeof(枚舉類型)相當(dāng)于sizeof(int) #include <iostream> using namespace std; int main() {enum foo{};enum gender{male, female};enum season{spring, summer, autumn, winter};enum week{sunday, monday, tuesday, wednesday, thursday, friday, saturday};printf("%d\n",sizeof(foo));//結(jié)果為4,foo是枚舉類型printf("%d\n",sizeof(gender));//結(jié)果為4,gender是枚舉類型printf("%d\n",sizeof(season));//結(jié)果為4,season是枚舉類型printf("%d\n",sizeof(week));//結(jié)果為4,week是枚舉類型printf("%d\n",sizeof(male));//結(jié)果為4,male是枚舉常量printf("%d\n",sizeof(monday)); //結(jié)果為4,monday是枚舉常量 }12、typedef給已經(jīng)存在的數(shù)據(jù)類型取別名,而不是定義一個(gè)新的數(shù)據(jù)類型。
13、++、–運(yùn)算符
14、如何將數(shù)值存儲(chǔ)到指定的內(nèi)存地址
int *p = (int*)0x12ff7c;*p = 100;等價(jià)于*(int*)0x12ff7c = 100;15、判斷下面錯(cuò)誤么?為什么?
int a[4]; cout<<sizeof(a[5])<<endl; //正確,結(jié)果是sizeof(int) = 4;雖然a[5]發(fā)生越界訪問(wèn),但是在sizeof(a[5])并沒有真正的訪問(wèn)a[5],只是根據(jù)數(shù)組元素的類型來(lái)確定其值,因此并不會(huì)出錯(cuò)。16、數(shù)組名a與&a的含義
a代表數(shù)組首元素的地址,即&a[0] &a代表數(shù)組的首地址【注】 首元素的地址與首地址的值相同,但是意義不同。17、數(shù)組名的含義
數(shù)組名不能作為左值數(shù)組名作為右值的含義 : 與&a[0]一樣,代表的是數(shù)組的首元素的地址(而不是數(shù)組的首地址)18、數(shù)組指針的初始化 格式
char ch[5] = "ABCD"; char ch[5] = "ABCDE"; //編譯失敗,因?yàn)槿萘渴?,此時(shí)"ABCDE"大小是6int (*p1)[5] = ch; //編譯失敗,不能將ch[5]轉(zhuǎn)換成char(*)[5] int (*p2)[5] = &ch; //編譯成功,p2指向數(shù)組ch的首地址(不是首元素的地址)19、二維數(shù)組/二級(jí)指針/指向二維數(shù)組的指針
(1) int a[5][5]; int (*p)[4]; p = a; 問(wèn): &p[4][2] - &a[4][2]; 的值為多少 ? 答: -4. 【解析】 &a[4][2]表示的是&a[0][0] + (5*sizeof(int))*4 + 2*sizeof(int) &p[4][2]表示的是&a[0][0] + (4*sizeof(int))*4 + 2*sizeof(int) p指向數(shù)組容量為4的數(shù)組,因此p+1的地址增量為sizeof(int)*4 (2)預(yù)備知識(shí) char** p;//二級(jí)指針指向指針 char (*p)[4]; //p指向二維數(shù)組【總結(jié)】 多維數(shù)組作為形參等價(jià)于對(duì)應(yīng)的指針數(shù)組 例如:char a[3][4]————char (*p)[4] char a[3][4][5]————char (*p)[4][5] 指針數(shù)組作為形參等價(jià)于對(duì)應(yīng)的多級(jí)指針 例如:char *a[5]————char **P20、
(一) #include <iostream> using namespace std; void f(char a[10]) //或者void f(char *a) {a = "AAAAAAA"; } int main() {char a[10] = "BB";f(a);cout<<a<<endl; //輸出結(jié)果 BB } (二) #include <iostream> using namespace std; void f(char a[10]) //或者void f(char *a) {a = "AAAAAAA"; } int main() {char *a = "BB";f(a);cout<<a<<endl; //輸出結(jié)果 BB } //總結(jié):無(wú)論是main函數(shù)中的a無(wú)論是char字符數(shù)組還是指向字符串常量的指針,想調(diào)用函數(shù)f改變a的內(nèi)容都實(shí)現(xiàn)不成功。因?yàn)樾螀⑹莊中的局部變量,在f調(diào)用完成后釋放,相當(dāng)于沒有調(diào)用f對(duì)a進(jìn)行處理。21、怎么為stu結(jié)構(gòu)體中的name指針?lè)峙鋬?nèi)存?
struct student {char *name;int age; }stu,*pstu; int main() {stu.name = (char*)malloc(sizeof(char)*100); //正確pstu = (struct student*)malloc(sizeof(struct student)); //錯(cuò)誤,【記住】此處只是給結(jié)構(gòu)體分配了內(nèi)存,并沒有給結(jié)構(gòu)體中的指針?lè)峙鋬?nèi)存。 } 【小知識(shí)點(diǎn)】malloc函數(shù)分配sizeof(0)的返回值是NULL指針么? 答:不是,返回一個(gè)正常的內(nèi)存地址,但是你卻無(wú)法使用這個(gè)塊大小為0的內(nèi)存。22、
void fun() {cout<<"FUN"<<endl; } int main() {void (*p)(); //定義一個(gè)p函數(shù)指針*(int*)&p = (int)fun; //將函數(shù)的入口地址賦值給指針變量p(*p)(); } *(int*)&p = (int)fun;的含義: (int*)&p 表示取出p的地址&p,再?gòu)?qiáng)制轉(zhuǎn)換成int*類型的指針。 *(int*)&p 即是取出p指向的int類型的數(shù)據(jù) (int)fun 表示將fun的入口地址轉(zhuǎn)換成int類型的數(shù)據(jù)23、signed、unsigned關(guān)鍵字
#include <iostream> using namespace std;int main() {char a[1000]; //char的范圍 [-128,127],超出這個(gè)范圍的值會(huì)產(chǎn)生溢出int i;for(i=0; i<1000; i++){ //a[0]到a[254]里面的值都不為0,而a[255]的值為0。strlen 函數(shù)是計(jì)//算字符串長(zhǎng)度的,并不包含字符串最后的‘\0’。而判斷一個(gè)字符串是否//結(jié)束的標(biāo)志就是看是否遇到‘\0’。如果遇到‘\0’,則認(rèn)為本字符串結(jié)束。a[i] = -1-i; }cout<<strlen(a)<<endl; //因此strlen(a) = 255int k = -20;unsigned j = 10;cout<<k+j<<endl; //k先從int轉(zhuǎn)為uint,再與j相加unsigned m ;for (m=9;m>=0;m--){printf("%u\n",m); //死循環(huán),m為uint,m>=0恒成立}return 0; }例:#include <iostream> using namespace std; int array[] = {23,32,11,12,35}; #define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0])) //注意:sizeof返回的是unsigned int類型 int main() {int d = -1,x;if(d <= TOTAL_ELEMENTS - 2) //因此d和TOTAL_ELEMENTS - 2進(jìn)行比較時(shí)//先把d從int轉(zhuǎn)換成unsignedint類型,在進(jìn)行比較//d轉(zhuǎn)換完后是個(gè)很大的數(shù),比較結(jié)果不是想要的x = array[d+1]; }24、大端小端
25、地址強(qiáng)制轉(zhuǎn)換
26、
int main() {int a[4]={1,2,3,4};int *ptr1=(int *)(&a+1);int *ptr2=(int *)((int)a+1);printf("%x,%x",ptr1[-1],*ptr2);return 0; } ptr1:將&a+1 的值強(qiáng)制轉(zhuǎn)換成int*類型,賦值給int* 類型的變量ptr,ptr1 肯定指到數(shù)組a 的下一個(gè)int 類型數(shù)據(jù)了。ptr1[-1]被解析成*(ptr1-1),即ptr1 往后退4 個(gè)byte。所以其值為0x4。 ptr2:按照上面的講解,(int)a+1 的值是元素a[0]的第二個(gè)字節(jié)的地址。然后把這個(gè)地址強(qiáng)制轉(zhuǎn)換成int*類型的值賦給ptr2,也就是說(shuō)*ptr2 的值應(yīng)該為元素a[0]的第二個(gè)字節(jié)開始的連續(xù)4 個(gè)byte 的內(nèi)容。 【問(wèn)題來(lái)了】這連續(xù)4 個(gè)byte 里到底存了什么東西呢?也就是說(shuō)元素a[0],a[1]里面的值到底怎么存儲(chǔ)的。這就涉及到系統(tǒng)的大小端模式了! 如果是小端模式的話,*ptr2 的值為0x2000000。 如果是大端模式的話,*ptr2 的值為0x100。27、
(1) ++i+++i+++i;解讀 ((++(i++))+(i++))+i;(2) ++i+++++i+++i;解讀 (++((i++)++))+(i++)+i//優(yōu)先級(jí):后自增 > 前自增27、幾個(gè)容易被誤解的運(yùn)算符優(yōu)先級(jí)
28、宏定義
總結(jié)
- 上一篇: 高级(复杂)指针的含义
- 下一篇: 改善程序的55个具体做法