摘要:博主接下來將會整理一些語言中常見的問題和坑,再看博主解釋的時候可以自己思考一下變量的聲明和定義有什么區別答變量的定義為變量分配地址和存儲空間,變量的聲明不分配地址。指針操作超過了變量的作用域范圍如返回局部變量的地址。
博主接下來將會整理一些語言中常見的問題和坑,再看博主解釋的時候可以自己思考一下
1.變量的聲明和定義有什么區別?
答:變量的定義為變量分配地址和存儲空間,變量的聲明不分配地址。
一個變量的可以在多個地方聲明,在只能在一個地方定義。加上extern修飾的是變量的聲明,說明將這個變量在文件后面定義或者在文件以外
說明:
很多時候一個變量,只是聲明不分配內存空間,直到使用時才初始化,分配內存
for example:外部變量
#includeint main(){ extern int a;//注意在這里是聲明不是定義聲明a已經是一個已經定義了的外部變量,聲明外部變量時可以將變量的類型去掉 extern a;}int a;//此處是定義,定義了a為整型的外部變量
指針常量和常量指針有什么區別
答:指針常量是定義了一個指針,這個指針的值只能夠在定義是初始化,在其他地方不能夠改變。而常量指針是定義了一個指針,這個指針指向了一個只讀對象,不能通過常量指針來修改這個對象的值,但這個對象本身不一定是常量。
注意:
無論是常量指針還是指針常量,其最大的用途就是作為函數的參數,保證實參在調用的時候的不可改變特性
#includeint main(){ int a = 1; int* const p1 = &a;//p1為指針常量 const int* p2 = &a;//p2為常量指針}
strlen和sizeof的區別
答:1.sizeof是一個操作符,而strlen是一個庫函數
2.sizeof的參數可以是數據類型,也可以是變量,而strlen只能以‘/0‘結尾的字符串作為參數
3.編譯器在編譯時就已經計算出sizeof的結果了,而strlen必須在運行時才能計算出來
4.sizeof計算出來的是數據類型所占的內存大小,而strlen計算的是字符串實際的長度
5.數組做sizeof的參數不退化,而傳遞給指針strlen就退化成指針了
結構體可以直接賦值嗎?
答:聲明時可以直接初始化,同一結構體的不同對象之間也可以直接賦值,但結構體中含有指針成員時一定要特別小心!!!!!!!!有可能多個指針指向了同一塊內存時某個指針釋放了這一段內存,可能會導致其他指針的非法操作。因此在釋放前一定要保證其他指針不在使用這一塊空間
sprintf,strcpy,memcpy有什么區別?
1.操作對象不一樣,strcpy操作的兩個對象均為字符串,sprintf的操作源對象可以是多種數據類型,目的操作對象是字符串,memcpy的兩個對象是兩個任意可操作內存的地址,并不限制于任何數據類型
2.執行效率不一樣memcpy效率最高,將一個數組置零的最快方法就是使用memcpy,strcpy次之,而sprintf效率最低
3.實現功能不同,strcpy?主要實現字符串變量間的拷貝,sprintf?主要實現其他數據類型格式到字?符串的轉化,memcpy?主要是內存塊間的拷貝。?「注意」:strcpy、sprintf?與memcpy?都可以實現拷貝的功能,但是針對的對象不同,根據實際需求,來?選擇合適的函數實現拷貝功能
#define和const用什么區別?
答:1.編譯器處理方式不同:#define宏是在預處理階段展開,不能對宏定義進行調試,而const常量是在編譯階段使用.
?2.類型和安全檢查不同:#define宏沒有類型,不做任何類型檢查,僅僅是代碼展開,可能產生邊際效應等錯誤,而const常量有具體類型,在編譯階段會執行類型檢查和作用域檢查;
3. 存儲方式不同:#define宏僅僅是代碼展開,在多個地方進行字符串替換,不會分配內存,存儲于程序的代碼段中,而const常量會分配內存(可以是在堆中也可以是在棧中),但只維持一份拷貝,存儲于程序的數據段中。?
定義域不同:#define宏不受定義域限制,而const常量只在定義域內有效
const定義常量從匯編的角度來看,只是給出了對應的內存地址,而不是象#define一樣給出的是立即數,所以,const定義的常量在程序運行過程中只有一份拷貝(因為是全局的只讀變量,存在靜態區),而?#define定義的常量在內存中有若干個拷貝。
(6)?有些集成化的調試工具可以對const常量進行調試,但是不能對宏常量進行調試。
(7)?提高了效率。?編譯器通常不為普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成為一個編譯期間的常量,沒有了存儲與讀內存的操作,使得它的效率也很高。
(8)?宏替換只作替換,不做計算,不做表達式求解;宏預編譯時就替換了,程序運行時,并不分配內存。
?
懸掛的指針和野指針有什么區別?
答:1.懸掛的指針:當指針所指向的對象已經被釋放了但是該指針沒有發生任何的改變,仍然指向已經被回收的那一塊空間在這種情況下該指針被稱為懸掛的指針
2.野指針:未初始化的指針被稱為野指針
typedef和#define有什么區別?
1.用法不同:typedef?用來定義一種數據類型的別名,增強程序的可讀性。define?主要用來定義?常量,以及書寫復雜使用頻繁的宏。
?執行時間不同:typedef?是編譯過程的一部分,有類型檢查的功能。define?是宏定義,是預編譯的部分,其發生在編譯之前,只是簡單的進行字符串的替換,不進行類型的檢查。
?作用域不同:typedef?有作用域限定。define?不受作用域約束,只要是在define?聲明后的引用?都是正確的。?
對指針的操作不同:typedef?和define?定義的指針時有很大的區別。?
特別要注意:typedef?定義是語句,因為句尾要加上分號。而define?不是語句,千萬不能在句尾加分號!!!!!!!!!!!!!!!!!!!!!!!!
?
如何避免野指針??
1.指針變量聲明時沒有初始化 。方案:在指針聲明時初始化,可以是具體的地址也可以用NULL來初始化。
2.指針變量被free或者delete后沒有置成NULL。方案:指針指向的內存空間被釋放后指針應該指向NULL。
3. 指針操作超過了變量的作用域范圍如返回局部變量的地址。方案:在變量作用域結束前釋放變量的地址空間并讓其指向NULL
棧和隊列有什么區別
棧和隊列都是存儲結構。棧是先進后出,而隊列是先進先出
補充:
棧區和堆區的區別:堆區的存儲是“順序隨意的“而棧區是”先進后出“棧由編譯器自動分配釋放,存放函數的參數值局部變量的值等。類似于數據結構的棧。堆一般是由程序員手動釋放
全局變量和局部變量有什么區別?操作系統和編譯器是如何知道的?
1.全局變量是整個程序都可訪問的變量,誰都可以訪問,生存期在整個程序從運行到結束(在程序結束時所占內存釋放)。
2. 局部變量存在于模塊(子程序,函數)中,只有所在模塊可以訪問,其他模塊不可直接訪問,模塊結束(函數調用完畢),局部變量消失,所占據的內存釋放。
?3.操作系統和編譯器,可能是通過內存分配的位置來知道的,全局變量分配在全局數據段并且在程序開始運行的時候被加載.局部變量則分配在堆棧里面
?
c語言中有那些不同的存儲類說明符?
答: auto ,register,static,extern?
?變量的范圍是什么?變量在c語言中的作用域怎樣
答:變量的范圍是程序的一部分,可以直接訪問該變量。在c語言中所有標識符都在靜態范圍內
如果沒有分號怎樣打印”hello world"
#include
int main()
{
if(printf("hello world))
{
}?
}
什么是NULL?
?NULL是指針沒用指向有效的位置,理想情況下,如果聲明時不知道指針的值,應該將其初始化為NULL,當指針所指向的內存被釋放的時候,我們應該將指針置為NULL
什么是內存泄漏?為什么要避免他??
程序員在堆區開辟了一塊內存而忘記釋放它時就會發生內存泄漏。從定義可以看的出來內存泄漏是比較嚴重的問題,永遠不會停止
什么是局部靜態變量?他們有什么用?
局部靜態變量是一個變量,其生命周期并不以聲明他的函數調用而結束。它延長到整個程序的壽命。所有對該函數 調用都共享局部靜態變量,靜態變量可以用來計算調用函數的次數
局部變量未初始化默認值為0
什么是靜態函數?他們有什么作用?
在c言中函數默認都是全局的,在函數前面加上static修飾使其變為靜態的和全局函數不同的是對靜態函數的訪問僅限于聲明他們的文件,因此當我們想要限制函數訪問時我們可以將其設置為靜態的
#include<>和#include""的區別是什么?
#include<>會先到系統指定目錄尋找頭文件
而#include""先到項目所在目錄尋找頭文件,如果沒有找到再到系統指定目錄尋找頭文件
變量命名的規則是什么??
變量名的開頭只能是字母或者下劃線?。變量名由數字,字母,下劃線組成
數組的特點是什么?
同一個數組中所有的元素數據類型都是相同的?同時數組中所有成員在內存中的地址都是連續的?
數組的分類
數組的分類:數組可以分為靜態數組和動態數組
靜態數組:類似int arr[90];在程序運行時確定數組的大小,運行過程中不能改變數組的大小
動態數組:主要是在堆區申請的空間,數組的大小是在運行過程中確定的!!!可以更改數組的大小
一維數組不初始化,部分初始化,完全初始化的特點??
1.?不初始化:如果是局部數組?數組元素的內容隨機?如果是全局數組,數組的元素內容為0
2.部分初始化:未被初始化的部分?動補0
3.完全初始化:如果?個數組全部初始化?可以省略元素的個數?數組的??由初始化的個數確定
指針和指針變量有什么區別??
1.指針:在內存中每一個字節都有一個地址?,這個地址的編號就是指針。也就是說指針就是地址
2.指針變量:指針變量本質也是一個變量,用來保存地址。所以指針變量和指針是截然不同的概念
?在使用realloc給已分配的堆區追加空間需要注意什么?
記得用指針變量保存realloc的返回值?
聯合體和共用體的區別是什么?
在結構體中成員擁有獨立的空間,
而共用體共享一塊空間,每一個共用體的成員能訪問共用區的空間大小是由成員自身的類型所決定?
如何理解淺拷貝和深拷貝?
1.?當結構體中有指針成員的時候容易出現淺拷?與深拷?的問題。
2.淺拷?就是,兩個結構體變量的指針成員指向同?塊堆區空間,在各個結構體變量釋放的時候會出現多次釋放同?段堆區空間
3.深拷?就是,讓兩個結構體變量的指針成員分別指向不同的堆區空間,只是空間內容拷??份,這樣在各個結構體變量釋放的時候就不會出現多次釋放同?段堆區空間的
普通局部變量,普通全局變量,靜態局部變量,靜態全局變量的區別
?普通局部變量:?存在棧區、不初始化內容隨機、只在定義所在的復合語句中有效、符合語句結束變量空間釋放
普通全局變量?:存在全局區、不初始化內容為0、進程結束空間才被釋放,能被當前源?件或其他源?件使?,只是其他源?件使?的時候,記得使?extern修飾
靜態局部變量:?存在全局區、不初始化內容為0、整個進程結束空間才被釋放,只能在定義所在的復合語句中有效
靜態全局變量?:存在全局區、不初始化內容為0、整個進程結束空間才被釋放,只能被當前源?件使?
描述一下gcc的編譯過程
gcc編譯過程分為4個階段:預處理、編譯、匯編、鏈接。
預處理:頭?件包含、宏替換、條件編譯、刪除注釋
編譯:主要進?詞法、語法、語義分析等,檢查?誤后將預處理好的?件編譯成匯編?件。
匯編:將匯編?件轉換成??進制?標?件
鏈接:將項?中的各個?進制?件+所需的庫+啟動代碼鏈接成可執??件
?
#和##的作用是什么??
#是把宏參數轉換為字符串的運算符,而##是把兩個宏參數連接的運算符?
extern "C"?
extern "c“的作用是為了能夠正確的實現c++代碼調用c語言代碼。加上extern "C"后會指示編譯器這一部分代碼按c語言的編譯方式進行編譯,而不是c++
(void *)ptr和(*(void** ))ptr的結果是否相同?其中ptr為同一個指針
局部變量是否可以和全局變量重名??
能,局部會屏蔽全局。要用全局變量,需要使用”::”?;局部變量可以與全局變量同名,在函數內引用這個變量時,會用到同名的局部變量,而不會用到全局變量。對于有些編譯器而言,在同一個函數內可以定義多個同名的局部變量,比如在兩個循環體內都定義一個同名的局部變量,而那個局部變量的作用域就在那個循環體內。
?
如何引用一個已定義過的全局變量??
?extern?可以用引用頭文件的方式,也可以用extern關鍵字,如果用引用頭文件方式來引用某個在頭文件中聲明的全局變理,假定你將那個編寫錯了,那么在編譯期間會報錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那么在編譯期間不會報錯,而在連接期間報錯。
全局變量可不可以定義在多個可被包含的.c文件包含頭文件。為什嗎??
?可以,在不同的C文件中以static形式來聲明同名全局變量。?可以在不同的C文件中聲明同名的全局變量,前提是其中只能有一個C文件中對此變量賦初值,此時連接不會出錯.
語句for(;1;)有什么問題?他的意思是什么??
和while(1)相同死循環?
do while();和while有什么區別?
一個先循環一遍在判斷,一個判斷以后贊循環??
#includeint main(){char a;char *str=&a;strcpy(str,"hello");printf(str);return 0;}
?這段代碼有什么問題?
?沒有為str分配內存空間,將會發生異常問題出在將一個字符串復制進一個字符變量指針所指地址。雖然可以正確輸出結果,但因為越界進行內在讀寫而導致程序崩潰。
int(*s[10])(int)表示什么意思?
int(*s[10)(int)是一個函數指針數組,每一個元素指向了一個int func(int param)的函數?
以下代碼有什么問題???
#includeint main(){char*s="AAA";printf("%s",s);s[0]="1";printf("%s",s);
?"AAA"是常量。s是指針變量,指向了這個字符串。在聲明時就有問題。應該寫成const char *s="AAA";又因為是常量,所以不能修改,所以s[0]=’1‘是非法的
下面這段代碼會輸出什么??
void func(void){unsigned int a=6;int b=-20;(a+b>6)?puts(">6"):puts("<=6");}
>6
當有符號和無符號運算時,同一轉成無符號,而在有符號最高位的1表示負數,當轉成無符號數時最高位不再表示負數所以是一個很大的數??
?下面這代碼的結果是什么?
include int main(void) { int i = -1; if(i = 0) printf("i = %d/n",i); else if(i = 1) printf("i = %d/n",i); else printf("i = %d/n",i); return 0; }
1,=號是賦值而不是判斷相等
?這段代碼有什么問題?
#include#includeint main(){ char s[] = "abcdefg"; char d[] = "123"; strcpy(d, s); printf("%s %s", s, d);}
結果:
?咦?很奇怪,輸出d是正確的,s發生了截斷!要說越界也應該是d錯啊,這是什么情況?大家不要著急,我們一步步來分析調試找原因。
??????首先我們先來回顧一下strcpy函數的原理,把一個字符串復制到一個字符串上并在末尾追加空字符,但沒有越界檢驗,安全性堪憂。但此題看運行結果是復制成功了,不應該是越界嗎?
??????那再往下就不能靠分析了,得調試程序找錯了,說白了現在問題就在越界這里。為什么了這是因為局部變量是放在棧區的,棧區是先使用高地址在使用低地址。由于在這個編譯器里面,變量之間的存放間隔很小,當strcpy越界的時候,就將s里面的類容給覆蓋了
?
#includeint main(){char *ptr;if(ptr=(char*)malloc(0)==NULL)printf("Got a null pointer);elseprintf("Got a valid pointer);return 0;}
以上代碼會輸出什么?
Got? a vail ponter
?1.找錯
void test (){char string [10];char *str1="0123456789";strcpy(string ,str1);}
這里string數組越界,因為字符串長度為10,還有一個結束符‘/0’。所以總共有11個字符長度。string數組大小為10,這里越界了。
注意:使用strcpy函數的時候一定要注意前面目的數組的大小一定要大于后面字符串的大小,否則便是訪問越界。
?
void test2(){char string [10],str1[10];for(i=0;i<10;i++){str1[i]="a";}strcpy(string,str1);}
這里有一個一眼就能看出的問題,那就是變量i沒有定義,這在代碼編譯階段編譯器可以幫你發現,很容易搞定。然而很多問題是自己造成的漏洞,編譯器是幫不上什么忙的。這里最大的問題還是str1沒有結束符,因為strcpy的第二個參數應該是一個字符串常量。該函數就是利用判斷第二個參數的結束符來得到是否拷貝完畢。所以在for循環后面應加上str1p[9]?=?‘/0’;
注意:字符數組和字符串的最明顯的區別就是字符串會被默認的加上結束符‘/0’。
?
void test3(char *str){char string[10];if(strlen(string )<=10){strcpy(string ,str);}}
?這里的問題仍是越界問題。strlen函數得到字符串除結束符外的長度。如果這里是<=10話,就很明顯越界了。
?下面這段代碼的運行結果是什么?
#includeint sum(int a){auto int c=0;static int b=3;c+=1;b+=2;return a+b+c;}int main(){int i=0;int a=2;for(i=0;i<5;i++){printf("%d ”,sum(a));}return 0;}
運行結果是:8,10,12,14,16,
在求和函數sum里面c是auto變量,根據auto變量特性知每次調用sum函數時變量c都會自動賦值為0.b是static變量,根據static變量特性知每次調用sum函數時變量b都會使用上次調用sum函數時b保存的值。
簡單的分析一下函數,可以知道,若傳入的參數不變,則每次調用sum函數返回的結果,都比上次多2.所以答案是:8,10,12,14,16,
?
int func(int a){int b;switch(a){case 1:b=30;case 2:b=20;case 3:b=16;default :b=0;}return b;}
?func(1)的值為多少?
因為case語句中沒有break,所以無論如何傳給函數的參數是多少結果都是0
a[q-p]=??
int a[3]; a[0]=0; a[1]=1; a[2]=2; int *p, *q; p=a; q=&a[2]; 很明顯:a[q - p] = a[2] = 2;
下面我們來看一段很有趣的代碼
#include int main() { int arr[] = { 6, 7, 8, 9, 10 }; int *ptr = arr; *(ptr++) += 123; printf("%d,%d/n", *ptr, *(ptr++)); return 0; }
結果為 8,7
程序運行結果為:
8,7
1.解釋:*(ptr++)?+=?123這句代碼中,使用了后++
所以效果相當于:*ptr(6)+123=129;
ptr+=1;
也就是運行后*ptr指向larr[1];
2.解釋:printf(“%d,%d/n”,?ptr,?(ptr++));這句代碼的執行涉及到匯編的函數的參數的壓棧過程是從后面的參數開始壓棧的,所以*(ptr++)會先被壓棧,導致ptr++首先生效(引用別人的?因為我不會匯編)
所以往棧里壓入*ptr時,此時的ptr已經變成指向arr[2]了
同理一下代碼的運行結果為8,8;
?
#include int main() { int arr[] = { 6, 7, 8, 9, 10 }; int *ptr = arr; *(ptr++) += 123; printf("%d,%d/n", *ptr, *(++ptr)); return 0; }
如果不是很理解的話,我們可以看一下這個例子
int i=2;printf("%d/n",i++);printf("%d/n",i);
?原理是一樣的
總結:
?(*p)++和*p++的概括
(*p)++指的是先取出p指向的存儲單元中的內容,然后將取出的數值加1,而p仍然指向原來的存儲單元。
*p++則指的是先取出p指向的存儲單元中的內容,然后將p值加1,此時p不再指向原來的存儲單元。
表達式(*p)++和*p++具有不同的含義,(*p)++并沒有修改指針p的指向,而*p++則修改了指針p的指向。
?
?以下代碼的結果是什么?
#includeint i;int main(){--i;if(i>sizeof(i)){printf("大于");}else{printf("小于“);}return 0;}
?結果為:大于:解釋首先全局變量未初始化默認值為0,--i之后變成-1,但是sizeof計計算出來的是無符號整型,在比較的時候會先將-1先轉成無符號數那么-1在內存中的最高位不在是符號位,那么這將會是一個很大的數所以是大于
#includeint main(){ union Data { struct { int x; int y; }s; int x; int y; }d; d.x=1; d.y=2; d.s.x=d.x*d.x; d.s.y=d.y+d.y; printf("%d %d",d.s.x,d.s.y);}
上面這段代碼的運行結果是什么?
答:4,8.解釋union中的成員相對于基地址的偏移量都為0,共用一塊內存
d.x,d.y的起始位置都相同,共享內存空間。給任意一個賦值另外一個也會被賦予相同的值?
x y s.y s.x
x,ys.x s.y d.x=1 1 0 d.y=2 2 0 d.s.x=d.x*d.x 4 0 d.s.y=d.y+d.y 4 8
?最后分享一些比較坑的
優先級問題 | 表達式 | 誤以為的結果 | 實際結果 |
.的優先級高于*,->可以消除這個問題 | *p.f | p所指向對象的字段f (*p).f | p對f取偏移,作為指針然后進行解除引用操作*(p.f) |
[]的優先級高于* | int *ap[] | ap是一個指向int數組的指針 int(*ap)[] | ap是個元素為int指針數組int(*ap[]) |
函數()高于* | int*fp() | fp是一個指針,所指函數返回int int(*fp)() | fp是個函數返回int* int*(fp()) |
==和!=高于位操作符 | (val&mask!=0)? | (val&mask)!=0 | val&(mask!=0) |
==和!=高于= | c=getchar()!=EOF | (c=getchar())!=EOF | c=(getchar()!=EOF) |
算數運算高于移位運算 | msb<<4+lsb | (msb<<4)+lsb | msb<<(4+lsb) |
逗號運算符最低 | i=1,2 | i=(1,2) | (i=1),2 |
博主水平有限,如有錯誤請指正,本文來自網絡收集和我自己的一些理解。如果覺得不錯的可以點個贊,謝謝!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/119305.html
摘要:在正式前端一些小細節前端掘金英文原文,翻譯未來的太讓人興奮了一方面,是全新的頁面布局方式另一方面,是酷炫的濾鏡顏色等視覺效果。老司機教你更好的進行編程個技巧前端掘金并不總是容易處理。 CSS3 實現文字流光漸變動畫 - 前端 - 掘金來自百度前端技術學院的實踐任務:有趣的鼠標懸浮模糊效果,參考:http://ife.baidu.com/course/d...,用CSS3實現了一下,順便...
摘要:實現不定期更新技巧前端掘金技巧,偶爾更新。統一播放效果實現打字效果動畫前端掘金前端開源項目周報前端掘金由出品的前端開源項目周報第四期來啦。 Web 推送技術 - 掘金騰訊云技術社區-掘金主頁持續為大家呈現云計算技術文章,歡迎大家關注! 作者:villainthr 摘自 前端小吉米 伴隨著今年 Google I/O 大會的召開,一個很火的概念--Progressive Web Apps ...
摘要:上面這三種均不造成重載,現在來說明原因。結論對于引用返回,返回的對象必須是棧幀銷毀后還存在的。全局,靜態,未銷毀的函數棧幀當中的都是可以的指針與引用如圖兩者底層實現差不多,引用是用指針模擬的。不建議聲明和定義分離,分離會導致鏈接錯誤。 ...
摘要:此專欄文章是對力扣上算法題目各種方法的總結和歸納整理出最重要的思路和知識重點并以思維導圖形式呈現當然也會加上我對導圖的詳解目的是為了更方便快捷的記憶和回憶算法重點不用每次都重復看題解畢竟算法不是做了一遍就能完全記住的所 ...
摘要:的最后一個大招就是替換一些傳統的服務端語言,例如,,等,在業務層上面使用來開發服務端完全不成問題。更多的的使用細節和技巧建議關注美團博客大搜車論壇下一篇我們開啟如何結合和搭建一個開發環境和項目目錄 往期回顧 前面2期都講得是瀏覽器端的東西比較多,包括Webpack,雖然是Node處理的,但是還是瀏覽器端用的多,對于現在的前端開發來說,不懂一點服務端的東西,簡直沒辦法活,一般的招聘要求都...
閱讀 2851·2021-09-22 15:43
閱讀 4686·2021-09-06 15:02
閱讀 845·2019-08-29 13:55
閱讀 1678·2019-08-29 12:58
閱讀 3061·2019-08-29 12:38
閱讀 1206·2019-08-26 12:20
閱讀 2263·2019-08-26 12:12
閱讀 3310·2019-08-23 18:35