目錄
位運算與邏輯運算讀程序練習
在switchcase 語句中能否使用continue關鍵字?為什么?
為什么盡量不使用goto語句?
void
i++與++i
i++和++i 哪個效率更高?
良好的條件比較語句風格
memcpy
memset
位運算與邏輯運算讀程序練習
int x = 3, y, z;
y = z = 2;
12 x = (y & z); printf("x = %d\n", x);
13 x = (y && z); printf("x = %d\n", x);
y = 4;
16 x = (y | z); printf("x = %d\n", x);
17 x = (y || z); printf("x = %d\n", x);
程序執行至第12 行時,y和z 的值都為2。此行把y和z 做按位與(&)運算的結果賦給變量x。y 和z 的二進制都是10,因此y & z 的結果為二進制10。因此x的值為2。
程序執行至第13 行時,y和z 的值都為2。此行把y和z 做邏輯與(&&)運算的結果賦給變量x。此時y和z 的值都不是0,因此y && z 的結果為1。因此x 的值為1。
程序執行至第16 行時,y的值為4,z 的值為2。此行把y和z 做按位或(|)運算的結果賦給變量x。此時y和z的二進制表示分別為100和010,因此y|z的結果為110。因此x的值為110,十進制表示為6。
程序執行至第17 行時,y 的值為4,z 的值為2。此行把y 和z 做邏輯或(||)運算的結果賦給變量x。此時y和z的值都不是0,因此y||z的結果為1。因此x的值為1。
在switchcase 語句中能否使用continue關鍵字?為什么?
switch-case
語句中通常不能直接使用continue
關鍵字。continue
語句通常用于循環語句(如for
或while
循環),用于跳過當前迭代并繼續下一次循環迭代。?
如果switch語句位于某個循環內部,那么continue將會作用于那個外層循環,跳過循環體的剩余部分并進入下一次迭代。如果switch不在循環內部,則continue關鍵字在技術上雖不產生語法錯誤,但也沒有實際用途,因為沒有迭代可跳過。
為什么盡量不使用goto語句?
1. 可讀性降低:goto語句使得程序的控制流變得難以追蹤,閱讀者需要跟隨goto跳轉來理解程序的執行順序,這增加了理解難度,尤其是當程序規模增大時。
2. 結構混亂:goto破壞了程序的結構化編程原則,如順序、分支(if-else、switch)、循環(for、while、do-while)等,這些結構提供了清晰的邏輯控制流控制方式。過度依賴goto可能導致“意大利面條代碼”,難以維護。
3. 維護困難:goto使得修改和維護代碼變得復雜,因為修改一處可能需要考慮對跳躍目標的影響,這可能導致連鎖反應式的修改,增加了出錯風險。
4. 調試挑戰:在調試時,goto使得堆棧跟蹤和理解程序的執行路徑復雜化,調試工具可能也難以準確展示程序流程。
5. 團隊協作:在團隊開發環境中,使用goto可能讓其他成員難以理解代碼意圖,增加協作難度,不利于代碼評審和交接。
6. 現代替代方案:現代編程語言和實踐提供了豐富的控制結構、函數、類、異常處理機制、迭代器等,能夠有效替代goto達到相同目的,同時保持代碼清晰和模塊化。
在某些特定場合(如低級別編程、操作系統內核、嵌入式系統或某些優化特定算法)goto可能有其正當使用場景,但總體上,遵循結構化編程原則,限制或避免goto使用是提升代碼質量和可維護性的共識。
void
如果指針p1和p2的類型相同,那么我們可以直接在p1和p2間互相賦值; 如果p1和p2指向不同的數據類型,則必須使用強制類型轉換運算符把賦值運算符右邊的 指針類型轉換為左邊指針的類型。
例如:
float *p1;
int *p2;
p1 = p2;
其中p1=p2語句會編譯出錯,提示“'=' :cannotconvertfrom'int*' to 'float*'”,必須改為: p1 = (float *)p2;
而void*則不同,任何類型的指針都可以直接賦值給它,無需進行強制類型轉換:
void *p1;
int *p2;
p1 = p2;
但如果
void *p1;
int *p2;
p2 = p1;
就會提示“'=' : cannotconvertfrom 'void *' to 'int *'”。
必須寫成? p2 = (int *)p1
i++與++i
#include <stdio.h>
int main(void){int i=8;printf("%d\n",++i);printf("%d\n",--i);printf("%d\n",i++);printf("%d\n",i--);printf("%d\n",-i++);printf("%d\n",-i--);printf("------\n");return 0;}
求輸出內容。
程序的說明如下:
程序第7 行,此時i 的值為8。這里先i 自增1,再打印i 的值。因此輸出9,并且i的值也變為9。
程序第8 行,此時i 的值為9。這里先i 自減1,再打印i 的值。因此輸出8,并且i的值也變為8。
程序第9 行,此時i 的值為8。這里先打印i 的值,再i 自增1。因此輸出8,并且i的值也變為9。
程序第10 行,此時i 的值為9。這里先打印i 的值,再i 自減1。因此輸出9,并且i的值也變為8。
程序第11 行,此時i 的值為8。這里的“-”表示負號運算符。因此先打印-i 的值,再i自增1。因此輸出-8,并且i的值也變為9。
程序第12 行,此時i 的值為9。這里的第一個“-”表示負號運算符,后面連在一起的兩個“-”表示自減運算符。因此先打印-i的值,再i自減1。因此輸出-9,并且i的值也變為8。
i++和++i 哪個效率更高?
內建數據類型的情況,效率沒有區別。
自定義數據類型的情況,++i效率較高。
i++(后置自增)和++i(前置自增)在大多數現代編譯譯器下對于基本數據類型(如int、float、double等)來說,性能上幾乎沒有區別。這兩種操作最終生成的匯編譯代碼很可能是相同的,尤其是在優化級別較高的編譯譯條件下。
在C++中:
?后置自增i++會先返回i的當前值,然后將i的值加1。
?前置自增++i會先將i的值加1,然后返回加1之后的值。理論上講,如果值不需要被保留,使用++i可能略微高效,因為它直接計算并返回新值,而i++可能涉及一個臨時變量來存儲舊值。但在實際應用中,這種差異微乎其微,幾乎不影響程序的整體性能,特別是在考慮現代處理器的速度和編譯器優化技術。
在具體應用時,選擇i++還是++i更多基于語境和語義的清晰性,而非性能考慮。
如果增量操作的結果是賦值操作的一部分,使用++i更自然;
如果需要先使用原值后遞增,則用i++。
良好的條件比較語句風格
假設浮點變量的名字為 x,它與 0.0的比較如下。
第一種:
1 if (x == 0.0)
2 if (x != 0.0)
第二種:
1 if ((x >= -EPSINON) && (X <= EPSINON))
2 if ((x < -EPSINON) || (X > EPSINON))
其中,EPSINON是允許的誤差(精度)。
第二種風格較良好。
注意:無論是float 還是double 類型的變量,都有精度限制。所以一定要避免將浮點變量用“==”或“!=”與數字比較,應該設法轉化成“>=”或“<=”形式。
假設布爾變量名字為flag,它與零值比較的標準if語句如下。
第一種:
1 if (flag == TRUE)
2 if (flag == FALSE)
第二種:
1 if (flag)
2 if (!flag)
第二種風格較良好。根據布爾類型的語義,零值為“假”(記為 FALSE),任何非零值都是“真”(記為TRUE)。TRUE的值究竟是什么并沒有統一的標準。例如Visual C++將TRUE定義為1,而Visual Basic 則將TRUE 定義為-1。因此不可將布爾變量直接與TRUE、FALSE進行比較。
假設整型變量的名字為value,它與零值比較的標準if語句如下。
第一種:
1 if (value == 0)
2 if (value != 0)
第二種:
1 if (value)
2 if (!value)
第一種風格較良好,第二種風格會讓人誤解value 是布爾變量,應該將整型變量用“==”或“!=”直接與0比較。
memcpy
void * memcpy(void *dest,constvoid *src,size_t len);
用于將一塊內存區域的數據從源地址復制到目的地址
每個參數的含義如下:
?void *dest:指向復制內容將要存放的目標數組的指針。目標區域必須有足夠的空間來容納復制的內容。
?const void *src:指向要復制內容的源數組的指針。
?size_t len:從源到目標復制的字節數。
#include <stdio.h>
#include <string.h>int main() {char src[50] = "Hello, World!";char dest[50]; // 目標量足夠大以容納src的內容// 使用memcpy函數復制src數組的內容到destmemcpy(dest, src, src, strlen(src) + 1); // 注意加1是因為要復制字符串末尾部的'\0''printf("Original String: %s\n", src);printf("Copied String: %s\n", dest);return 0;
}
在長度上加1是為了確保字符串的終止符也被復制,因為strlen不計算字符串的終止符'\0'字符。
輸出結果
Original String: Hello, World!
Copied String: Hello, World!
memset
void *memset(void *buffer, int c, size_t num, size_t size);
memset函數會將從buffer指向的內存地址開始的連續size個字節(字節)全部設置為c指定的值。這個函數常用于清零內存塊(當c設為0時),或者快速填充固定值。
#include <stdio.h>
#include <string.h>int main() {int arr[10];memset(arr, 0, sizeof(arr)); // 將整個arr數組清零printf("Array after memset: %d\n", arr[0]); // 輸出應為0,因為所有元素都被清零了return 0;
}