零、概述
寫此篇博客的目的主要是爲了梳理一下c語言基礎知識,因爲不會像學c語言那樣有很多例子,或者按照初學c語言的順序,所以不推薦剛學習c語言的人閱讀。
本篇文章有什麼:
- 對c語言系統的梳理
- 對一些使用的細節進行梳理
- 解答對使用c語言時產生的一些疑惑
- 本篇內容基於“C語言程序設計-浙江大學-翁愷”、“C++ Primer”部分內容以及個人理解進行梳理,不充分之處歡迎指出
一、變量和基本類型
(一)基本類型
1、有符號常見類型大小及其範圍
1byte(字節)=8bit(位);每個bit就是一個0或者1,byte是c語言裏面數據的最小單位
常用2的次方:
27=128
28=256
215=32,768
216=65,536
231=2,147,483,648
232=4,294,967,296
263=9,223,372,036,854,775,808
264=18,446,744,073,709,551,616
類型 | 含義 | 32位編譯器中大小(一般) | 64位編譯器中大小(一般) | 最小值(32位) | 最大值(32位) |
---|---|---|---|---|---|
bool(stdbool.h) | 布爾類型 | 1byte | 1byte | false | true |
char | 單個字符 | 1byte | 1byte | -27 | 27-1 |
short | 短整形 | 2byte | 2byte | -215 | 215-1 |
int | 整形 | 4byte | 4byte | -231 | 231-1 |
long | 長整形 | 4byte | 8byte | -231 | 231-1 |
long long | 長整形 | 8byte | 8byte | -263 | 263-1 |
float | 單精度浮點數 | 4byte | 4byte | -2127 | 2128 |
double | 雙精度浮點數 | 8byte | 8byte | -21023 | 21024 |
long double | 擴展精度浮點數 | 12byte | 16byte | -216383 | 216384 |
char* | 字符常量或字符串常量 | 4byte | 8byte | 無意義 | 無意義 |
2、浮點數範圍來由及其有效數字
這裏單獨討論一下浮點數的取值範圍,浮點數都遵循IEEE754標準,所以:
4*8=32位的float的第1位是符號位,第2~9位有8位是指數位,第10~32位有23位是尾數位
那麼可以很容易看到float的範圍是[-1*2127≈1.7*1038, 1*2128≈3.4*1038]
因爲轉換成IEEE754都要進行標準化,也就是將原來的整數點整數的形式轉化成二進制點二進制的形式,然後將點的位置移動到左邊第一個1(2)之後,產生指數位。規定點之後的數填在尾數位上,所以31位前暗含了1(2),所以上面的範圍會先乘個1。
32位的指數位 = 移位數(左移一位+1,右移一位-1)+ 127得出。所以負數只有127(127-127=0),而正數有128(127+128=255)
8*8=64位的double的第1位是符號位,第2~12位有11位是指數位,第13~64位有52位是尾數位
那麼可以很容易看到float的範圍是[-1*21023≈8.988*10307, 1*21024≈1.797693*10308]
12*8=96或16*8=128位的long double的第1位是符號位,第2~16位有15位是指數位,在32位系統中第17~96位有80位是尾數位,在64位系統中第17~128位有112位是尾數位,那麼範圍是[-1*216383≈5.9*104931, 1*216384≈1.1897*104932](與尾數大小無關)。
其實float的範圍就已經非常非常大了,那爲什麼還會有double甚至long double呢,這是因爲浮點數能準確記錄量級,但是無法準確記錄太長的數字,數字的有效位數值取決於尾數位的長度,我們可以總結成下表:
類型 | 有效位數計算 | 有效位數 |
---|---|---|
float | 223+1=16,777,216 | 8-1=7 |
double | 252+1=9,007,199,254,740,992 | 16-1=15 |
long double | 280+1=2.4*10 24 / 2112+1=1.038*1034 | 25-1=24 / 35-1=34 |
3、字面值常量
(1)十進制字面值
對於一個20(10)=24(8)=14(16),在c語言中對應表達爲24(10進制)、024(8進制)、0x14(16進制)。
默認情況下,十進制字面值的類型是int,如果int裝不下就是long,再裝不下就是long long。
(2)浮點數字面值
對於一個浮點數可以表示爲314.159(10)也可以表示爲3.14159e+2(科學記數法)。
默認情況下,浮點數字面值是一個double
(3)指定字面值類型
整形字面值 | ||
後綴 | 最小匹配類型 | 例子 |
u / U | unsigned | 20u / 20U |
l / L | long | 20l / 20L |
ll / LL | long long | 20ll / 20LL |
浮點形字面值 | ||
f / F | float | 20.0f / 20.0F |
l / L | long double | 20.0l / 20.0L |
4、字符和字符串常量
形如’a’、"HelloWorld"都可以看作是常量,特別說明當例如char* s1="HelloWorld";char* s2="HelloWorld";
時,s1和s2所指的都是內存中代碼段的常量,可讀不可寫且地址相同,類似於const char* s1;
字符是以單引號’括住的單個字符,只佔一個char(一個byte)
字符串以雙引號"括住一系列字符,最後隱含了’\0’,所以佔n+1個char(n爲字符串有意義的長度)
特殊的常量:無窮,每個編譯器的表示都不一樣,可以使用如下代碼輸出:
printf("%f\n", 1/0.0); // 無窮大。我的電腦上爲:1.#INF00
printf("%f\n", -1/0.0); // 無窮小。我的電腦上爲:1.#INF00
printf("%f\n", 0.0/0.0); // 空。我的電腦上爲:-1.#IND00
5、總結
有符號的類型(除浮點數)的範圍都是由[-2n-1, 2n-1-1](n爲類型所佔bit)
無符號的類型(除浮點數)的範圍都是由[0, 2n-1](n爲類型所佔bit)
浮點數的範圍與其指數位大小有關,爲[-2{[2^(m-1)]-1}, 2[2^(m-1)]](m爲指數位位數)
(二)變量
1、定義變量
變量由一個類型聲明符加一個或多個變量名組成的列表(c99及以後可以在函數內任意位置定義變量),例如:
int a; // a是int類型的變量
double b=1.0; //b是double類型的變量,賦1.0爲初值
char c, d; // c, d是char類型的變量
2、常用特殊前綴
extern int a; // 聲明一個int類型的變量a,一般用在.h文件中聲明項目全局變量
const int b=1; // 定義一個不可變的變量b,使用const關鍵字都要賦初值
static int c=1; // 在函數中使用,只在第一次使用時初始化,相當於作用域在函數內的全局變量
typedef long long int; // 把long long當作int使用
3、數組與初始化變量
int a=1; // 給a初始化爲1
// 以下爲C99加入的特性
int b[10] = {1}; // 第一個元素初始化爲1,其他9個元素初始化爲0
int c[10] = {0}; // 全部初始化爲0,常用初始化手段
int d[10] = {[1]=1, 2, [4]=4}; // 0 1 2 0 4 0 0 0 0 0
4、變量作用域
變量有兩種類型:局部變量全局變量,變量作用域即變量可使用的範圍。
對於局部變量可以簡單理解爲大括號{}內即爲一個作用域,變量在哪個大括號內,作用域就在哪。
對於全局變量的作用域就是在當前的.c文件中,在.h文件聲明後可以在整個項目內使用。
對於同名變量,小作用域的變量會在其作用域中覆蓋大作用域的變量。
(1)局部變量與全局變量的差別
差別因素 | 局部變量 | 全局變量 |
---|---|---|
初始值 | 初始值取決於內存裏的數(隨機) | 一般爲0,指針爲null |
作用域 | 僅限於大括號內 | .c文件甚至項目 |
(三)字符串
字符串在c語言中有兩種形式char*和char[],這裏簡單分辨下兩者之間的不同。
char* c1="HelloWorld!"
中,c1指向代碼段中的常量,只讀不寫,且常量相同,指向的地址也相同。
char c2[]="HelloWorld!"
中,c2指向堆棧段中的數據,可讀可寫,相當於把代碼端的數據拷貝了出來。
(四) 自定類型——結構體
結構體可以看作是一種個基礎類型複合的類型。
// 聲明如下:
struct DATE{
int year, month, day;
};
// 幾種定義方式如下:
struct DATE date1; // 不賦初值
struct DATE date2={2020, 4, 5}; // 根據結構體中的順序賦初值,這裏是年月日
struct DATE date3 = {.year=2020, .day=5}; // 給單獨變量賦初值
struct DATE *date4 = &date3; // 用指針取date3地址(指針後面會單獨說)
// 幾種賦值方式如下:
date3 = (struct DATE){.year=1999}; // 將數據強制轉換成struct DATE類型賦值
date3 = date2; // 自動賦值
date3.year=2010; // 對變量中單一元素賦值
date4->year=2010; // 對指針所指變量中的單一元素賦值
// 常用聲明
typedef struct DATE2{
int year, month, day;
} D;
D date5; // 這樣就可以不用寫struct DATE2這麼一長串,取而代之用D來表示
擴展:union
union作爲關鍵字與struct類似,但是struct中每個成員都是單獨的內存,而union只佔最大成員變量的大小,通常用與二進制與十進制的轉換。
union DATE{
int year;
int month;
int day;
}; /* 一個union DATE只有一個int大小 */
擴展:enum枚舉,枚舉類似#define,但是有體系些(c語言的枚舉不好用)
// 聲明枚舉, 默認下標RED:0, YELLOW:1, GREEN:2
enum COLOR1 {RED1, YELLOW1, GREEN1};
// 自定下標
enum COLOR2 {RED2=1, YELLOW2, GREEN2=5};
二、表達式
這裏給出運算符優先級
優先級 | 運算符 | 名稱或含義 | 使用形式 | 結合方向 | 說明 |
1 | [] | 數組下標 | 數組名[整型表達式] | 左到右 | |
() | 圓括號 | (表達式)/函數名(形參表) | |||
. | 成員選擇(對象) | 對象.成員名 | |||
-> | 成員選擇(指針) | 對象指針->成員 | |||
2 | - | 負號運算符 | -算術類型表達式 | 右到左 | 單目運算符 |
(type) | 強制類型轉換 | (純量數據類型)純量表達式 | |||
++ | 自增運算符 | ++純量類型可修改左值表達式 | 單目運算符 | ||
-- | 自減運算符 | --純量類型可修改左值表達式 | 單目運算符 | ||
* | 取值運算符 | *指針類型表達式 | 單目運算符 | ||
& | 取地址運算符 | &表達式 | 單目運算符 | ||
! | 邏輯非運算符 | !純量類型表達式 | 單目運算符 | ||
~ | 按位取反運算符 | ~整型表達式 | 單目運算符 | ||
sizeof | 長度運算符 | sizeof 表達式 / sizeof(類型) | |||
3 | / | 除 | 表達式/表達式 | 左到右 | 雙目運算符 |
* | 乘 | 表達式*表達式 | 雙目運算符 | ||
% | 餘數(取模) | 整型表達式%整型表達式 | 雙目運算符 | ||
4 | + | 加 | 表達式+表達式 | 左到右 | 雙目運算符 |
- | 減 | 表達式-表達式 | 雙目運算符 | ||
5 | << | 左移 | 整型表達式<<整型表達式 | 左到右 | 雙目運算符 |
>> | 右移 | 整型表達式>>整型表達式 | 雙目運算符 | ||
6 | > | 大於 | 表達式>表達式 | 左到右 | 雙目運算符 |
>= | 大於等於 | 表達式>=表達式 | 雙目運算符 | ||
< | 小於 | 表達式<表達式 | 雙目運算符 | ||
<= | 小於等於 | 表達式<=表達式 | 雙目運算符 | ||
7 | == | 等於 | 表達式==表達式 | 左到右 | 雙目運算符 |
!= | 不等於 | 表達式!= 表達式 | 雙目運算符 | ||
8 | & | 按位與 | 整型表達式&整型表達式 | 左到右 | 雙目運算符 |
9 | ^ | 按位異或 | 整型表達式^整型表達式 | 左到右 | 雙目運算符 |
10 | | | 按位或 | 整型表達式|整型表達式 | 左到右 | 雙目運算符 |
11 | && | 邏輯與 | 表達式&&表達式 | 左到右 | 雙目運算符 |
12 | || | 邏輯或 | 表達式||表達式 | 左到右 | 雙目運算符 |
13 | ?: | 條件運算符 | 表達式1? 表達式2: 表達式3 | 右到左 | 三目運算符 |
14 | = | 賦值運算符 | 可修改左值表達式=表達式 | 右到左 | |
/= | 除後賦值 | 可修改左值表達式/=表達式 | |||
*= | 乘後賦值 | 可修改左值表達式*=表達式 | |||
%= | 取模後賦值 | 可修改左值表達式%=表達式 | |||
+= | 加後賦值 | 可修改左值表達式+=表達式 | |||
-= | 減後賦值 | 可修改左值表達式-=表達式 | |||
<<= | 左移後賦值 | 可修改左值表達式<<=表達式 | |||
>>= | 右移後賦值 | 可修改左值表達式>>=表達式 | |||
&= | 按位與後賦值 | 可修改左值表達式&=表達式 | |||
^= | 按位異或後賦值 | 可修改左值表達式^=表達式 | |||
|= | 按位或後賦值 | 可修改左值表達式|=表達式 | |||
15 | , | 逗號運算符 | 表達式,表達式,… | 左到右 | 從左向右順序結合 |
針對十進制字面值
<<:左移數據,相當於乘2(右邊填0)
>>:右移數據,相當於除2(unsigned左填0,signed左邊填原來的最高位(因爲負數移位還要是負數))
三、語句
簡單語句:;
,只有一個分號的空語句。
複合語句:由{}
大括號嵌套起來的語句,空塊(括號裏什麼都沒用)等於空語句。
(一)條件語句
1、if語句
if語句一般格式:if…else if…else(翻譯爲如果…否則如果…否則)
if (age>18) { // 如果年齡大於18歲
} else if(age<18) { // 如果年齡小於18歲
} else { // 上面的都不滿足
}
可用搭配:
if...
if...else if...
if...eles...
if...else if...else if...else...
tips:
(1)if依據後面的條件語句的結果進行判斷,非0爲真,0爲假
(2)else不需要接條件
(3)if語句後面不要接;
2、switch語句
switch語句一般格式:
switch(ch){ // switch是傳入整數並判斷整數進行判斷
case 'a':
...
break;
case 'b':
...
break;
default:
...
}
tips:
(1)case只是入口,所以每個case都要用break;
跳出
(2)default類型if語句中的else,即上面的case都不滿足時進入
(3)switch只能傳入整數進行判斷,上面的例子就是將char類型的ch轉換成了ASCII碼進行比較
(4)switch後面不要接;
(二)迭代語句
1、while語句
while語句的一般格式:
while(條件){
語句
}
tips:
(1)while適用於不知道次數的循環
(2)while後面不要接;
2、do while語句
do while語句的一般格式:
do{
語句
}while(條件);
tips:
(1)do while適用於不知道次數且要先做一遍的循環
(2)do while後面要接;
(3)do while後面要接;
(4)do while後面要接;
3、for語句
for語句的一般格式:
for (初始化變量; 條件; 變量操作) {
語句
}
int n=10; // 定義循環次數
// 例子1,循環n次
for (int i=0; i<n; i++) {
;
}
等價於
int i=0;
while(i<n){
; // 語句
i++;
}
// 例子2,多參數
for (int i=0, j=10; i<n; i++, j--) {
;
}
等價於
int i=0, j=10;
while(i<n){
; // 語句
i++;
j--;
}
tips:
(1)for適用於知道次數的循環
(2)for後面不要接;
(3)for可以壓縮行數,簡化部分使用while的情況
(三)跳轉語句
break;直接跳出當前條件語句或迭代語句,能且只能 跳出 當前 的 一個 語句。
continue;在 條件語句 中表示什麼都不做(替代空語句);在 迭代語句 中表示 直接進入下一輪循環,不執行完當前循環 。
goto;跳轉到指定位置(該位置必須有聲明),例子:
start: // goto聲明
... // 語句
... // 語句
... // 語句
goto start; //跳轉到start聲明位置
!!!注意,goto僅推薦用於跳出多重循環,例如:
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
for (int k=0; k<o; k++) {
if (跳出條件) {
goto end;
}
}
}
}
end:
四、函數
(一)函數一般格式
返回值類型 函數名(形參列表) {
...語句...
return 返回值;
}
// 例子,main函數
int main(){
return 0;
}
tips:
(1)特殊的返回值類型void(什麼都不返回),此時return 返回值;寫爲return;。
(2)形參列表爲空時,表明不需要傳入函數。
(二)函數聲明
函數聲明一般寫在程序開頭。儘管部分c語言編譯器沒有要求在調用函數時,就要知道函數格式。但是讓編譯器提前知道格式,就可以在編譯時進行檢查,防止我們錯誤使用了函數。
有一函數定義如下:
int f(int a){
return a;
}
那麼函數的聲明可以寫成:
int f(int a);
或者
int f(int);
特殊的對於返回值和參數都是void的函數,聲明可以寫成:
void f(void);
(三)結構體中的函數
如果能在結構體中寫函數,那麼結構體就更接近一個類了,例子如下:
struct SHOWDATE{
void (*show)(int year, int month, int day);
};
static void show(int year, int month, int day){
printf("%d-%d-%d", year, month, day);
}
static SHOWDATE showDate={.show=show};
int main(){
showDate.show(2020, 1, 1);
return 0;
}
五、指針
(一)定義
int a=10;
int *p=&a; // (1)(2)
*p = 12; // (3)
int * const p1; // (4)
const int * p2; // (5)
int const * p3;
(1)*代表這是一個指針類型。具體到例子中就一個int*類型,代表p存放一個指向int類型的地址
(2)&代表取地址。在例子中就是取int類型變量的地址,即變量a的地址
(3)*p代表一個變量,這個變量就是p所指地址裏面的變量
(4)int * const p1;不能修改p裏的地址
(5)const int * p2;和int const * p3;不能修改p裏地址對應的值
(二)結構體與指針
typedef strcut stDATE{
int year, month, day;
} DATE;
int main(){
DATE date1={2020, 1, 1};
DATE* date2=&date1;
date1.year=2019;
date2->month=2;
printf("%d-%d-%d", date1.year, date1.month, date1.day);
return 0;
}
(1)結構體變量訪問結構體成員使用.,結構體指針變量訪問結構體成員使用->。
(三)數組與指針
int a[10] = {0} // (1)、(2)
int *p = a; // (3)
void *q = (void*)p; // (4)
int *new = (int*)malloc(10*sizeof(int)); // (5)
free(new);
(1)int a[10]裏面的a實際上是數組第一個變量的地址,所以可以int *p = a;。
(2)*(p+n)與a[n]等價。
(3)int*指針變量加上一個數n,代表指針變量的數值加上n*32(int的bit)得到偏移。
(4)q表示未定的類型(類似char*但不一樣)。
(5)使用int*指針變量來創建數組,使用完要free(程序關閉會自動釋放,如果程序長時間使用則會造成內存泄露)。
其他:
char c[][]錯誤;
char c[][10]正確且限制大小;
char *c[]正確,c[n]是一個char*的字符或字符串
char **c正確,一個指向指針的指針
六、文件讀取
(一)文本文件
函數定義:
FILE* fopen(const char* restrict path, const char* restrict mode); // 打開文件。參數:路徑,模式
int fclose(FILE* stream); // 關閉文件
fscanf(FILE*, ...); // 文件讀取(類似scanf,只是加了第一個參數,具體在八)
fprintf(FILE*, ...); // 文件輸出(類似printf,只是加了第一個參數,具體在八)
fopen的mode選項:
r:只讀
r+:讀寫,從文件頭開始
w:只寫,清空或新建
w+:讀寫,清空或新建
a:追加,追加或新建
…x:只新建,若存在則不能打開(wx、ax等)
例子:
FILE* p=fopen("file", "r");
if(fp) {
...
fclose(fp);
}
(二)二進制文件
函數定義(打開文件和關閉文件與文本文件一致):
// ptr:讀入字符串存放的變量、size*nmemb:讀多少字符、stream:文件句柄
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
二進制文件讀寫主要是讀寫結構體,現在不常用,因爲可移植性不好,不如讀寫文件
七、編譯預處理指令與.h文件
(一)編譯預處理指令
在c語言中,所有#開頭的語句都是編譯預處理指令。
c語言編譯經過
.c——處理編譯預處理指令——》
.i——c編譯器編譯成彙編文件——》
.s——生成目標代碼文件——》
.o——生成可執行文件——》
#.out / .exe
四步得到可執行文件,第一步就是處理編譯預處理指令。
1、# include <stdio.h>
# include <stdio.h>就是將stdio.h文件裏面的內容複製到代碼裏面去
2、#define 名稱 操作(宏定義)
# define被稱爲宏定義,格式爲# define 名稱 操作
例子:
#define PI 3.14159
#define cube(x) (x*x*x)
int main(){
cube(3);
return 0;
}
可以用宏定義定義一個變量PI或一個方法cube。
宏定義定義方法看起來很方便,但十分不推薦初學者使用,因爲宏定義的方法是沒有類型的,而且運算順序也不好理解(看上面 二、表達式 15個運算等級的表,就知道多複雜了)。
如果確實想用類似宏定義的方式來定義函數,可以使用下面的方法。
3、(補充)inline內聯函數
inline char *dbtest(int a)
{
return (a % 2 > 0 ? "奇" : "偶");
}
(二).h文件
.h文件主要用來聲明對應.c文件裏面的變量或函數,把.c文件裏面的聲明放到.h文件就可以了(全局變量需要加上extern前綴)。
導入標準庫時推薦使用<>,如
# include <stdio.h>
導入自定聲明時推薦使用"",如# include "train.h"
(三)一些常用宏
printf("%d", __LINE__); // 輸出當前語句行號
printf("%s", __FILE__); // 輸出當前文件名
printf("%s", __DATE__); // 輸出編譯日期
printf("%s", __TIME__); // 輸出編譯時間
printf("%d", __STDC__); // 當要求程序嚴格遵循ANSIC標準時該標識符被賦值爲1
printf("%s", __FUNC__); // 輸出當前函數名
八、常用函數
(一)常用函數
size_t = unsigned int
庫名 | 函數定義 | 解釋 |
---|---|---|
默認 | size_t sizeof(type_name) | 返回byte |
string.h | size_t strlen(const char *s) | 返回s的字符串長度(不包括最後的\0) |
string.h | int strcmp(const char *s1, const char *s2) | 比較兩個字符串,返回0:相等; 1:s1>s2; -1:s1<s2 |
string.h | int strncmp(const char *s1, const char *s2, size_t n) | 安全版本比較前n個字符的大小,其他同上 |
string.h | char* strcpy(char *restrict dst, const char *restrict src) | 把src的字符串拷貝到dst,restrict表示不重疊 |
string.h | char* strncpy(char *restrict dst, const char *restrict src, size_t n) | 安全版本,拷貝n個字符,其他同上 |
string.h | char* strcat(char *restrict s1, const char *restrict s2) | 把s2拷貝到s1後面,s1要足夠大 |
string.h | char* strncat(char *restrict s1, const char *restrict s2, size_t n) | 安全版本,追加n個字符,其他同上 |
string.h | char* strchr(const char *s, int c) | 在s串中找到c第一次出現的位置,返回指針,沒有爲null |
string.h | char* strrchr(const char *s, int c) | 在s串中從右邊找到c第一次出現的位置,返回指針,沒有爲nulltring.h |
string.h | char* strstr(const char *s1, const char *s2) | 在字符串s1中不忽略大小寫尋找字符串s2 |
string.h | char* strcasestr(const char *s1, const char *s2) | 在字符串s1中忽略大小寫尋找字符串s2 |
string.h | void *memset(void *s, int c, unsigned long n) | 將指針變量s所指向的前n字節的內存單元用一個“整數”c替換 |
string.h | void *memcpy(void *dest, const void *src, size_t n) | 從src的開始位置拷貝n個字節的數據到dest。如果dest存在數據,將會被覆蓋。 |
stdlib.h | system(“pause”) | 暫停程序,按任意鍵繼續 |
(二)scanf和printf
scanf(stdio.h)函數聲明:int scanf(const char *format, …)
format格式:%[flag]type
printf(stdio.h)函數聲明:int printf(const char *format, …)
format格式:%[flags][width][.prec][hlL]type
flag | 含義 |
---|---|
- | 跟width一起用,左對齊(%-9d) |
+ | 正數強制輸出正號(%+9d) 可以%±9d/%-+9d |
空 | 正常輸出 |
0 | 數字前填充0(%09d) 不可以%-09d(因爲負號已經是左對齊了) |
width / prec | 含義 |
---|---|
number | 最小字符數 |
.number | 小數點後位數。%9.2f:一共9位,小數點後2位(正數部分7位) |
* | 將number放到後面作爲參數使用(%*d, number, int) |
.* | 將.number放到後面作爲參數使用(%.*d, .number, int) |
hlL | 含義 |
---|---|
hh | 單個字節(char 1byte) |
h | short(2byte) |
l | long(4byte) |
ll | long long(8byte) |
L | long double(16byte) |
type | 用於 | 含義 |
---|---|---|
i / d | int | 接受整數值並將它表示爲有符號的十進制整數,i是老式寫法 |
u | unsigned int | 無符號10進制整數 |
o | unsigned int | 無符號8進制整數(不輸出前綴0) |
x / X | unsigned int | 無符號16進制整數,x對應的是abcdef,X對應的是ABCDEF(不輸出前綴0x) |
f / F / lf | double | 單精度浮點數和雙精度浮點數用f(lf 在C99開始加入標準,意思和 f 相同) |
e / E | double | 科學計數法表示的數,此處"e"的大小寫代表在輸出時用的“e”的大小寫 |
g / G | double | 有效位數,如:%.8g表示單精度浮點數保留8位有效數字。 |
c | char | 字符型。可以把輸入的數字按照ASCII碼相應轉換爲對應的字符 |
s / | char * / wchar_t * | 字符串。輸出字符串中的字符直至字符串中的空字符(字符串以’\0‘結尾,這個’\0’即空字符 |
p | 指針(void *) | 以16進制形式輸出指針 |
n | 讀入/寫出的個數(int *) | 到此字符之前爲止,一共輸出的字符個數,不輸出文本 |
% | 無 | 不進行轉換,輸出字符‘%’(百分號)本身 |
m | 無 | 打印errno值對應的出錯內容,(例: printf("%m\n"); ) |