日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

C语言深度解剖

發(fā)布時(shí)間:2025/3/21 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言深度解剖 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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)鍵字

1】 任何類型的指針都可以賦值給void*,無(wú)需進(jìn)行強(qiáng)制轉(zhuǎn)換;反之不行 void* p1; int* p2; p1 = p2; //正確 p2 = p1; //錯(cuò)誤——>修改成 p2 = (int*)p1;2void不能代表一個(gè)真實(shí)的變量,因?yàn)槎x變量時(shí)必須為其分配內(nèi)存空間,定義void變量,編譯器都不知道該分配多大的內(nèi)存空間,所以void不能代表一個(gè)真實(shí)的變量。 void a; //編譯錯(cuò)誤 function(void a); //編譯錯(cuò)誤3】幾個(gè)函數(shù) void memset(void* buffer,int ch,size_t num); 例:int array[100];memset(array,0,100*sizeof(int)); //將數(shù)組array清0

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ū)別1const是在編譯期間確定值,#define是在預(yù)處理階段2const有類型 (3const在程序運(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常量3sizeof(枚舉類型)相當(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)算符

#include <iostream> using namespace std; int main() {int i = 0; int k =0;k = (i++,i++,i++); // k = 2, i = 3i = 0; k =0;k = (++i,++i,++i); //k = 3, i = 3i = 0; k =0;k = (i++)+(i++)+(i++); // k = 0, i = 3 i = 0; k =0;k = (++i)+(++i)+(++i); // k = 由編譯器決定, i = 3i = 0; k =0;k =(++i,i++,i+10); // k = 12, i = 2 } 逗號(hào)表達(dá)式,i在遇到每個(gè)逗號(hào)后,認(rèn)為本計(jì)算單位已經(jīng)結(jié)束,i自增 因此:k = (i++,i++,i++);前兩個(gè)i++遇到逗號(hào)時(shí)自增,最后一個(gè)i++遇到分號(hào)時(shí)自增 k = (i++)+(i++)+(i++);遇到分號(hào)后才認(rèn)為本計(jì)算單位結(jié)束,i這時(shí)候自增 k = (++i)+(++i)+(++i); //編譯器不同,結(jié)果不同 (1)k的結(jié)果可能是9,即i先三次自增后變成3,再相加為9; (2)k的結(jié)果可能是,k = ( (++i)+(++i) ) + (++i);即計(jì)算前兩個(gè)i的和,這時(shí)候i自增兩次則i = 2,2個(gè)i的和為4,然后再加上第三次自增的i得7.

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 **P

20、

(一) #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)換

struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p; 假設(shè)p 的值為0x100000。如下表表達(dá)式的值分別為多少? p + 0x1 = 0x___ ? (unsigned long)p + 0x1 = 0x___? (unsigned int*)p + 0x1 = 0x___? 我相信會(huì)有很多人一開始沒看明白這個(gè)問(wèn)題是什么意思。其實(shí)我們?cè)僮屑?xì)看看,這個(gè)知識(shí)點(diǎn) 似曾相識(shí)。一個(gè)指針變量與一個(gè)整數(shù)相加減,到底該怎么解析呢? 還記得前面我們的表達(dá)式“a+1”與“&a+1”之間的區(qū)別嗎?其實(shí)這里也一樣。指針變 量與一個(gè)整數(shù)相加減并不是用指針變量里的地址直接加減這個(gè)整數(shù)。這個(gè)整數(shù)的單位不是 byte 而是元素的個(gè)數(shù)。所以: p + 0x1 的值為0x100000+sizof(Test)*0x1。至于此結(jié)構(gòu)體的大小為20byte,前面的章 節(jié)已經(jīng)詳細(xì)講解過(guò)。所以p +0x1 的值為:0x100014。 (unsigned long)p + 0x1 的值呢?這里涉及到強(qiáng)制轉(zhuǎn)換,將指針變量p 保存的值強(qiáng)制轉(zhuǎn)換 成無(wú)符號(hào)的長(zhǎng)整型數(shù)。任何數(shù)值一旦被強(qiáng)制轉(zhuǎn)換,其類型就改變了。所以這個(gè)表達(dá)式其實(shí)就 是一個(gè)無(wú)符號(hào)的長(zhǎng)整型數(shù)加上另一個(gè)整數(shù)。所以其值為:0x100001。 (unsigned int*)p + 0x1 的值呢?這里的p 被強(qiáng)制轉(zhuǎn)換成一個(gè)指向無(wú)符號(hào)整型的指針。所 以其值為:0x100000+sizof(unsigned int)*0x1,等于0x100004。

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、宏定義

