我從零開始學習C語言(15)- 基本類型 PART2

開始學習第七章其余部分。


7.3.4 轉義序列

正如在前面示例中見到的那樣,字符常量通常是用單引號括起來的單個字符。然而,一些特殊符號(比如換行符)是無法采用上述方式書寫的,因為它們不可見(非打印字符),或者無法從鍵盤輸入。因此,為了使程序可以處理字符集中的每一個字符,C語言提供了一種特殊的表示法——轉義序列。

escape翻譯成“轉義”,而計算機領域的"escape"特指符號系統的規則轉換

轉義序列共有兩種:字符轉義序列 數字轉義序列 。在3.1節已經見過了一部分字符轉義序列,下表給出了完整的字符轉義序列集合。

字符轉義序列

名稱轉義序列
警報(響鈴)符\a
回退符\b
換頁符\f
換行符\n
回車符\r
水平制表符\t
垂直制表符\v
反斜杠????????\\
問號????????\?
單引號\'
雙引號\"

轉義序列\a 、\b 、\f 、\r 、\t 和\v 表示常用的ASCII控制字符, 轉義序列\n 表示ASCII碼的回行符,轉義序列\\ 允許字符常量或字符串包含字符\ ,轉義序列\' 允許字符常量包含字符' ,而轉義序列\" 則允許字符串包含字符" , 轉義序列\? 很少使用。

字符轉義序列使用起來很容易,但是它們有一個問題:轉義序列列表沒有包含所有無法打印的ASCII字符,只包含了最常用的字符。字符轉義序列也無法用于表示基本的128個ASCII字符以外的字符。數字轉義序列可以表示任何字符,所以它可以解決上述問題。

為了把特殊字符書寫成數字轉義序列,首先需要在類似附錄E那樣的表中查找字符的八進制或十六進制值。例如,某個ASCII碼轉義字符(十進制值為27)對應的八進制值為33,對應的十六進制值為1B。上述八進制或十六進制碼可以用來書寫轉義序列。

八進制轉義序列 由字符\ 和跟隨其后的一個最多含有三位數字的八進制數組成

(此數必須表示為無符號字符,所以最大值通常是八進制的377。)例如,可以將轉義字符寫成\33 或\033。跟八進制常量不同,轉義序列中的八進制數不一定要用0開頭。

十六進制轉義序列 由\x 和跟隨其后的一個十六進制數組成

雖然標準C對于十六進制數的位數沒有限制,但其必須表示成無符號字符(因此,如果字符長度是8位,那么十六進制數的值不能超過FF )。若采用這種表示法,可以把轉義字符寫成\x1b 或\x1B的形式。字符x 必須小寫,但是十六進制的數字(例如b )不限大小寫。

作為字符常量使用時,轉義序列必須用一對單引號括起來。例如,表示轉義字符的常量可以寫成'\33' (或'\x1b' )的形式。轉義序列可能有點隱晦,所以采用#define 的方式給它們命名通常是個不錯的主意:

#define ESC '\33' /* ASCII escape character */

正如3.1節看到的那樣,轉義序列也可以嵌在字符串中使用。

轉義序列不是唯一一種用于表示字符的特殊表示法。三字符序列提供了一種表示字符# 、[ 、\ 、] 、^ 、{ 、| 、}和~ 的方法,這些字符在一些國家的鍵盤上是打不出來的。 C99增加了通用字符名。通用字符名跟轉義序列相似,不同之處在于通用字符名可以用在標識符中。

7.3.5 字符處理函數

前面已經講過如何使用if 語句把小寫字母轉換成大寫字母:

if ('a' <= ch && ch <= 'z')ch = ch -'a' + 'A';

但是這不是最好的方法。一種更快捷且更易于移植的轉換方法是調用C語言的toupper 庫函數:

ch = toupper(ch); /* converts ch to upper case */

toupper 函數在被調用時檢測參數(本例中為ch )是否是小寫字母。如果是,它會把參數轉換成相應的大寫字母;否則, toupper函數會返回參數的值。上面的例子采用賦值運算符把toupper 函數返回的值存儲在變量ch 中。當然也可以同樣簡單地進行其他的處理,比如存儲到另一個變量中,或用if 語句進行測試:

if (toupper(ch) == 'A') ...

調用toupper 函數的程序需要在頂部放置下面這條#include 指令:

#include <ctype.h>

toupper 函數不是在C函數庫中唯一實用的字符處理函數。后續會描述全部字符處理函數,并且給出使用示例。

7.3.6 用scanf 和printf 讀/寫字符

轉換說明%c 允許scanf 函數和printf 函數對單個字符進行讀/寫操作:

char ch;
scanf("%c", &ch); /* reads a single character */
printf("%c", ch); /* writes a single character */

在讀入字符前,scanf 函數不會跳過空白字符。如果下一個未讀字符是空格,那么在前面的例子中,scanf 函數返回后變量ch 將包含一個空格。為了強制scanf 函數在讀入字符前跳過空白字符,需要在格式串中的轉換說明%c 前面加上一個空格:

scanf(" %c", &ch); /* skips white space, then reads ch */

回顧3.2節的內容,scanf 格式串中的空白意味著“跳過零個或多個空白字符”。因為通常情況下scanf 函數不會跳過空白,所以它很容易檢查到輸入行的結尾:檢查剛讀入的字符是否為換行符。例如,下面的循環將讀入并且忽略掉當前輸入行中剩下的所有字符:

do {scanf("%c", &ch);
} while (ch != '\n');

下次調用scanf 函數時,將讀入下一輸入行中的第一個字符。

7.3.7 用getchar 和putchar 讀/寫字符

C語言還提供了另外一些讀/寫單個字符的方法。特別是, 可以使用getchar 函數和putchar 函數來取代scanf 函數和printf 函數。putchar 函數用于寫單個字符:

putchar(ch);


每次調用getchar 函數時,它會讀入一個字符并將其返回。為了保存這個字符,必須使用賦值操作將其存儲到變量中:

ch = getchar(); /* reads a character and stores it in ch */

事實上,getchar 函數返回的是一個int 類型的值而不是char 類型的值(原因將在后續章節中討論)。因此,如果一個變量用于存儲getchar 函數讀取的字符,其類型設置為int 而不是char 也沒啥好奇怪的。和scanf 函數一樣,getchar 函數也不會在讀取時跳過空白字符

執行程序時,使用getchar 函數和putchar 函數(勝于scanf 函數和printf 函數)可以節約時間。getchar 函數和putchar 函數執行速度快有兩個原因。第一個原因是,這兩個函數比scanf 函數和
printf 函數簡單得多,因為scanf 函數和printf 函數是設計用來按不同的格式讀/寫多種不同類型數據的。第二個原因是,為了額外的速度提升,通常getchar 函數和putchar 函數是作為宏來實現的

getchar 函數還有一個優于scanf 函數的地方:因為返回的是讀入的字符,所以getchar 函數可以應用在多種不同的C語言慣用法中,包括用在搜索字符或跳過所有出現的同一字符的循環中。思考下面這個scanf 函數循環,前面我們曾用它來跳過輸入行的剩余部分:

do {scanf("%c", &ch);
} while (ch != '\n');

用getchar 函數重寫上述循環,得到下面的代碼:

do {ch = getchar();
} while (ch != '\n');

把getchar 函數調用移到控制表達式中可以精簡循環

while ((ch = getchar()) != '\n');

這個循環讀入一個字符,把它存儲在變量ch 中,然后測試變量ch 是否不是換行符。如果測試結果為真,那么執行循環體(循環體實際為空),接著再次測試循環條件,從而引發讀入新的字符。實際上我們并不需要變量ch ,可以把getchar 函數的返回值與換行符進行比較:

[慣用法]

while (getchar() != '\n'); /* skips rest of line */

這個循環是非常著名的C語言慣用法,雖然這種用法的含義是十分隱晦的,但是值得學習

getchar 函數對于搜索字符的循環和跳過字符的循環都很有用。思考下面這個利用getchar 函數跳過不定數量的空格字符的語句

[慣用法]

while ((ch = getchar()) == ' '); /* skips blanks */

當循環終止時,變量ch 將包含getchar 函數遇到的第一個非空白字符。

注意:如果在同一個程序中混合使用getchar 函數和scanf 函數,請一定要注意。scanf 函數傾向于遺留下它“掃視”過但未讀取的字符(包括換行符)。思考一下,如果試圖先讀入數再讀入字符的話,下面的程序段會發生什么:

printf("Enter an integer: ");
scanf("%d", &i);
printf("Enter a command: ");
command = getchar();

在讀入i 的同時,scanf 函數調用將會留下沒有消耗掉的任意字符,包括(但不限于)換行符。getchar 函數隨后將取回第一個剩余字符但這不是我們所希望的結果

程序014 確定消息的長度

為了說明字符的讀取方式,下面編寫一個程序來計算消息的長度。在用戶輸入消息后,程序顯示長度:

Enter a message: Brevity is the soul of wit.
Your message was 27 character(s) long.

消息的長度包括空格和標點符號,但是不包含消息結尾的換行符。程序需要采用循環結構來實現讀入字符和計數器自增操作,循環在遇到換行符時立刻終止。我們既可以采用scanf 函數也可以采用getchar 函數讀取字符,但大多數C程序員愿意采用getchar 函數。采用簡明的while 循環書寫的程序如下:

length.c

/* Determines the length of a message */
#include <stdio.h>
int main(void)
{char ch;int len = 0;printf("Enter a message: ");ch = getchar();while (ch != '\n') {len++;ch = getchar();}printf("Your message was %d character(s) long.\n", len);return 0;
}

回顧有關while 循環和getchar 函數慣用法的討論,我們發現程序可以縮短成如下形式:

length2.c

/* Determines the length of a message */
#include <stdio.h>
int main(void)
{int len = 0;printf("Enter a message: ");while (getchar() != '\n')len++;printf("Your message was %d character(s) long.\n", len);return 0;
}

7.4 類型轉換

在執行算術運算時,計算機比C語言的限制更多。為了讓計算機執行算術運算,通常要求操作數有相同的大小(即位的數量相同),并且要求存儲的方式也相同。計算機可能可以直接將兩個16位整數相加,但是不能直接將16位整數和32位整數相加,也不能直接將32位整數和32位浮點數相加。

C語言則允許在表達式中混合使用基本類型。在單個表達式中可以組合整數、浮點數,甚至是字符。當然,在這種情況下C編譯器可能需要生成一些指令將某些操作數轉換成不同類型,使得硬件可以對表達式進行計算。例如,如果對16位short 型數和32位int 型數進行加法操作,那么編譯器將安排把16位short 型值轉換成32位值。如果是int型數據和float 型數據進行加法操作,那么編譯器將安排把int 型值轉換成為float 格式。這個轉換過程稍微復雜一些,因為int 型值和float 型值的存儲方式不同。

因為編譯器可以自動處理這些轉換而無需程序員介入,所以這類轉換稱為隱式轉換 。C語言還允許程序員使用強制運算符執行顯式轉換 。先討論隱式轉換,顯式轉換推遲到本節的最后進行。遺憾的是,執行隱式轉換的規則有些復雜,主要是因為C語言有大量不同的算術類型。

當發生下列情況時會進行隱式轉換。當算術表達式或邏輯表達式中操作數的類型不相同時。(C語言執行所謂的常用算術轉換 。)
① 當賦值運算符右側表達式的類型和左側變量的類型不匹配時。

② 當函數調用中的實參類型與其對應的形參類型不匹配時。

③ 當return 語句中表達式的類型和函數返回值的類型不匹配時。

這里將討論前兩種情況,其他情況將留到第9章進行介紹。

7.4.1?常用算術轉換

常用算術轉換可用于大多數二元運算符(包括算術運算符、關系運算符和判等運算符)的操作數。例如,假設變量f 為float 類型,而變量i 為int 類型。

常用算術轉換將會應用在表達式f + i 的操作數上,因為兩者的類型不同。顯然把變量i 轉換成float 類型(匹配變量f 的類型)比把變量f 轉換成int 類型(匹配變量i 的類型)更安全。

整數始終可以轉換成為float 類型;可能會發生的最糟糕的事是精度會有少量損失。相反,把浮點數轉換成為int 類型,將有小數部分的損失;更糟糕的是,如果原始數大于最大可能的整數或者小于最小的整數,那么將會得到一個完全沒有意義的結果。


常用算術轉換的策略是把操作數轉換成可以安全地適用于兩個數值的“最狹小的”數據類型

(粗略地說,若某種類型要求的存儲字節比另一種類型少,則這種類型就比另一種類型更狹小。)

為了統一操作數的類型,通常可以將相對較狹小類型的操作數轉換成另一個操作數的類型來實現(這就是所謂的提升 )。 最常用的提升是整值提升?,它把字符或短整數轉換成int 類型(或者某些情況下是unsigned int 類型)。

執行常用算術轉換的規則可以劃分成兩種情況。

任一操作數的類型是浮點類型的情況 。按照下圖將類型較狹小的操作數進行提升:

float -> double -> long double

也就是說,如果一個操作數的類型為long double ,那么把另一個操作數的類型轉換成long double 類型。否則,如果一個操作數的類型為double 類型,那么把另一個操作數轉化成double 類型。否則,如果一個操作數的類型是float 類型,那么把另一個操作數轉換成float 類型。

注意,這些規則涵蓋了混合整數和浮點類型的情況。例如,如果一個操作數的類型是long int 類型,并且另一個操作數的類型是double 類型,那么把long int 類型的操作數轉換成double 類型。

兩個操作數的類型都不是浮點類型的情況 。首先對兩個操作數進行整值提升(保證沒有一個操作數是字符類型或短整型)。然后按照下圖對類型較狹小的操作數進行提升:

int ->unsigned int -> long int -> unsigned long int


