Android NDK 7 C語(yǔ)言IO

一、輸入和輸出流

  • C 語(yǔ)言的標(biāo)準(zhǔn)輸入輸出函數(shù)都是獨(dú)立于設(shè)備的,不需要考慮如何在特定設(shè)備上傳輸數(shù)據(jù);C 語(yǔ)言的庫(kù)函數(shù)和操作系統(tǒng)會(huì)確保在特定設(shè)備上的操作完全正常。
  • C 語(yǔ)言的每個(gè)輸入源和輸出目的地都稱(chēng)為流(stream);流和設(shè)備實(shí)體相互獨(dú)立。
    • 程序使用的設(shè)備通常都有一個(gè)或多個(gè)相關(guān)的流;
    • 磁盤(pán)驅(qū)動(dòng)器一般包含多個(gè)文件,流和數(shù)據(jù)源或目的地一一對(duì)應(yīng),而不是和設(shè)備一一對(duì)應(yīng)。
  • 輸入輸出流還可以進(jìn)一步細(xì)分為:字符流(character stream)和二進(jìn)制流(binary stream)。
    • 字符流中傳輸?shù)氖且幌盗凶址?,可以根?jù)格式規(guī)范庫(kù)例程修改;
    • 二進(jìn)制流中傳輸?shù)臄?shù)據(jù)是以系列字節(jié),不能以任何方式修改。

二、標(biāo)準(zhǔn)流

流有標(biāo)識(shí)它的名稱(chēng)。C 語(yǔ)言有三個(gè)在 <stdio.h> 頭文件中預(yù)定義的標(biāo)準(zhǔn)流,程序只要包含這個(gè)頭文件,就可以使用這些流。這三個(gè)標(biāo)準(zhǔn)流是:

  • stdin
  • stdout
  • stderr

stderr 流只是將來(lái)自 C 庫(kù)的錯(cuò)誤信息傳送出去,也可以將自己的錯(cuò)誤信息傳送給 sterr。寫(xiě)入 stdout 和 stderr 這兩個(gè)流的數(shù)據(jù)的目的地默認(rèn)為命令行,這兩個(gè)流的主要差別是:

  • 輸出到 stdout 的流在內(nèi)存上緩存,所以寫(xiě)入到 stdout 的數(shù)據(jù)不會(huì)馬上送到設(shè)備。
  • 輸出到 stderr 的流不進(jìn)行緩存,所以寫(xiě)入 stderr 的數(shù)據(jù)會(huì)立即傳送到設(shè)備上。

對(duì)于緩存的流,程序會(huì)在內(nèi)存中傳輸緩存區(qū)域的數(shù)據(jù),在物理設(shè)備上傳輸數(shù)據(jù)可以異步進(jìn)行。這使得輸入輸出操作更為高效。

使用操作系統(tǒng)命令,stdin 和 stdout 都可以重定向到文件上,而不是默認(rèn)的鍵盤(pán)和屏幕上。

三、鍵盤(pán)輸入

stdin 上的鍵盤(pán)輸入有兩種形式:

  1. 格式化輸入,主要由 scanf() 提供;
  2. 非格式化輸入,通過(guò) getchar() 等函數(shù)接收原始的字符串?dāng)?shù)據(jù)。

3.1、格式化鍵盤(pán)輸入

scanf 從 stdin 流中讀入字符,并根據(jù)格式控制字符串中的格式說(shuō)明符,將它們轉(zhuǎn)換成一個(gè)或多個(gè)值。

示例代碼:

char string[100];
scanf("%s",string);

3.2、輸入格式控制字符串

在 scanf() 函數(shù)中使用的格式控制字符串不完全類(lèi)似于 printf() 中的格式控制字符串。在格式控制字符串中添加一個(gè)或多個(gè)空白字符串,如空格 ' '、制表符 '\t' 或換行符 '\n',scanf() 會(huì)忽略空白字符,直接讀入輸入中的下一個(gè)非空白字符。在格式控制字符串中只要出現(xiàn)一個(gè)空白字符,就會(huì)忽略無(wú)數(shù)個(gè)連續(xù)的空白字符。要注意的是,scanf() 默認(rèn)忽略空白字符,但使用 %c、%[] 或 %n 說(shuō)明符讀取數(shù)據(jù)時(shí)除外(見(jiàn)下表)。