1)不可以用#define定義注釋符號(hào)(因?yàn)樽⑨屜扔陬A(yù)處理指令被處理) 例:#define BSC //#define BMC /*#define EMC */ D) BSC my single-line comment E) BMC my multi-line comment EMC 【分析】 D)和E)都錯(cuò)誤,為什么呢?因?yàn)樽⑨屜扔陬A(yù)處理指令被處理,當(dāng)這兩行被展開成//…或/*…*/時(shí),注釋已處理完畢,此時(shí)再出現(xiàn)//…或/*…*/自然錯(cuò)誤.因此,試圖用宏開始或結(jié)束一段注釋是不行的。2)用define 宏定義表達(dá)式 這些都好理解,下面來(lái)點(diǎn)有“技術(shù)含量”的: 定義一年有多少秒: #define SEC_A_YEAR 60*60*24*365 這個(gè)定義沒錯(cuò)吧?很遺憾,很有可能錯(cuò)了,至少不可靠。你有沒有考慮在16位系統(tǒng)下 把這樣一個(gè)數(shù)賦給整型變量的時(shí)候可能會(huì)發(fā)生溢出?一年有多少秒也不可能是負(fù)數(shù)吧。修改一下: #define SEC_A_YEAR (60*60*24*365)UL3)宏定義中的空格 另外還有一個(gè)問(wèn)題需要引起注意,看下面例子: #define SUM (x) (x)+(x) 【分析】 這還是定義的宏函數(shù)SUM(x)嗎?顯然不是。 編譯器認(rèn)為這是定義了一個(gè)宏:SUM代表的是(x) (x)+(x)。 為什么會(huì)這樣呢?其關(guān)鍵問(wèn)題還是在于SUM后面的這個(gè)空格。 所以在定義宏的時(shí)候一定要注意什么時(shí)候該用空格,什么時(shí)候不該用空格。這個(gè)空格僅僅在定義的時(shí)候有效,在使用這個(gè)宏函數(shù)的時(shí)候,空格會(huì)被編譯器忽略掉。也就是說(shuō),上一節(jié)定義好的宏函數(shù)SUM(x)在使用的時(shí)候在SUM 和(x)之間留有空格是沒問(wèn)題的。比如:SUM(3)和SUM (3)的意思是一樣的。 (4#include文件包含 格式1#include <filename> 其中,filename 為要包含的文件名稱,用尖括號(hào)括起來(lái),也稱為頭文件,表示預(yù)處理到系統(tǒng)規(guī)定的路徑中去獲得這個(gè)文件(即C 編譯系統(tǒng)所提供的并存放在指定的子目錄下的頭文件)。找到文件后,用文件內(nèi)容替換該語(yǔ)句。 格式2#include “filename” 其中,filename 為要包含的文件名稱。雙引號(hào)表示預(yù)處理應(yīng)在當(dāng)前目錄中查找文件名為filename 的文件,若沒有找到,則按系統(tǒng)指定的路徑信息,搜索其他目錄。找到文件后,用文件內(nèi)容替換該語(yǔ)句。 需要強(qiáng)調(diào)的一點(diǎn)是:#include 是將已存在文件的內(nèi)容嵌入到當(dāng)前文件中。

總結(jié)

以上是生活随笔為你收集整理的C语言深度解剖的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。