有一種特殊情況,只有在long int 類型和unsigned int 類型長度(比如32位)相同時才會發生。在這類情況下,如果一個操作數的類型是long int ,而另一個的類型是unsigned int,那么兩個操作數都會轉換成unsigned long int 類型

注意:

當把有符號操作數和無符號操作數組合時,把有符號操作數“轉換”成無符號的值。轉換過程中需要加上或者減去的倍數,其中是無符號類型能表示的最大值。這條規則可能會導致某些隱蔽的編程錯誤

假設int 類型的變量 i 的值為-10,而unsigned int 類型的變量u 的值為10。如果用 < 運算符比較變量 i 和變量 u?,那么期望的結果應該是1(真)。但是,在比較前,變量 i 轉換成為unsigned int 類型。因為負數不能被表示成無符號整數,所以轉換后的值將不再為-10,而是加上4 294 967 296的結果(假定4 294 967 295是最大的無符號整數),即4 294 967 286。因而i < u 比較的結果將為0。有些編譯器會在程序試圖比較有符號數與無符號數時給出一條類似“comparison between signedand unsigned”的警告消息。

由于此類陷阱的存在,所以最好盡量避免使用無符號整數,特別是不要把它和有符號整數混合使用

下面的例子顯示了常用算術轉換的實際執行情況:

char c;
short int s;
int i;
unsigned int u;
long int l;
unsigned long int ul;
float f;
double d;
long double ld;
i = i + c; /* c is converted to int */
i = i + s; /* s is converted to int */
u = u + i; /* i is converted to unsigned int */
l = l + u; /* u is converted to long int */
ul = ul + l; /* l is converted to unsigned long int */
f = f + ul; /* ul is converted to float */
d = d + f; /* f is converted to double */
ld = ld + d; /* d is converted to long double */

7.4.2 賦值過程中的轉換

常用算術轉換不適用于賦值運算。C語言會遵循另一條簡單的轉換規則,那就是把賦值運算右邊的表達式轉換成左邊變量的類型。如果變量的類型至少和表達式類型一樣“寬”,那么這種轉換將沒有任何障礙。例如:

char c;
int i;
float f;
double d;
i = c; /* c is converted to int */
f = i; /* i is converted to float */
d = f; /* f is converted to double */

其他情況下是有問題的。把浮點數賦值給整型變量會丟掉該數的小數部分:

int i;
i = 842.97; /* i is now 842 */
i = -842.97; /* i is now -842 */

此外,把某種類型的值賦給類型更狹小的變量時, 如果該值在變量類型范圍之外,那么將會得到無意義的結果(甚至更糟)。

c = 10000; /*** WRONG ***/
i = 1.0e20; /*** WRONG ***/
f = 1.0e100; /*** WRONG ***/

這類賦值可能會導致編譯器或lint之類的工具發出警告。

如果浮點常量被賦值給float 型變量時,一個很好的方法是在浮點常量尾部加上后輟f ,我們從第2章開始就一直是這么做的:

f = 3.14159f;

如果沒有后輟,常量3.14159 將是double 類型,可能會引起警告消息。

7.4.3 C99中的隱式轉換

C99中的隱式轉換和C89中的隱式轉換略有不同,這主要是因為C99增加了一些類型(_Bool 、long long 類型、擴展的整數類型和復數類型)。

為了定義轉換規則,C99允許每個整數類型具有“整數轉換等級”。下面按從最高級到最低級的順序排列。

(1) long long int 、unsigned long long int
(2) long int 、unsigned long int
(3) int 、unsigned int
(4) short int 、unsigned short int
(5) char 、signed char 、unsigned char
(6) _Bool

簡單起見,這里忽略了擴展的整數類型和枚舉類型。

C99用整數提升取代了C89中的整值提升,可以將任何等級低于int 和unsigned int 的類型轉換為int (只要該類型的所有值都可以用int 類型表示)或unsigned int 。

與C89一樣,C99中執行常用算術轉換的規則可以劃分為兩種情況。

任一操作數的類型是浮點類型的情況 。

只要兩個操作數都不是復數型,規則與前面一樣。

兩個操作數的類型都不是浮點類型的情況 。

首先對兩個操作數進行整數提升。如果這時兩個操作數的類型相同,過程結束。否則,依次嘗試下面的規則,一旦遇到可應用的規則就不再考慮別的規則:
如果兩個操作數都是有符號型或者都是無符號型,將整數轉換等級較低的操作數轉換為等級較高的操作數的類型;

如果無符號操作數的等級高于或等于有符號操作數的等級,將有符號操作數轉換為無符號操作數的類型。

如果有符號操作數類型可以表示無符號操作數類型的所有值,將無符號操作數轉換為有符號操作數的類型。

否則,將兩個操作數都轉換為與有符號操作數的類型相對應的無符號類型。
另外,所有算術類型都可以轉換為_Bool 類型。如果原始值為0則轉換結果為0,否則結果為1。

7.4.4?強制類型轉換

雖然C語言的隱式轉換使用起來非常方便,但我們有些時候還需要從更大程度上控制類型轉換。基于這種原因,C語言提供了強制類型轉換。強制類型轉換表達式的格式如下:

[強制轉換表達式] ????????(類型名) 表達式

這里的?類型名 表示的是表達式應該轉換成的類型。

下面的例子顯示了使用強制類型轉換表達式計算float類型值小數部分的方法:

float f, frac_part;
frac_part = f - (int) f;

強制類型轉換表達式(int) f 表示把f 的值轉換成int 類型后的結果。C語言的常用算術轉換則要求在進行減法運算前把(int)f 轉換回float 類型。f 和(int)f 的不同之處就在于f 的小數部分,這部分在強制類型轉換時被丟掉了。

強制類型轉換表達式可以用于顯示那些肯定會發生的類型轉換:

i = (int) f; /* f is converted to int *

它也可以用來控制編譯器并且強制它進行我們需要的轉換。思考下面的例子:

float quotient;
int dividend, divisor;
quotient = dividend / divisor;

正如現在寫的那樣,除法的結果是一個整數,在把結果存儲在quotient 變量中之前,要把結果轉換成float 格式。但是,為了得到更精確的結果,可能需要在除法執行之前把dividend 和divisor 的類型轉換成float 格式的。強制類型轉換表達式可以完成這一點:

quotient = (float) dividend / divisor;

變量divisor 不需要進行強制類型轉換,因為把變量dividend 強制轉換成float 類型會迫使編譯器把divisor 也轉換成float 類型。
順便提一下,C語言把(類型名) 視為一元運算符。一元運算符的優先級高于二元運算符,所以編譯器會把表達式:

(float) dividend / divisor

解釋為

((float) dividend) / divisor

如果感覺有點混淆,那么注意還有其他方法可以實現同樣的效果:

quotient = dividend / (float) divisor;

或者

quotient = (float) dividend / (float) divisor;

有些時候,需要使用強制類型轉換來避免溢出。思考下面這個例子:

long i;
int j = 1000;
i = j * j; /* overflow may occur */