scanf() 會(huì)讀入任何非空白字符(除了%以外),但不會(huì)存儲(chǔ)這個(gè)連續(xù)出現(xiàn)的字符。以下是輸入格式符的一般形式:

% * field_width 長(zhǎng)度修飾符 conversion_character

含義如下:

格式部分 意 義
% 表示格式說(shuō)明符的開(kāi)始
* 表示要忽略的輸入值
field_width 指定輸入字段中的字符數(shù)
長(zhǎng)度修飾符 指定數(shù)值和指針輸入值的長(zhǎng)度
conversion_character 指定要把輸入轉(zhuǎn)換成什么類(lèi)型

以下是一般格式的個(gè)部分說(shuō)明:

  • %:表示格式說(shuō)明符的開(kāi)頭,不能省略。
  • *:可選,表示忽略下一個(gè)輸入值。
  • 字段寬度:可選,它是一個(gè)整數(shù),指定了 scanf() 讀入的字符數(shù)。
  • 長(zhǎng)度修飾符:可選,

示例代碼:

#include <stdio.h>
#define SIZE 20

void try_input(char* prompt, char* format);

int main() {
    try_input("Enter as input: -2.35 15 25 ready2go\n", "%f %d %d %[abcdefghijklmnopqrstuvwxyz] %*1d %s%n");
    try_input("\nEnter the same input again: ", "%4f %4d %d %*d %[abcdefghijklmnopqrstuvwxyz] %*1d %[^o]%n");
    try_input("Enter as input: -2.3A 15 25 ready2go\n", "%4f %4d %d %*d %[abcdefghijklmnopqrstuvwxyz] %*1d %[^o]%n");
    return 0;
}

void try_input(char* prompt, char* format) {
    int value_count = 0;
    float fp1 = 0.0f;
    int i = 0;
    int j = 0;
    char word1[SIZE] = " ";
    char word2[SIZE] = " ";
    int byte_count = 0;
    printf(prompt);
    value_count = scanf(format, &fp1, &i, &j, word1, word2, &byte_count);
    fflush(stdin);
    printf("The input format string for scanf() is:\n \"%s\"\n",format);
    printf("Count of bytes read = %d\n",byte_count);
    printf("Count of values read = %d\n",value_count);
    printf("fp1 = %f i = %d j = %d\n",fp1, i, j);
    printf("word1 = %s word2 = %s\n",word1, word2);
}

3.3、輸入格式字符串中的字符

可以在輸入格式字符串中包含一些不是格式轉(zhuǎn)換說(shuō)明符的字符。為此,必須指定輸入中有這些字符,且 scanf() 函數(shù)應(yīng)讀取它們,但不存儲(chǔ)它們。這些非格式轉(zhuǎn)換字符必須和輸入流的字符完全相同,只要有一個(gè)不同,scanf() 就會(huì)終止輸入。

示例代碼:

#include <stdio.h>

int main() {

    int i = 0;
    int j = 0;
    int value_count = 0;
    float fp1 = 0.0f;
    printf("Enter: fp1 = 3.14159 i = 7 8\n");
    printf("\nInput:");
    value_count = scanf("fp1 = %f i = %d %d", &fp1, &i, &j);
    printf("\nOutput:");
    printf("Count of values read = %d\n", value_count);
    printf("fp1 = %f\ti = %d\tj = %d\n", fp1, i, j);
    return 0;
}

運(yùn)行結(jié)果:

Enter: fp1 = 3.14159 i = 7 8

輸入:

Input:fp1 = 3.14 i = 4 5

輸出:

Output:Count of values read = 3
fp1 = 3.140000  i = 4   j = 5

這些非格式轉(zhuǎn)換字符必須和輸入流的字符完全相同,才會(huì)得到正常的輸出結(jié)果。

3.4、輸入浮點(diǎn)數(shù)的各種變化

使用 scanf() 函數(shù)讀取格式化的浮點(diǎn)數(shù)時(shí),不僅可以選擇格式說(shuō)明符,還可以輸入不同形式的數(shù),示例代碼:

