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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C专家编程 总结

發布時間:2025/3/20 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C专家编程 总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1 類型轉換

當執行算術運算時,操作數的類型如果不同,就會發生轉換,數據類型一般朝著浮點精度高、長度更長的方向轉換,整數型如果轉換為signed不會丟失信息,就轉換為signed,否則轉換為unsigned。

K&R C所采用無房戶后保留原著,就是當一個無符號類型與int或更小的整型混合使用時,結果類型是無符號類型。

?

2 C語言中const并不真正表示常量。

?

3 switch語句的缺點

1)switch語句最大的缺點是它不會在每個case標簽后面的語句執行完畢后自動終止。

2)由于break語句事實上跳出的是最近的那層循環語句或switch語句,所以break可能使switch語句提前跳出結束。

?

4 C語言中的符號重載

?

符號  ? ? ? 意義
static 

在函數內部,表示該變量的值在各個調用間一直保持延續性

在函數這一級,表示該函數只對本文件可見

extern

用于函數定義,表示全局可見(屬于冗余的)

用于變量,表示它在其他地方定義

void

作為函數的返回類型,表示不返回任何值

在指針聲明中,表示通用指針的類型

位于參數列表中,表示沒有參數

*

乘法運算符

用于指針,間接引用

在聲明中,表示指針

&

位的AND操作符

取地址運算符

=賦值符
==比較運算符

<=

<<=

小于運算符

左移復合賦值運算符

<

小于運算符

#include指令的左定界符

()

在函數定義中,包圍形式參數表

調用一個函數

改變表達式的運算次序

將值轉換為其他類型(強制類型轉換)

定義帶參數的宏

包圍sizeof操作符的操作數(如果它是類型名)

5 C語言中的優先級

優先級問題表達式人們可能誤以為的結果實際結果

.的優先級高于*。->操作符

用于消除這個問題

*p.fp所指對象的字段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)!=0val&(mask!=0)
==和!=高于賦值符c=getchar()!=EOF(c=getchar())!=EOFc=(getchar()!=EOF)
算術運算符高于移位運算符msb<<4+lsb(msb<<4)+lsbmsb<<(4+lsb)
逗號運算符在所有運算符中優先級最低i=1,2i=(1,2)(i=1),2

結合性只用于表達式中出現兩個以上相同優先級的操作符的情況,用于消除歧義。事實上,你會注意所有優先級相同的操作符,它們的結合性也相同。

?

6 返回局部對象的指針

?