乍看之下,這條語句沒有問題。表達式j * j 的值是1 000 000,并且變量 i 是long int 類型的,所以i 應該能很容易地存儲這種大小的值,不是嗎?問題是,當兩個int 類型值相乘時,結果也應該是int 類型的,但是j * j 的結果太大,以致在某些機器上無法表示成int 型,從而導致溢出。幸運的是,可以使用強制類型轉換避免這種問題的發生:

i = (long) j * j;

因為強制運算符的優先級高于* ,所以第一個變量j 會被轉換成long int 類型,同時也迫使第二個j 進行轉換。注意,語句

i = (long) (j * j); /*** WRONG ***/

是不對的,因為溢出在強制類型轉換之前就已經發生了。

7.5 類型定義

5.2節中,我們使用#define 指令創建了一個宏,可以用來定義布爾型數據:

#define BOOL int

但是, 一個更好的設置布爾類型的方法是利用所謂的類型定義的特性:

typedef int Bool;

注意,所定義的類型的名字放在最后 。還要注意,我們使用首字母大寫的單詞Bool 。將類型名的首字母大寫不是必須的,只是一些C語言程序員的習慣。
采用typedef 定義Bool 會導致編譯器在它所識別的類型名列表中加入Bool

現在,Bool 類型可以和內置的類型名一樣用于變量聲明、強制類型轉換表達式和其他地方了。例如,可以使用Bool 聲明變量:

Bool flag; /* same as int flag; */

編譯器將會把Bool 類型看成是int 類型的同義詞;因此,變量flag 實際就是一個普通的int 類型變量。

7.5.1 類型定義的優點

類型定義使程序更加易于理解(假定程序員是仔細選擇了有意義的類型名)。例如,假設變量cash_in 和變量cash_out 將用于存儲美元數量。把Dollars 聲明成

typedef float Dollars;

并且隨后寫出

Dollars cash_in, cash_out;

這樣的寫法比下面的寫法更有實際意義:

float cash_in, cash_out;

類型定義還可以使程序更容易修改。如果稍后決定Dollars 實際應該定義為double 類型的,那么只需要改變類型定義就足夠了:

typedef double Dollars;

Dollars 變量的聲明不需要進行改變。如果不使用類型定義,則需要找到所有用于存儲美元數量的float 類型變量(這顯然不是件容易的工作)并且改變它們的聲明。

7.5.2 類型定義和可移植性

類型定義是編寫可移植程序的一種重要工具。程序從一臺計算機移動到另一臺計算機可能引發的問題之一就是不同計算機上的類型取值范圍可能不同。如果i 是int 類型的變量,那么賦值語句

i = 100000;

在使用32位整數的機器上是沒問題的,但是在使用16位整數的機器上就會出錯。

可移植性技巧 為了更大的可移植性,可以考慮使用typedef定義新的整數類型名

假設編寫的程序需要用變量來存儲產品數量, 取值范圍在0~50 000。為此可以使用long int 類型的變量(因為這樣保證可以存儲至少在2 147 483 647以內的數),但是用戶更愿意使用int 類型的變量,因為算術運算時int 類型值比long int 類型值運算速度快;同時,int 類型變量占用的空間較少。我們可以定義自己的“數量”類型,而避免使用int 類型聲明數量變量:

typedef int Quantity;

并且使用這種類型來聲明變量:

Quantity q;

當把程序轉到使用較短整數的機器上時,需要改變Quantity 的定義:

typedef long Quantity

可惜的是,這種技術無法解決所有的問題,因為Quantity 定義的變化可能會影響Quantity 類型變量的使用方式。我們至少需要改動使用了Quantity 類型變量的printf 函數調用和scanf 函數調用,用轉換說明%ld 替換%d 。

C語言庫自身使用typedef 為那些可能依據C語言實現的不同而不同的類型創建類型名;這些類型的名字經常以_t 結尾,比如ptrdiff_t 、size_t 和wchar_t 。這些類型的精確定義不盡相同,下面是一些常見的例子:

typedef long int ptrdiff_t;
typedef unsigned long int size_t;
typedef int wchar_t;

在C99中,<stdint.h> 頭文件使用typedef 定義占用特定位數的整數類型名。例如,int32_t 是恰好占用32位的有符號整型。這是一種有效的定義方式,能使程序更易于移植。

7.6?sizeof 運算符

sizeof 運算符允許程序存儲指定類型值所需空間的大小。表達式

[sizeof 表達式]???????? sizeof (類型名)

的值是一個無符號整數代表存儲屬于類型名的值所需要的字節數。表達式sizeof(char) 的值始終為1,但是對其他類型計算出的值可能會有所不同。在32位的機器上,表達式sizeof(int) 的值通常為4。注意,sizeof 運算符是一種特殊的運算符, 因為編譯器本身通常就能夠確定sizeof 表達式的值

通常情況下,sizeof 運算符也可以應用于常量、變量和表達式。如果i 和 j 是整型變量,那么sizeof(i) 在32位機器上的值為4,這和表達式sizeof(i+j) 的值一樣。跟應用于類型時不同,sizeof應用于表達式時不要求圓括號,我們可以用sizeof i 代替sizeof(i) 。但是,由于運算符優先級的問題,圓括號有時還是需要的編譯器會把表達式sizeof i + j 解釋為(sizeof i) + j,這是因為sizeof 作為一元運算符的優先級高于二元運算符+ 。為了避免出現此類問題,我們在sizeof 表達式中始終加上圓括號

顯示sizeof 值時要注意,因為sizeof 表達式的類型是size_t ,這是一種由實現定義的類型。在C89中,最好在顯示前把表達式的值轉換成一種已知的類型。size_t 一定是無符號整型,所以最安全的方法是把sizeof 表達式強制轉換成unsigned long 類型(C89中最大的無符號類型),然后使用轉換說明符%lu 顯示

printf("Size of int: %lu\n", (unsigned long) sizeof(int));

C99中,size_t 類型可以比 unsigned long 更長。但C99中的printf 可以直接顯示出size_t 類型值而不需要強制轉換。方法是在轉換說明中的一般整數(通常用u )代碼前使用字母z :

printf("Size of int: %zu\n", sizeof(int)); /* C99 only */

問答

1、浮點常量為什么存儲成double 格式而不是float 格式?

答:由于歷史的原因,C語言更傾向于使用double 類型,float 類型則被看成是“二等公民”。思考Kernighan和Ritchie的The CProgramming Language 一書中關于float 的論述:“使用float 類型的主要原因是節省大型數組的存儲空間,或者有時是為了節省時間,因為在一些機器上雙精度計算的開銷格外大。”經典C要求所有浮點計算都采用雙精度的格式。(C89和C99沒有這樣的要求。)

這讓我聯系到,現在AI大模型精度用的就是浮點類型fp8、fp16等,用它的原因之一就是能大幅節省存儲空間。

2、為什么C語言要提供類型定義呢?定義一個BOOL 宏不是和用typedef 定義一個Bool 類型一樣好用嗎?