#include <stdio.h>

int main() {
    float fp1 = 0.0f;
    float fp2 = 0.0f;
    float fp3 = 0.0f;
    int value_count = 0;

    printf("Enter: 3.14.314E1.0314e+02\n");
    printf("\nInput:");
    value_count = scanf("%f %f %f", &fp1, &fp2, &fp3);
    printf("\nOutput:");
    printf("Number of values read = %d\n", value_count);
    printf("fp1 = %f fp2 = %f fp3 = %f\n", fp1, fp2, fp3);
    return 0;
}

執(zhí)行結(jié)果:

Enter: 3.14.314E1.0314e+02

Input:3.14.314E1.0314e+02

Output:Number of values read = 3
fp1 = 3.140000 fp2 = 3.140000 fp3 = 3.140000

3.5、讀取十六進(jìn)制和八進(jìn)制值

可以使用格式說(shuō)明符 %x 從輸入流中讀取十六進(jìn)制值,使用 %o 讀取八進(jìn)制值,示例代碼如下:

#include <stdio.h>

int main() {
    int i = 0;
    int j = 0;
    int k = 0;
    int n = 0;

    printf("Enter three integer values\n");
    n = scanf("%d %x %o", &i, &j, &k);
    printf("\nOutput:");
    printf("%d values read.\n", n);
    printf("i = %d j = %d k = %d\n", i, j, k);
    return 0;
}

執(zhí)行結(jié)果:

Enter three integer values: 12 12 12

Output:3 values read.
i = 12 j = 18 k = 10

3.6、使用 scanf() 讀取字符

使用 %c 可以讀取一個(gè)字符,并將它存儲(chǔ)為 char 類(lèi)型。對(duì)于字符串可以使用 %s 或是 %[]。此時(shí)要給存儲(chǔ)的字符串追加上 '\0',作為最后一個(gè)字符。要注意的是,%[] 可以讀入含有空格的字符串,
但是 %s 不可以。使用 %[] 說(shuō)明符時(shí),只需要在方括號(hào)內(nèi)包含空格字符即可。

示例代碼:

#include <stdio.h>
#define MAX_TOWN 10

int main() {
    char initial = ' ';
    char name[80] = {' '};
    char age[4] = {'0'};

    printf("Enter your first initial: ");
    scanf("%c", &initial);
    printf("Enter your first name: ");
    scanf("%s", name);
    fflush(stdin);

    if(initial != name[0]) printf("%s, you got your initial wrong.\n", name);
    else printf("Hi, %s. Your initial is correct. Well done!\n", name);

    printf("Enter your full name and your age separated by a comma:\n");
    scanf("%[^,] , %[0123456789]", name, age);
    printf("\nYour name is %s and your age are %s years old.\n", name, age);

    return 0;
}

執(zhí)行結(jié)果:

Enter your first initial: I
Enter your first name: Ivor
Hi, Ivor. Your initial is correct. Well done!
Enter your full name and your age separated by a comma:
Ivor Horton , 23

Your name is
Ivor Horton  and your age are 23 years old.

scanf("%c", &initial) 中如果輸入空格,那么程序就會(huì)將空白字符當(dāng)成 initial 的值,根據(jù)控制字符串的定義方式,使用說(shuō)明符 %c 輸入的第一個(gè)字符就是要提取的字符。如果不接受空格
為 initial,可以修改為以下輸入語(yǔ)句:

scanf(" %c", &initial);

控制字符串中的第一個(gè)字符是空格,因此 scanf() 會(huì)讀入且忽略所有空格。

scanf("%[^,] , %[0123456789]", name, age) 中“,”后的空格也是必需的,原因與以上語(yǔ)句一樣。

3.7、從鍵盤(pán)上輸入字符串

<stdio.h> 中的 gets_s() 函數(shù)可以將一整行的文本作為字符串讀入。其函數(shù)原型如下:

char *gets_s(char *str, rsize_t n);