char * localized_time(char* filename) {struct tm *tm_ptr;struct stat stat_block;char buffer[120];stat(filename,&stat_block);tm_ptr=localtime(&stat_block.st_mtime);strftime(buffer,sizeof(buffer,"%a %b %e %T %Y",tm_ptr);return buffer; }

問題就出在最后一行,也就是返回buffer的那行。buffer是一個自動分配內存的數組,是該函數的局部變量。當控制流離開聲明自動變量(即局部變量)的范圍時,自動變量就會失效。這就意味著即使返回一個指向局部變量的指針,當函數結束時,由于該變量已被銷毀,誰也不知道這個指針所指向的地址的內容是什么。

在C語言中,自動變量在堆棧中分配內存。當包含自動變量的函數或代碼塊退出時,它們所占用的內存便被回收,它們的內容肯定會被下一個調用的函數覆蓋。這一切取決于堆棧中先前的自動變量位于何處,活動函數聲明了什么變量,寫入了什么內容等。原先自動變量地址的內容可能被立即覆蓋,也可能稍后才被覆蓋。

?

解決這種問題有幾種方案:

1 返回一個指向字符串常量的指針。例如:

char* func() { return "Only works for simple strings";}

2 使用全局聲明的數組。例如:

char *fun() {

...

my_global_array[i] =?

...

return my_global_array;

}

這個適用于自己創建字符串的情況,也很簡單易用。它的缺點在于任何人都有可能在任何時候修改這個全局數組,而且該函數的下一次調用也會覆蓋該數組的內容。

3 使用靜態數組。例如:

char * func()

{

static char buffer[20];

...

return buffer;

}

這就可以防止任何人修改這個數組。只有擁有指向該數組的指針的函數才能修改這個靜態數組。但是,該函數的下一次調用將覆蓋這個數組的內容,所以調用者必須在此之前使用或備份數組的內容。和全局數組一樣,大型緩沖區如果閑置不用是非常浪費內存空間的。

4 顯式分配一些內存,保存返回的值。例如:

char* func(){

char *s=malloc(120);

...

return s;

}

這個方法具有靜態數組的優點,而且在每次調用時都創建一個新的緩沖區,所以該函數以后的調用不會覆蓋以前的返回值。它適用于多線程的代碼。它的缺點在于程序員必須承擔內存管理的責任。根據程序的復雜程度,這項任務可能很容易,也可能很復雜。如果內存尚在使用就釋放或者出現“內存泄露”(不再使用的內存未回收),就會產生令人難以置信的Bug。

5最好使用的解決方案就是要求調用者分配內存來保存函數的返回值。為了提高安全調用者應該同時指定緩沖區的大小。

void func(char* result,int size){

...

strncpy(result,"That't be in the data segment,Bob",size);

}

buffer=malloc(size);

func(buffer,size);

...

free(buffer);

如果程序員可以在同一代碼塊中同時進行“malloc”和“free”操作,內存管理是最為輕松的。這個解決方案就可以實現這一點。

?

7 聲明是如何形成的

不合法的聲明:

  • 函數的返回值不能是一個函數,所以像foo()()這樣是非法的。
  • 函數的返回值不能是一個數組,所以像foo()[]這樣是非法的。
  • 數組里面不能有函數,所以像foo[]()這樣是非法的。

合法的聲明:

  • 函數的返回值允許是一個函數指針,如: int(*fun())();
  • 函數的返回值允許是一個指向數組的指針,如: int(*foo())[];
  • 數組里面允許有函數指針,如int(*foo[])()
  • 數組里面允許有其他數組,所以你經常看到int foo[][]

8 typedef的使用

typedef與define的區別:

首先,可以用其他類型說明符對宏類型名進行擴展,但對typedef所定義的類型名不能這樣做。如下所示:

#define peach int?

unsigned peach i; //沒問題

typedef int banana;

unsigned banana i; ?//錯誤! 非法

其次,在連續幾個變量的聲明中,用typedef定義的類型能夠保證聲明中所有的變量均為同一種類型,而用#define定義的類型則無法保證。如下所示:

#define int_ptr int *;

int_ptr chalk,cheese;

經過宏擴展,第二行變為:

int * chalk,cheese;

這使得chalk和cheese成為不同的類型:chalk是一個指向int的指針,而cheese則是一個int。相反,下面的代碼中:

typedef char* char_ptr;

char_ptr Bentley,Rolls_Royce;

Bentley和Rolls_Royce的類型依然相同。雖然前面的類型名變了,但它們的類型相同,都是指向char的指針。

?

//下面兩個聲明具有相似的形式 typedef struct fruit{ int weight, price_per_lb; } fruit; //語句1 struct veg{ int weight,price_per_lb; } veg; //語句2

但它們代表的意思卻完全不一樣,語句1聲明了結構表情“fruit”和由“typedef聲明的結構類型”fruit“,其實際效果如下:

struct fruit mandarin; ? //使用結構標簽fruit

fruit mandarin; ? //使用結構類型fruit

語句2聲明了結構標簽veg和變量veg,只有結構標簽能夠在以后的聲明中使用,如

struct veg potato;

如果試圖使用veg ?cabbage這樣的聲明,將是一個錯誤。這有點類似下面的寫法:

int i;

i j;

?

9 聲明和定義的區別

記住,C語言的對象必須有且只有一個定義,但它可以有多個extern聲明。

定義是一種特殊的聲明,它創建了一個對象;聲明簡單地說明了在其他地方創建的對象的名字,它允許你使用這個名字。

?

定義 ? 只能出現在一個地方 ? ? ? ? 確定對象的類型并分配內存,用于創建的對象。例如,int my_array[100];

聲明 ? 可以多次出現 ??     ? 描述對象的類型,用于指代其他地方定義的對象(例如在其他文件里)例:extern int my_array[];

?

區分定義和聲明

只要記住下面的內容即可分清定義和聲明:

聲明相當于普通的聲明:它所聲明的并非自身,而是描述其他地方的創建的對象。

定義相當于特殊的聲明:它為對象分配內存。

?

extern對象聲明告訴編譯器對象的類型和名字,對象的內存分配則在別處進行。由于并未在聲明中為數組分配內存,所以并不需要提供關于數組長度的信息。對于多維數組,需要提供除最左邊一維之外其他維的長度——這就給編譯器足夠的信息產生相應的代碼。

?

10 數組和指針的訪問

C語言引入了”可修改的左值“這個術語,它表示左值允許出現在賦值語句的左邊,這個奇怪的術語是為與數組名區分,數組名也用于確定對象在內存中的位置,也是左值,但它不能作為賦值的對象。因此,數組名是個左值但不是可修改的左值。

?

左值:出現在賦值符左邊的符號有時被稱為左值。

右值:出現在賦值符右邊的符號有時則被稱為右值。

編譯器為每個變量分配一個地址(左值)。這個地址在編譯時可知,而且該變量在運行時一直保存于這個地址。相反,存儲于變量中的值(它的右值)只有在運行時才可知。如果需要用到變量中存儲的值,編譯器就發出指令從指定的地址讀入變量值并將它存于寄存器中。

這里的關鍵之處在于每個符號的地址在編譯時可知。所以,如果編譯器需要一個地址(可能還需要加上偏移量)來執行某種操作,它就可以直接進行操作,并不需要增加指令首先取得具體的地址。相反,對于指針,必須首先在運行時取得它的當前值,然后才能對它進行解除引用的操作(作為以后進行查找的步驟之一)。

相反,如果聲明extern char *p,它將告訴編譯器p是一個指針,它指向的對象是一個字符。為了取得這個字符,必須得到地址p的內容,把它作為字符的地址并從這個地址中取得這個字符。指針的訪問要靈活很多,但需要增加一個額外的提取,如圖所示:

11 數組和指針的其他區別

比較數組和指針的另外一個方法就是對比兩者的特點。

數組和指針的區別

指針? ? ? ? ?數組

保存數據的地址

保存數據 ? ? ? ? ? ? ??

間接訪問數據,首先取得指針的內容,把它作為地址,然后從這個地址提取數據。

如果指針有一個下標[I],就把指針的內容加上I作為地址,從中提取數據

?直接訪問,a[I]只是簡單地從a+I為地址取得數據

通常用于動態數據結構通常用于存儲固定數目且數據類型相同的元素
相關的函數為malloc() free()?隱式分配和刪除
通常指向匿名數據自身即為數據名

定義指針時,編譯器并不為指針所指向的對象分配空間,它只是分配指針本身的空間,除非在定義時賦給指針一個字符串常量進行初始化。例如,下面的定義創建了一個字符串常量(為其分配了內存):

char *p="breadfruit";

注意只有對字符串常量才是如此。不能指望為浮點數之類的常量分配空間,如:

float *pip=3.141; ?//錯誤

初始化指針時所創建的字符串常量被定義為只讀的。如果試圖通過指針修改這個字符串的值,程序就會出現未定義的行為。

?

數組也可以用字符串常量進行初始化:

char a[]="gooseberry";

與指針相反,由字符串常量初始化的數組是可以修改的。

?

?12 UNIX中的堆棧段和MS-DOS中的堆棧段

在UNIX中,當進程需要更多空間時,堆棧會自動生長。程序員可以想象堆棧是無限大的。這是UNIX勝過其他操作系統如MS-DOS的許多優勢之一。在UNIX的實現中一般使用某種形式的虛擬內存。當試圖訪問當前系統分配給堆棧的空間之外時,它將產生一個硬件中斷,稱為頁錯誤。處理頁錯誤的方法有好幾種,取決于對頁的引用是否有效。

在正常情況下,內核通過向違規的進程發送合適的信號(可能是段錯誤)來處理對地址的引用。在堆棧頂部的下端有一個稱為red zone的小型區域,如果對這個區域進行引用,不會產生失敗。相反,操作系統通過一個好的內存塊來增加堆棧段的大小。

在DOS中,在建立可執行文件時,堆棧的大小必須同時確定,而且它不能在運行時增加,如果你猜測錯誤,需要的堆棧空間大于所分配的空間,那么你和程序都會迷失。如果設置了檢查選項,就會收到STACK OVERFLOW(堆棧溢出)消息。

?

13 返回局部指針的兩種不同情況。

?先看下面的程序:

#include<iostream> using namespace std;char* test2() {char p[] = "hello world";return p; } char* test3(){char *p = "hello world";return p; } int main() {test2();test3(); }

其中,test2中p是一個數組,分配在棧空間,在棧空間存放字符串。當是數組p,則函數會將字符串常量的字符逐個復制到p數組里面,返回p則是返回數組p,但是調用函數結束后p被銷毀,里面的元素不存在了。

但是在test3中p是一個指針,則p指向存放字符串常量的地址,返回p則是返回字符串常量地址值,調用函數結束字符串常量不會消失(是常量)。所以返回常量的地址不會出錯。

雖然都是返回局部變量的指針,但是test2結果會有問題,但是test3不會。

總結

以上是生活随笔為你收集整理的C专家编程 总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。