答:類型定義和宏定義存在兩個重要的不同點。首先,類型定義比宏定義功能更強大。特別是,數組和指針類型是不能定義為宏的
假設我們試圖使用宏來定義一個“指向整數的指針”類型:
#define PTR_TO_INT int *
聲明
PTR_TO_INT p, q, r;
在處理以后將會變成
int * p, q, r;
可惜的是,只有p 是指針,q 和r 都成了普通的整型變量。類型定義不會有這樣的問題。

其次,typedef 命名的對象具有和變量相同的作用域規則;定義在函數體內的typedef 名字在函數外是無法識別的。另一方面,宏的名字在預處理時會在任何出現的地方被替換掉。

練習題

7.1節

1. 給出下列整數常量的十進制值。
(a) 077
(b) 0x77
(c) 0XABC

(a)這是八進制數,按權值展開即可:

077=7\times 8^{1}+7\times 8^{0}=56+7=63

(b)這是十六進制數,按權值展開即可:

0\times 77=7\times16^1 + 7\times16^0=112+7=119

(c)這是十六進制數,按權值展開即可:

0\times ABC=A\times16^2 + B\times16^1 +C\times16^0= 10\times16^2 + 11\times16^1 +12\times16^0=2560+176+12=2748

7.2節

2. 下列哪些常量在C語言中不是合法的?區分每一個合法的常量是整數還是浮點數。

(a) 010E2
(b) 32.1E+5
(c) 0790
(d) 100_000
(e) 3.978e-2

(a)推測:八進制表示,后面是十進制指數,可能不合法。

驗證:輸出了1000,推測錯誤。

修正:因為后面E明確為科學計數法,故010被認為是10進制浮點數,會被識別成double類型的浮點數。

(b) 32.1E+5,合法。E后面階碼為+5,為double類型浮點數。

驗證:輸出3210000.000000,推測正確。

(c) 0790,不合法。8進制數,這里9超過了基數范圍。

驗證:?error: invalid digit "9" in octal constant<stdin>

(d)100_000,合法,整型變量,但要看編譯器支持情況(C11之后支持)。

C99之前會報錯:[Error] invalid suffix "_000" on integer constant

(e) 正確,e科學計數法,階碼為-2。被識別成double類型的浮點數。

3、下列哪些類型在C語言中不是合法的?

(a) short unsigned int
(b) short float
(c) long double
(d) unsigned long

(a)合法, 無符號短整型。

(b)不合法,不可以類型不能定義為短整型又定義為單精度浮點型。

[Error] both 'short' and 'float' in declaration specifiers

(c)合法,c99的長浮點型

(d)合法,無符號長整型

7.3節

4. 如果變量c是char 類型,那么下列哪條語句是非法的?

(a) i += c; /* i has type int */
(b) c = 2 * c - 1;
(c) putchar(c);
(d) printf(c);

(a) 合法

(b)合法

(c)合法

(d)不合法,需要格式說明符。

5. 下列哪條不是書寫數65的合法方式?(假設字符集是ASCII。)

(a) 'A'
(b) 0b1000001 = 65
(c) 0101 = 65
(d) 0x41 = 65

#include <stdio.h>int main(void)
{printf("%d\n", 'A');printf("%d\n", 0b1000001);printf("%d\n", 0101);printf("%d\n", 0x41);return 0;
}

都能打印65

6. 對于下面的數據項,指明char 、short 、int 、long 類型中哪種類型是足以存儲數據的最小類型。
(a) 一個月的天數
(b) 一年的天數
(c) 一天的分鐘數
(d) 一天的秒數

(a)1-31 => char

(b) 366 => short

(c) 1440 => short

(d) 86400 => int/long

7.4節

9. 假設變量i 和變量j 都是int 類型,那么表達式i / j +'a' 是什么類型?

(i / j) => int類型

(i / j) + 'a','a'由char類型提升到int類型,所以最后為int類型。

10.假設變量i 是int 類型,變量j 是long int 類型,并且變量k 是unsigned int 類型,那么表達式i + (int)j * k 是什么類型?

(int)j被強制轉為int類型,j*k中,j再被提升到unsigned int類型,最后i也被提升到unsigned int類型。故最后是unsigned int類型。(前面提到int不要與unsigned int混合計算,負數計算或比較會出現意外情況)

11.假設變量i 是int 類型,變量f 是float 類型,變量d 是double 類型,那么表達式i * f / d 是什么類型?

double類型。

12.?假設變量i 是int 類型,變量f 是float 類型,變量d 是double 類型,請解釋在執行下列語句時發生了什么轉換?

d = i + f;

int - > float -> double

13. 假設程序包含下列聲明:

char c = '\1';
short s = 2;
int i = -3;
long m = 5;
float f = 6.5f;
double d = 7.5;

給出下列每個表達式的值和類型。
(a) c * i   (c) f / c   (e) f - d
(b) s + m   (d) d / s   (f) (int) f

(a)int

(b)long

(c)float

(d)double

(e)double

(f)int

14. 下列語句是否總是可以正確地計算出f 的小數部分(假設f 和frac_part 都是float 類型的變量)?
frac_part = f - (int) f;

如果不是,那么出了什么問題?

數字極小會出現精度問題,所以更好的方法是使用modf函數

7.5節

15. 使用typedef 創建名為Int8 、Int16 和Int32 的類型。定義這些類型以便它們可以在你的機器上分別表示8位、16位和32位的整數。


#include <stdint.h>// 定義跨平臺的8/16/32位整數類型
typedef int8_t   Int8;   // 精確8位有符號整數
typedef uint8_t  UInt8;  // 精確8位無符號整數
typedef int16_t  Int16;  // 精確16位有符號整數
typedef uint16_t UInt16; // 精確16位無符號整數
typedef int32_t  Int32;  // 精確32位有符號整數
typedef uint32_t UInt32; // 精確32位無符號整數int main(void)
{return 0;
}

編程題

1. 如果i * i 超出了int 類型的最大取值,那么6.3節的程序square2.c 將失敗(通常會顯示奇怪的答案)。運行該程序,并確定導致失敗的 n 的最小值。嘗試把變量i 的類型改成short 并再次運行該程序。(不要忘記更新printf 函數調用中的轉換說明!)然后嘗試改成long 。從這些實驗中,你能總結出在你的機器上用于存儲整數類型的位數是多少嗎?

/* Prints a table of squares using a for statement */
#include <stdio.h>
int main(void)
{int i;short n;printf("This program prints a table of squares.\n");printf("Enter number of entries in table: ");scanf("%hd", &n);for (i = 1; i <= n; i++)printf("%10d%10d\n", i, i * i);return 0;
}

2. 修改6.3節的程序square2.c ,每24次平方后暫停并顯示下列信息:

Press Enter to continue...

顯示完上述消息后,程序應該使用getchar 函數讀入一個字符。getchar 函數讀到用戶錄入的回車鍵才允許程序繼續。