該函數(shù)會(huì)將之多 n-1 個(gè)連續(xù)字符讀入指針 str 所指向的內(nèi)存中,直到按下回車(chē)為止。它會(huì)用中止字符‘\0’取代按下的回車(chē)鍵時(shí)讀入的換行符。其返回值與第一個(gè)變?cè)嗤?。如果在輸入?br> 過(guò)程中出錯(cuò),str[0] 就設(shè)置為‘\0’字符??梢允褂脴?biāo)準(zhǔn)庫(kù)函數(shù) fgets() 替代該函數(shù),示例代碼如下:

#include <stdio.h>
#define MAX_TOWN 10

int main() {
    char initial[3] = {' '};
    char name[80] = {' '};

    printf("Enter your first initial: ");
    fgets(initial, sizeof(initial), stdin);
    printf("Enter your name: ");
    fgets(name, sizeof(name), stdin);


    if(initial[0] != name[0]) printf("%s, you got your initial wrong.\n", name);
    else printf("Hi, %s, Your initial is correct. Well done!\n", name);

    return 0;
}

執(zhí)行結(jié)果:

Enter your first initial: I
Enter your name: Ivor Normal
Hi, Ivor Normal
Your initial is correct. Well done!

fgets() 函數(shù)讀取的字符數(shù)比第二個(gè)變?cè)付ǖ淖址麛?shù)少 1,再添加終止字符'\0'。fgets() 函數(shù)再輸入字符串中存儲(chǔ)一個(gè)換行符來(lái)對(duì)應(yīng)按下的回車(chē)鍵,而 gets_s() 函數(shù)不是這樣。
為了避免調(diào)用 printf() 輸出姓名時(shí)輸出一個(gè)換行符,需要覆蓋這個(gè)換行符。

對(duì)于字符串輸入,使用 gets_s() 和 fgets() 通常是首選,除非要控制字符串的內(nèi)容,此時(shí)可以使用 %[]。

3.8、單個(gè)字符的鍵盤(pán)輸入

stdio.h 中的 getc() 函數(shù)從流中讀取一個(gè)字符,把字符代碼返回為 int 類(lèi)型。getc() 的變?cè)橇鳂?biāo)識(shí)的。讀到流的末尾時(shí),getc() 返回 EOF,這個(gè)符號(hào)在 stdio.h 中總是定義
為一個(gè)負(fù)整數(shù)。因?yàn)?char 類(lèi)型可以由編譯器的開(kāi)發(fā)人員確定為帶符號(hào)的或是不帶符號(hào)的,所以 getc() 不返回 char 類(lèi)型。如果它返回 char 類(lèi)型的值,則 char 定義為無(wú)符號(hào)的類(lèi)型
時(shí),就不能返回 EOF。一般情況下,函數(shù)返回 int 類(lèi)型的值,但是希望返回 char 類(lèi)型時(shí),幾乎總是可以肯定它需要返回 EOF。

getchar() 函數(shù)可以從 stdin 中一次讀取一個(gè)字符,等價(jià)于用 stdin 變?cè){(diào)用 getc()。getchar() 在 stdio.h 中定義,原型如下:

int getchar(void);

getchar() 不需要變?cè)?,它?huì)把輸入流中讀入的字符代碼返回為 int 類(lèi)型。getchar() 和 putchar() 每次只處理一個(gè)字符,示例代碼:

#include <stdio.h>

int main(void) {

    char ch;

    while((ch = getchar()) != '#') {
        putchar(ch);
    }

    return 0;
}

執(zhí)行結(jié)果:

Hello MARVEL#I'm Iron man
Hello MARVEL

這里涉及了緩沖區(qū),用戶(hù)輸入的字符被收集并存儲(chǔ)在與一個(gè)被稱(chēng)為緩沖區(qū)的臨時(shí)存儲(chǔ)區(qū),按下 ENTER 鍵后,程序才可使用用戶(hù)輸入的字符。緩沖區(qū)的意義是:首先,把若干字符作為一個(gè)塊進(jìn)行傳輸比逐個(gè)
發(fā)送這些字符節(jié)省時(shí)間;其次,如果用戶(hù)打錯(cuò)了字符,可以直接通過(guò)鍵盤(pán)修正錯(cuò)誤。