/* Prints a table of squares using a for statement */
#include <stdio.h>
int main(void)
{int i, n;char ch;printf("This program prints a table of squares.\n");printf("Enter number of entries in table: ");scanf("%d", &n);getchar(); //要吃掉scanf返還的換行符for (i = 1; i <= n; i++){printf("%10d%10d\n", i, i * i);if(i % 24 == 0)while((ch = getchar()) != '\n'); }return 0;
}

3. 修改7.1節的程序sum2.c ,對double 型值組成的數列求和。

/* Sums a series of numbers (using long int variables) */
#include <stdio.h>
#include <math.h>
int main(void)
{double n, sum = 0.0;printf("This program sums a series of numbers.\n");printf("Enter numbers (0 to terminate): ");scanf("%lf", &n);while (fabs(n) >= 1e-9) {sum += n;scanf("%lf", &n);}printf("The sum is: %f\n", sum);return 0;
}

4. 編寫程序可以把字母格式的電話號碼翻譯成數值格式:

Enter phone number: CALLATT
2255288

(如果沒有電話在身邊,參考這里給出的字母在鍵盤上的對應關系:2=ABC ,3=DEF ,4=GHI ,5=JKL ,6=MNO ,7=PQRS ,8=TUV ,9=WXYZ 。)原始電話號碼中的非字母字符(例如數字或標點符號)保持不變:

Enter phone number: 1-800-COL-LECT
1-800-265-5328

可以假設任何用戶輸入的字母都是大寫字母。

#include <stdio.h>
#include <ctype.h>
int main(void)
{char c;printf("Enter phone number: ");while((c = getchar()) != '\n'){if(isalpha(c) && isupper(c)){switch (c) {case 'A': case 'B': case 'C':putchar('2');break;case 'D': case 'E': case 'F':putchar('3');break;case 'G': case 'H': case 'I':putchar('4');break;case 'J': case 'K': case 'L':putchar('5');break;case 'M': case 'N': case 'O':putchar('6');break;case 'P': case 'Q': case 'R': case 'S':putchar('7');break;case 'T': case 'U': case 'V':putchar('8');break;case 'W': case 'X': case 'Y': case 'Z':putchar('9');break;default: break;}}else putchar(c);}return 0;
}

5. 在十字拼字游戲中,玩家利用小卡片組成單詞,每個卡片包含字母和面值。面值根據字母稀缺程度的不同而不同。(面值有:1——AEILNORSTU,2——DG,3——BCMP,4——FHVWY,5——K,8——JX,10——QZ。)編寫程序通過對單詞中字母的面值求和來計算單詞的值:

Enter a word: pitfall
Scrabble value: 12

編寫的程序應該允許單詞中混合出現大小寫字母。提示 :使用toupper 庫函數。

#include <stdio.h>
#include <ctype.h>
int main(void)
{char c;int v = 0;printf("Enter a word: ");while((c = getchar()) != '\n'){c = tolower(c);switch(c){case 'a': case 'e': case 'i': case 'l': case 'n': case 'o':	case 'r': case 's': case 't': case 'u':v++;break;case 'd': case 'g':v += 2;break;case 'b': case 'c': case 'm': case 'p':v += 3;break;case 'f': case 'h': case 'v': case 'w': case 'y':v += 4;break;case 'k':v += 5;break;case 'j': case 'x':v += 8;break;case 'q': case 'z':v += 10;break;								}}printf("Scrabble value: %d", v);return 0;
}

6. 編寫程序顯示sizeof(int) 、sizeof(short) 、sizeof(long) 、sizeof(float) 、sizeof(double) 和sizeof(long double) 的值。

#include <stdio.h>
int main(void)
{printf("sizeof(int) = %zu\n", sizeof(int));printf("sizeof(short) = %zu\n", sizeof(short));printf("sizeof(long) = %zu\n", sizeof(long));printf("sizeof(long long) = %zu\n", sizeof(long long));printf("sizeof(float) = %zu\n", sizeof(float));printf("sizeof(double) = %zu\n", sizeof(double));printf("sizeof(long double) = %zu\n", sizeof(long double));return 0;
}

7. 修改第3章的編程題6,使得用戶可以對兩個分數進行加、減、乘、除運算(在兩個分數之間輸入+ 、- 、* 或/ 符號)。

#include <stdio.h>int main(void)
{int num1, denom1, num2, denom2, result_num, result_denom;char sign;printf("Enter two fractions separated by a sign:");scanf("%d/%d%c%d/%d", &num1, &denom1, &sign, &num2, &denom2);switch(sign){case '+':result_num = num1 * denom2 + num2 * denom1; //通分-分子result_denom = denom1 * denom2; //通分-分母break;case '-':result_num = num1 * denom2 - num2 * denom1; //通分-分子result_denom = denom1 * denom2; //通分-分母break;case '*':result_num = num1 * num2; //通分-分子result_denom = denom1 * denom2; //通分-分母break;	case '/':result_num = num1 * denom2;result_denom = denom1 * num2;break;default:break;	}printf("The result is %d/%d\n", result_num, result_denom);return 0;
}

8. 修改第5章的編程題8,要求用戶輸入12小時制的時間。輸入時間的格式為時∶分 后跟A、P、AM或PM(大小寫均可)。數值時間和AM/PM之間允許有空白(但不強制要求有空白)。有效輸入的示例如下:

1:15P
1:15PM
1:15p
1:15pm
1:15 P
1:15 PM
1:15 p
1:15 pm

可以假定輸入的格式就是上述之一,不需要進行錯誤判定。

#include <stdio.h>
#include <ctype.h>
int main(void)
{int h, m;int t;char c;printf("Enter a 12-hour time: ");scanf("%d:%d %c", &h, &m, &c);t = toupper(c) == 'A' ? h * 60 + m : h * 60 + m + 12 * 60;if(t < (480 + 583) / 2)printf("Closest departure time is 8:00 a.m., arriving at 10:06 a.m.");else if (t < (583 + 679) / 2)printf("Closest departure time is 9:43 a.m., arriving at 11:52 a.m.");else if(t < (679 + 767) / 2)printf("Closest departure time is 11:19 a.m., arriving at 1:31 p.m.");else if(t < (767 + 840) / 2)printf("Closest departure time is 12:47 a.m., arriving at 3:00 p.m.");else if(t < (840 + 945) / 2)printf("Closest departure time is 2:00 p.m., arriving at 4:08 p.m.");else if(t < (945+1140) / 2)printf("Closest departure time is 3:45 p.m., arriving at 5:55 p.m.");else if(t < (1140 + 1305) / 2)printf("Closest departure time is 7:00 p.m., arriving at 9:20 p.m.");else if(t < (135 + 480) / 2)printf("Closest departure time is 9:45 p.m., arriving at 11:58 p.m.");else printf("Closest departure time is 9:45 p.m., arriving at 11:58 p.m.");return 0;
}

9. 編寫程序要求用戶輸入12小時制的時間,然后用24小時制顯示該時間:

Enter a 12-hour time: 9:11 PM
Equivalent 24-hour time: 21:11

參考編程題8中關于輸入格式的描述。

#include <stdio.h>
#include <ctype.h>
int main(void) {int h, m;char c;printf("Enter a 12-hour time: ");scanf("%d:%d %c", &h, &m, &c);if (toupper(c) == 'A') {printf("Equivalent 24-hour time: %d:%d", h, m);}else {printf("Equivalent 24-hour time: %d:%d", h + 12, m);}return 0;
}

10. 編寫程序統計句子中元音字母(a、e、i、o、u)的個數:

Enter a sentence: And that's the way it is.
Your sentence contains 6 vowels.

#include <stdio.h>
#include <ctype.h>int main(void) {int cnt = 0;char c;printf("Enter a sentence: ");while ((c = getchar()) != '\n') {switch(toupper(c)){case 'A': case 'E': case 'I': case 'O' : case 'U':cnt++;break;default:break;}}printf("Your sentence contains %d vowels.", cnt);return 0;
}

11. 編寫一個程序,根據用戶輸入的英文名和姓先顯示姓氏,其后跟一個逗號,然后顯示名的首字母,最后加一個點:

Enter a first and last name: Lloyd Fosdick
Fosdick, L.

用戶的輸入中可能包含空格(名之前、名和姓之間、姓氏之后)。

#include <stdio.h>
#include <ctype.h>int main(void) {char f, c;printf("Enter a first and last name: ");while(!isalpha(f = getchar()));while((c = getchar()) != ' ');while((c = getchar()) == ' ');putchar(c);while((c = getchar()) != ' '  && (c != '\n')){putchar(c);}printf(", %c.", toupper(f));return 0;
}

12. 編寫程序對表達式求值:

Enter an expression: 1+2.5*3
Value of expression: 10.5

表達式中的操作數是浮點數,運算符是+ 、- 、* 和/ 。表達式從左向右求值(所有運算符的優先級都一樣)。

?

#include <stdio.h>int main(void) {char sign;float n, r = 0.0f;printf("Enter an expression: ");scanf("%f", &r);while ((sign = getchar()) != '\n') {scanf("%f", &n);if (sign == '+') r += n;else if (sign == '-') r -= n;else if (sign == '*') r *= n;else if (sign == '/') r /= n;}printf("Value of expression: %g\n", r);return 0;
}

13. 編寫程序計算句子的平均詞長:

Enter a sentence: It was deja vu all over again.
Average word length: 3.4

簡單起見,程序中把標點符號看作其前面單詞的一部分。平均詞長顯示一個小數位。

#include <stdio.h>int main(void) {char c;int words = 0, cnt = 0;printf("Enter a sentence: ");while((c = getchar()) != '\n'){if(c == ' ' || c == '.') words++;if(c != ' ') cnt++;}printf("Average word length: %.1f", cnt * 1.0f / words);return 0;
}

14. 編寫程序,用牛頓方法計算正浮點數的平方根:

Enter a positive number: 3
Square root: 1.73205

設 x是用戶輸入的數。牛頓方法需要先給出 x平方根的猜測值y(我們使用1)。后續的猜測值通過計算 y和 x/y的平均值得到。下表給出了求解3的平方根的過程。

xyx/yy和x/y的平均值
3132
321.51.75
31.751.714291.73214
31.732141.731961.73205
31.732051.732051.73205

注意, y的值逐漸接近x的平方根。為了獲得更高的精度,程序中應使用double 類型的變量代替float 類型的變量。當y的新舊值之差的絕對值小于0.00001和y的乘積時程序終止。提示 :調用fabs 函數求double 類型數值的絕對值。(為了使用fabs 函數,需要在程序的開頭包含<math.h> 頭。)

#include <stdio.h>
#include <math.h>
int main(void) {int x;double y = 1.0, div, avg = y;printf("Enter a positive number: ");scanf("%d", &x);do{y = avg;div = x / y;avg = (y + div) / 2.0;}while(fabs(y - avg) >= 1E-5 * y);printf("Square root: %g", y);return 0;
}

15. 編程計算正整數的階乘:

Enter a positive integer: 6
Factorial of 6: 720

(a) 用short 類型變量存儲階乘的值。為了正確打印出n的階乘,n的最大值是多少?
(b) 用int 類型變量重復(a)。
(c) 用long 類型變量重復(a)。
(d) 如果你的編譯器支持long long 類型,用long long 類型變量重復(a)。
(e) 用float 類型變量重復(a)。
(f) 用double 類型變量重復(a)。
(g) 用long double 類型變量重復(a)。
在(e)~(g)幾種情況下,程序會顯示階乘的近似值,不一定是準確值。

(a)

#include <stdio.h>
#include <math.h>
int main(void) {int n;short fact = 1;printf("Enter a positive integer: ");scanf("%d", &n);for(int i = 1; i <= n; i++){fact *= i;}printf("Factorial of %d: %hd", n, fact);
}

最多能算到7!

(b)最多能算到12!

(c)最多能算到12!

(d)最多能算到20!

(e) float類型(約6-7位有效數字):

最大n=34(之后精度顯著下降)

(f) double類型(約15-16位有效數字):

最大n=170(171!會溢出為inf)

(g) long double類型(通常80位,約18-19位有效數字):

最大n=1754(具體值取決于編譯器實現)

下次準備更新第8章-數組

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/919968.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/919968.shtml
英文地址,請注明出處:http://en.pswp.cn/news/919968.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

K8S的部署與常用管理

一、k8s的部署 1.1.集群環境初始化 1.1.1.所有主機禁用swap [rootk8s- ~]# systemctl mask dev-nvme0n1p3.swap [rootk8s- ~]# swapoff -a [rootk8s- ~]# systemctl status dev-nvme0n1p3.swap [rootk8s- ~]# vim /etc/fstab 內容&#xff1a; 注釋swap 1.1.2.安裝k8s部署工…

2025年機械工程與自動化技術國際會議(ICMEAT 2025)

2025年機械工程與自動化技術國際會議&#xff08;ICMEAT 2025&#xff09; 2025 International Conference on Mechanical Engineering and Automation Technology一、大會信息會議簡稱&#xff1a;ICMEAT 2025 大會地點&#xff1a;中國杭州 審稿通知&#xff1a;投稿后2-3日內…

高數 不定積分(4-3):分部積分法

文章目錄寫在前面分部積分法&#x1f615; 一個小問題? 分部積分法是怎么來的&#xff1f;&#x1f330; 幾個小例子? 最終總結&#xff01;后話寫在前面 文章傳送門&#xff1a;高數 不定積分&#xff08;4-2&#xff09;&#xff1a;換元積分法 今天再更一篇:) 上篇文章&…

Chrome/360 瀏覽器 WebUI 資源底層機制解析:共享資源與專屬資源的奧秘