緩沖分為兩類(lèi):

  1. 完全緩沖 I/O:緩沖區(qū)被填滿(mǎn)時(shí)才刷新緩沖區(qū),通常出現(xiàn)在文件輸入流中;
  2. 行緩沖 I/O:出現(xiàn)換行符時(shí)刷新緩沖區(qū),鍵盤(pán)輸入通常是行緩沖輸入。

ANSI C 和 后續(xù)的 C 標(biāo)準(zhǔn)都規(guī)定輸入是緩沖的,不過(guò)最初 K&R 把這個(gè)權(quán)利交給了編譯器的編寫(xiě)者。

stdio.h 中也聲明了 ungetc() 函數(shù),它允許把剛才讀取的一個(gè)字符放回輸入流。該函數(shù)需要兩個(gè)參數(shù):輸入流
的字符和流的標(biāo)識(shí)符(對(duì)于標(biāo)準(zhǔn)流就是 stdin)。ungetc() 返回一個(gè) int 類(lèi)型的值,對(duì)應(yīng)放回輸入流的字符,如果操作失敗,返回一個(gè)特殊的字符 EOF。

原則上,可以把一連串字符串放回輸入流,但只保證一個(gè)字符有效。

示例代碼如下:

#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
#include <string.h>
#define LENGTH 50

void eatspace(void);
bool getinteger(int* n);
char* getname(char* name, size_t length);
bool isnewline(void);

int main(void) {
    int number;
    char name[LENGTH] = {'\0'};
    printf("Enter a sequence of integers and alphabetic names in asingle line:\n");
    while(!isnewline()) {
        if(getinteger(&number)) printf("Integer value: %8d\n", number);
        else if(strlen(getname(name,LENGTH)) > 0) printf("Name: %s\n", name);
        else {
            printf("Invalid input.\n");
            return 1;
        }
    }
    return 0;
}

void eatspace(void) {
    char ch = 0;
    while(isspace(ch = (char)getchar()));
    ungetc(ch, stdin);
}

bool getinteger(int* n) {
    eatspace();
    int value = 0;
    int sign = 1;
    char ch = 0;

    if((ch = (char)getchar()) == '-') sign = -1;
    else if(isdigit(ch)) value = ch - '0';
    else if(ch != '+') {
        ungetc(ch, stdin);
        return false;
    }

    while(isdigit(ch = (char)getchar())) {
        value = 10*value + (ch - '0');
    }

    ungetc(ch,stdin);
    *n = value * sign;
    return true;
}

char* getname(char* name, size_t length) {
    eatspace();
    size_t count = 0;
    char ch = 0;
    while(isalpha(ch = (char)getchar())) {
        name[count++] = ch;
        if(count == length -1) break;
    }

    name[count] = '\0';
    if(count < length - 1) ungetc(ch, stdin);
    return name;
}

bool isnewline(void) {
    char ch = 0;
    if((ch = (char)getchar()) == '\n') return true;
    ungetc(ch, stdin);
    return false;
}

執(zhí)行結(jié)果:

Enter a sequence of integers and alphabetic names in asingle line:
Ivor 78 Horton 34 Jane 18

Name: Ivor
Integer value:       78
Name: Horton
Integer value:       34
Name: Jane
Integer value:       18

四、屏幕輸出

將格式化數(shù)據(jù)傳輸?shù)?stout 流的主要函數(shù)是 printf(),stdio.h 中還聲明了可選的 printf_s() 函數(shù),也就是 printf() 函數(shù)的安全版本。printf_s() 和 printf() 的
主要區(qū)別是 printf_s() 不允許在格式字符串中包含 %n,這是因?yàn)?該輸出說(shuō)明符會(huì)把數(shù)據(jù)寫(xiě)入內(nèi)存,導(dǎo)致不安全。

4.1、使用 printf() 的格式化輸出

函數(shù)原型如下:

int printf(char *format, ...);

printf() 函數(shù)的指針變?cè)荒転?NULL,如果變?cè)嘤喔袷秸f(shuō)明符,就會(huì)忽略多余的變?cè)?/p>

printf() 的格式轉(zhuǎn)換說(shuō)明符比 scanf() 復(fù)雜很多,輸出格式說(shuō)明符一般形式如下:

% flag field_width precision size_flag conversion_character

其內(nèi)容和意義如下:

格式部分 標(biāo) 記 意 義 是否可選
% 格式說(shuō)明符的開(kāi)頭
flag -, +, space, # or 0 影響輸出的標(biāo)記
field_width 輸出字段中的最小字符數(shù)
precision 精度指定符
size_flag h, hh, l, ll, j, z, t, t or L 修改轉(zhuǎn)換類(lèi)型的尺寸標(biāo)記
conversion_character d, i, o, u, x, X, p, n, e, E, f, F, g, G, a, A, c, or s 要使用的輸出轉(zhuǎn)換類(lèi)型

影響輸出的標(biāo)記

字 符 用 途
+ 對(duì)于有符號(hào)的輸出,這個(gè)字符確保輸出值的前面總是有一個(gè)符號(hào)+或-。默認(rèn)情況下,只有符號(hào)有 - 號(hào)
- 指定輸出值在輸出字段中左對(duì)齊,右邊用空格填充。輸出的默認(rèn)對(duì)齊方式為右對(duì)齊
0 指定在輸出值的前面填充0,以填滿(mǎn)字段寬度
# 指定將0放在八進(jìn)制的輸出前面,將0X或0x放在十六進(jìn)制的輸出的前面,或者浮點(diǎn)數(shù)包含小數(shù)點(diǎn)。對(duì)于g或G浮點(diǎn)轉(zhuǎn)換字符,忽略尾部的0
space 指定在正數(shù)或0輸出值前面放置一個(gè)空格,而不是+號(hào)

輸出字段中的最小字符數(shù)

如果輸出需要更多的字符,它會(huì)自動(dòng)增加。如果輸出值需要的字符小于指定的最小字符數(shù),多余位置會(huì)填充空白,除非字段寬度用前導(dǎo) 0 指定,例如 09,此時(shí)在左邊會(huì)補(bǔ)入 0。

精度指定符

它通常用于浮點(diǎn)數(shù)的輸出,包含一個(gè)小數(shù)點(diǎn)后跟一個(gè)整數(shù)。說(shuō)明符 .n 表示輸出值精確到小數(shù)點(diǎn)后 n 位。如果輸出的小數(shù)點(diǎn)位數(shù)多于 n,就四舍五入或舍棄掉。如果把它用于整數(shù)裝換,它就指定要在
輸出中顯式的最少位數(shù)。

尺寸標(biāo)記

標(biāo) 記 作 用
h 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 short 或 unsigned short 變?cè)?/td>
hh 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 signed char 或 unsigned char 變?cè)?/td>
l 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 long 或 unsigned long 變?cè)?/td>
ll 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 long long 或 unsigned long long 變?cè)?/td>
j 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 intmax 或 uintmax_t 變?cè)T摌?biāo)記會(huì)避免編譯器發(fā)出警告,因?yàn)?size_t 取決于是實(shí)現(xiàn)碼的整數(shù)類(lèi)型
z 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 size_t 變?cè)?/td>
t 其后的整數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 ptrdiff_t 變?cè)?/td>
L 其后的浮點(diǎn)數(shù)轉(zhuǎn)換說(shuō)明符應(yīng)用于 long double 變?cè)?/td>

轉(zhuǎn)換字符

應(yīng)用類(lèi)型 轉(zhuǎn)換字符 生成的輸出
整數(shù)
d 或 i 帶符號(hào)的十進(jìn)制整數(shù)值
o 不帶符號(hào)的八進(jìn)制整數(shù)值
u 不帶符號(hào)的十進(jìn)制整數(shù)值
x 不帶符號(hào)的十六進(jìn)制整數(shù)(使用小寫(xiě)的十六進(jìn)制數(shù))
X 不帶符號(hào)的十六進(jìn)制整數(shù)(使用大寫(xiě)的十六進(jìn)制數(shù))
浮點(diǎn)數(shù)
f 或 F 帶符號(hào)的小數(shù)值
e 帶符號(hào)和指數(shù)的小數(shù)值
E 與 e 相同,使用 E
g 與 e 或 f 相同,取決于值的大小和精度
G 與 g 相同,但用 E 表示指數(shù)
A 或 a 用十六進(jìn)制表示雙精度值,十六進(jìn)制的尾數(shù)前面加上 0x 或 0X 前綴,指數(shù)前加上 p 或 P,例如:0xh.hhhp±d
指針
p 把變?cè)妮敵鰹橹羔槪冊(cè)念?lèi)型應(yīng)該是 void*
字符
c 單個(gè)字符或精度字符
s 在‘\0’之前的所有字符串或已輸出的 precision 個(gè)字符