在 Chromium 和 360 瀏覽器源碼中&#xff0c;我們會發現 WebUI 頁面不僅有 C 邏輯處理&#xff08;如 WebUIMessageHandler&#xff09;&#xff0c;還伴隨著大量 HTML、CSS 和 JS 文件。尤其是 src/ui/webui/resources 和 src/chrome/browser/360/webui 這兩個目錄&#xff0…

基于springboot的高校后勤保修服務系統/基于android的高校后勤保修服務系統app

基于springboot的高校后勤保修服務系統/基于android的高校后勤保修服務系統app

Qt QML 用Q_PROPERTY快捷訪問c++屬性

在之前我寫過如何調用函數&#xff0c;當時的屬性都是手搓的&#xff0c;也就是自己寫成員變量、變化信號和讀寫函數&#xff0c;但其實有一個很便捷的方法&#xff0c;即使用Q_PROPERTY&#xff0c;下面給出標準結構&#xff1a;Q_PROPERTY(數據類型 變量名 READ 變量名 WRITE…

ubuntu中網卡的 IP 及網關配置設置為永久生效

要將 Ubuntu 中 ens33 和 ens36 網卡的 IP 及網關配置設置為永久生效&#xff08;重啟后不丟失&#xff09;&#xff0c;需通過 netplan 配置并禁用 cloud-init 對網絡的干擾&#xff08;避免重啟后配置被覆蓋&#xff09;&#xff0c;具體步驟如下&#xff1a;一、最終的永久生…

不再讓Windows更新!Edge游戲助手卸載及關閉自動更新

文章目錄Windows系統更新問題方法一&#xff1a;通過注冊表手動設置1. 打開注冊表編輯器2. 定位到目標路徑3. 創建新的DWORD值4. 修改數值方法二&#xff1a;命令行設置1. 打開命令提示符2. 輸入命令驗證設置是否生效恢復更新Edge關閉游戲助手Edge關閉后臺運行Edge關閉自動更新…

css3之flex布局

flex布局要牢記的兩個知識點&#xff1a; 開啟了flex布局的元素叫flex containerflex container里面的直接子元素叫flex items 這兩點要記牢&#xff0c;設置屬性的時候才不會搞混這個是flex布局的整體圖 一、flex container上的屬性 1.flex-direction 修改主軸方向的屬性&…

vscode 搭建C/C++開發環境搭建(linux)

1.編譯器/調試器安裝首先&#xff0c;需要安裝編譯器&#xff08;GCC/G&#xff09;和調試器&#xff08;GDB&#xff09;,用于編譯和調試代碼。1.打開終端(Ctrl Alt T)2.更新軟件包獲取新版本信息sudo apt update3.安裝build-essential包,它包含gcc,g等必要庫sudo apt install…

vue-pure-admin頁面引入和功能添加流程解析

vue-pure-admin (opens new window)是一款開源完全免費且開箱即用的中后臺管理系統模版。完全采用 ECMAScript 模塊&#xff08;ESM&#xff09;規范來編寫和組織代碼&#xff0c;使用了最新的 Vue3、Vite、Element-Plus、TypeScript、Pinia、Tailwindcss 等主流技術開發 以下是…

vlc-android: 編譯自己的libvlc

概述 VLC 媒體播放器作為一款由志愿者開發團隊精心維護的自由、開源且跨平臺的多媒體播放器&#xff0c;能輕松駕馭絕大多數多媒體文件&#xff0c;無論是本地磁盤中的視頻、音頻&#xff0c;還是來自網絡的流媒體協議. VLC for Android 支持網絡串流&#xff0c;無論是基于 H…

并聯諧振與串聯諧振

在LC電路中&#xff0c;感抗和容抗相等時對應的頻率值稱為諧振頻率&#xff0c;在接收廣播電視信號或無線通信信號時&#xff0c;使接收電路的頻率與所選擇的發射的信號頻率相同就叫做調諧。并聯諧振LC并聯諧振電路是指將電感器和電容器并聯形成&#xff0c;如圖所示。在并聯諧…

打印機怎么連接電腦?打印機驅動?【圖文詳解】USB連接打印機?wifi連接打印機?

一、問題背景 在日常辦公與生活里&#xff0c;把電腦和打印機連接起來&#xff0c;是實現文檔、照片等打印的基礎操作。但很多人初次嘗試時&#xff0c;會因不熟悉流程而感到無從下手。 無論是辦公場景下急需打印重要文件&#xff0c;還是日常生活中想要打印照片留念&#xff0…

CVPR 2025 | 醫學影像加速進化:深度學習×多模態,精準診斷再升級

關注gongzhonghao【CVPR頂會精選】今天聊一個醫學圖像領域的前沿探索&#xff1a;結合空間感知卷積、擴散模型與視覺語言模型&#xff0c;從圖像配準到合成分割&#xff0c;再到跨模態理解&#xff0c;打造了一個更加智能、魯棒且可泛化的醫學影像工具鏈。無論是SACB-Net帶來的…

[每周一更]-(第157期):深入理解Go語言的垃圾回收機制:調優與監控

Go語言以其簡潔的語法和強大的并發能力而聞名&#xff0c;而它的垃圾回收&#xff08;GC&#xff09;機制則是支撐其高性能的關鍵組件之一。本文將深入探討Go語言的垃圾回收原理&#xff0c;并介紹如何對其進行調優與監控&#xff0c;以提升應用程序的性能。 Go語言垃圾回收機制…

Java 學習筆記(基礎篇9)

1. 綜合練習題目 1 &#xff1a;金額轉換為中文大寫格式請編寫一個 Java 程序&#xff0c;實現將數字金額轉換為中文大寫格式&#xff08;帶單位&#xff09;的功能&#xff0c;具體要求如下&#xff1a;(1) 程序接收用戶輸入的一個整數金額&#xff08;范圍&#xff1a;0-9999…

云原生俱樂部-k8s知識點歸納(5)

寫到這里&#xff0c;k8s的內容已經到一半了&#xff0c;雖然后面的內容我覺得更加玄學一點。控制器真的是個神奇的東西&#xff0c;雖然后面的CRD會帶著大家一起做一個控制器&#xff0c;但是還是覺得很奇妙。控制器大概就是k8s中的精華了&#xff0c;通過控制器去監聽k8s中ap…

C++復習2

C繼承 繼承的概念 繼承&#xff08;inheritance&#xff09;機制是面向對象程序設計使代碼可以復用的重要的手段&#xff0c;它允許程序員在保持原有類特性的基礎上進行擴展&#xff0c;增加功能&#xff0c;這樣產生新的類&#xff0c;稱為派生類。 繼承呈現了面向對象程序設計…

ZKmall模塊商城的跨境電商支付安全方案:加密與權限的雙重防護

跨境電商支付環節面臨雙重挑戰&#xff1a;一方面&#xff0c;不同國家的支付協議、貨幣結算規則差異顯著&#xff0c;需滿足多幣種、多渠道的支付需求&#xff1b;另一方面&#xff0c;跨境數據傳輸的安全性與操作權限的嚴格管控直接關系到資金安全與合規性。ZKmall 模塊商城針…