注意:%n 只能用于 printf()。對(duì)應(yīng)的變?cè)仨毷?int* 類(lèi)型。作用是將字符數(shù)輸入 stdout。

4.2、轉(zhuǎn)移序列

轉(zhuǎn)移序列 說(shuō) 明
\b 退格
\f 換頁(yè)
\n 換行
\r 回車(chē)(用于打印機(jī)),在屏幕輸出中,就是移動(dòng)到當(dāng)前行的開(kāi)頭
\t 水平制表符

4.3、整數(shù)輸出

示例代碼:

#include <stdio.h>

int main() {

    int i = 15, j = 345, k = 4567;
    long long li = 56789LL, lj = 67891234567LL, lk = 23456789LL;

    printf("i = %d j = %d k = %d i = %6.3d j = %6.3d k = %6.3d\n", i, j, k, i, j, k);
    printf("i = %-d j = %+d k = %-d i = %-6.3d j = %-6.3d k = %-6.3d\n", i, j, k, i, j, k);
    printf("li = %d lj = %d lk = %d\n", li, lj, lk);
    printf("li = %lld lj = %lld lk = %lld\n", li, lj, lk);

    return 0;
}

編譯時(shí)會(huì)有如下警告:

warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long long int’ [-Wformat=]
     printf("li = %d lj = %d lk = %d\n", li, lj, lk);

執(zhí)行結(jié)果:

i = 15 j = 345 k = 4567 i =    015 j =    345 k =   4567
i = 15 j = +345 k = 4567 i = 015    j = 345    k = 4567
li = 56789 lj = -828242169 lk = 23456789
li = 56789 lj = 67891234567 lk = 23456789

由以上輸出結(jié)果可以看出,標(biāo)志“-”使輸出左對(duì)齊。對(duì)于第二個(gè) i 值的輸出,插入了一個(gè)前導(dǎo) 0, 因?yàn)樽钚【戎付?3.如果在格式說(shuō)明符的最小字符寬度前放置一個(gè) 0,并忽略精度說(shuō)明符,也可以
得到相同的結(jié)果。有精度說(shuō)明符時(shí),會(huì)忽略前導(dǎo) 0。

第三行輸出在編譯時(shí)會(huì)發(fā)出警告,給出值的字符寬度及精度不夠大,會(huì)造成意想不到的結(jié)果。

4.4、輸出浮點(diǎn)數(shù)

示例代碼:

#include <stdio.h>

int main() {

    float fp1 = 345.678f, fp2 = 1.234E6f;
    double fp3 = 234567898.0, fp4 = 11.22334455e-6;

    printf("%f %+f %-10.4f %6.4f\n", fp1, fp2, fp1, fp2);
    printf("%e %+E\n", fp1, fp2);
    printf("%f %g %#+f %8.4f %10.4g\n", fp3, fp3, fp3, fp3, fp4);

    return 0;
}

執(zhí)行結(jié)果:

345.678009 +1234000.000000 345.6780   1234000.0000
3.456780e+02 +1.234000E+06
234567898.000000 2.34568e+08 +234567898.000000 234567898.0000  1.122e-05

fp1 的第二個(gè)輸出值說(shuō)明熱如何限制小數(shù)點(diǎn)后的位數(shù)。為 fp2 的第二個(gè)輸出指定的字符寬度太小,放不下小數(shù)位,因此會(huì)舍棄多余的部分。

4.5、字符輸出

示例代碼:

#include <stdio.h>
#include <limits.h>
#include <ctype.h>

int main() {
    int count = 0;
    printf("The printable characters are the following:\n");

    for(int code = 0; code <= CHAR_MAX; ++code) {
        char ch = (char)code;
        if(isprint(ch)) {
            if(count++ % 32 == 0) printf("\n");
            printf(" %c", ch);
        }
    }
    printf("\n");
    return 0;
}

執(zhí)行結(jié)果:

! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~

五、其他輸出函數(shù)

在 stdio.h 頭文件中聲明的 puts() 函數(shù)和 gets_s() 函數(shù)互補(bǔ)。該函數(shù)原型如下:

int puts(const char *string);

puts() 函數(shù)接受字符串指針作為變?cè)?,將字符串后的一個(gè)換行符寫(xiě)入標(biāo)準(zhǔn)輸出流 stdout。其字符串必須用字符‘\0’終止。puts() 函數(shù)的參數(shù)是 const,所以該函數(shù)不能修改
傳送給她的字符串。puts() 函數(shù)用于輸出單行信息,例如:

puts("Is there no end to input and output?");

使用 printf() 函數(shù)必須在字符串末尾添加‘\n’,才能達(dá)到該效果。

5.1 屏幕的非格式化輸出

函數(shù) putchar() 也是 stdio.h 中定義的函數(shù),與 getchar() 函數(shù)互補(bǔ)。其原型如下:

int putchar(int c);

該函數(shù)將單個(gè)字符 c 輸出到 stdout 上,并返回所顯示的字符。它可以輸出信息,一次先是一個(gè)字符,該方法可以控制是否輸出某些字符。例如,輸出一個(gè)字符串:

char string[] = "Beware the Jabberwock, \nmy son!";
puts(string);

或是:

int i = 0;
while() {
    if(string[i] != '\n') putchar(string[i]);
    ++i;
}

5.2、數(shù)組的格式化

在 stdio.h 中聲明的 sprintf_s() 或 snprintf_s() 函數(shù),可以格式化數(shù)據(jù)寫(xiě)入 char 類(lèi)型的數(shù)組中,它們是 sprintf() 標(biāo)準(zhǔn)函數(shù)的安全版本,因此它們禁止寫(xiě)到數(shù)組外部。它們的區(qū)別是,
sprintf_s() 將超出數(shù)組的范圍看作一個(gè)運(yùn)行錯(cuò)誤,而 snprintf_s() 僅截?cái)噍敵?,使結(jié)果能放置在數(shù)組中。這里僅討論 snprintf_s(),其函數(shù)原型如下:

int snprintf_s(char * restrict str, rsize_t n, const * restrict format, ...);

第一個(gè)變?cè)菙?shù)組的地址,是數(shù)組的地址,使輸出的目的地。第二個(gè)變?cè)迨遄娴麻L(zhǎng)度,它的工作方式與 printf_s() 相同,只是將數(shù)據(jù)寫(xiě)入 str,而不是 stdout。示例代碼:

char result[20];
int count = 4;
int nchars = snprintf_s(result, sizeof(result), "A dog has %d legs.", count);

5.3、數(shù)組的格式化輸入

可選的 sscanf_s() 函數(shù)與 snprintf_s() 函數(shù)互補(bǔ),因?yàn)?sscanf_s() 函數(shù)可以在格式字符串的控制下,從 char 類(lèi)型的數(shù)組元素中讀取數(shù)據(jù)。 它是 sscanf() 函數(shù)的安全版本,主要區(qū)別是
sscanf_s() 需要給每個(gè) c、s 或是 [ 說(shuō)明符指定地址和長(zhǎng)度變?cè)?sscanf() 只需要地址。sscanf_s() 函數(shù)的原型如下:

int sscanf_s(const char * restrict str, const char * restrict format, ...);

示例代碼:

char *source = "Fred 94";
char name[10] ={0};
int age = 0;
int items = sscanf_s(source, " %s %d", name, sizeof(name), &age);

以上代碼的執(zhí)行結(jié)果是:name 包含字符串 "Fred",age 的值是 94。items 變量的值是 2,因?yàn)閺膕ource 中讀取了兩項(xiàng)。

參考

Beginning C

深入理解計(jì)算機(jī)操作系統(tǒng)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容