1. 程式人生 > >C語言scanf函式輸入時鍵盤緩衝區\n的問題[經典問題]

C語言scanf函式輸入時鍵盤緩衝區\n的問題[經典問題]

程式時對scanf在鍵盤緩衝區留下的字元有疑問,思考不果。看了百度百科上的scanf詞條,說scanf輸入遇到空格、跳格、回車才會從緩衝區往變數送字元。於是自己寫了以下幾個程式思考,還是不果。

程式1

#include "stdio.h"

void main()

{

 char a;

 char b;

 scanf("%d",&a);

 scanf("%d",&b);

 printf("%d %d",a,b);

}

鍵盤輸入

97<回車>96<回車>

輸出

97 96

問題1:呼叫第一個scanf輸入時,鍵盤緩衝區所有的字元為97\n,遇到回車,所以緩衝區把97賦值給a。呼叫第二個scanf輸入時,鍵盤緩衝區所有的字元為96\n,遇到回車,所以緩衝區把96賦值給b。以上我的分析對嗎?

程式2

#include "stdio.h"

void main()

{

 char a;

 char b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}

鍵盤輸入

9<回車>

輸出

57 10

問題2:呼叫第一個scanf輸入時,鍵盤緩衝區所有的字元為9\n,遇到回車,所以緩衝區把9賦值給a。呼叫第二個scanf輸入時,鍵盤緩衝區所有的字元為\n,遇到回車,所以緩衝區把\n賦值給b。以上我的分析對嗎?如果對,那程式1中呼叫第一個scanf時,又為什麼不是把97賦值給a後,將\n賦值給b呢?為什麼呼叫第二個scanf時還需要繼續輸入96<回車>來對b賦值?呼叫第一個scanf輸入時留在緩衝區的\n去哪裡了?無端消失了?

程式3

#include "stdio.h"

void main()

{

 char a[100];

 char b[100];

 scanf("%s",a);

 scanf("%s",b);

 printf("%s %s",a,b);

}

鍵盤輸入

abc<回車>def<回車>

輸出

abc def

問題3:從輸出結果可以看出,字元陣列a和字元陣列b都在同一行輸出。所以字元陣列a的值為{‘a’,’b’,’c’},不是{‘a’,’b’,’c’,’\n’}。字元陣列b的值為{‘d’,’e’,’f’},不是{‘\n’,‘d’,’e’,’f’},也不是{‘\n’,‘d’,’e’,’f’,’\n’}。以上的分析對嗎?如果對,那呼叫第一個scanf輸入時留在緩衝區的\n去哪裡了?還有第二個scanf留下\n呢?

程式4

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf("%c",&j);/*注意這裡%前沒有空格*/

 printf("%d",j);

}

鍵盤輸入

1<回車>

輸出

10

程式5

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf(" %c",&j);/*注意這裡%前有一個空格*/

 printf("%d",j);

}

問題4:程式4應該就像程式2那樣,最後把\n(ASCII值為10)賦值給j了,所以輸出10。但程式5 scanf裡那個空格如何阻止\n給j賦值?想不通,懇請賜教!

程式6

#include "stdio.h"

void main()

{

 int a;

 int b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}

鍵盤輸入

1<回車>

輸出

-858993615 -858993654

問題5:這個問題和\n無關的,但寫了程式1,卻發現了這個問題。我用的是VC6,就算不是VC6,C的任何編譯軟體裡int和char不都是通用的嗎?為什麼程式1用%d格式能正常獲得char型變數,但程式6用%c格式卻不能正常獲得int型變數?

-----------------------------------------------------分界線-----------------------------------------------

你首先要明白,從鍵盤讀入鍵盤緩衝區(buffer)的資料都是以ASCII碼儲存的(包括回車)。

程式1

#include "stdio.h"

void main()

{

 char a;

 char b;

 scanf("%d",&a);

 scanf("%d",&b);

 printf("%d %d",a,b);

}

鍵盤輸入

97<回車>

第一次回車後,buffer中的ASCII:39h,37h,0AH(0A是換行的ASCII), scanf會根據格式字串中的第一個%d對buffer按位元組順序讀取,當讀取到0A時,認為%d型的資料結束,此時把已經讀取到的39h,37h依據%d轉為整型資料97儲存在字元型變數a中。(這裡是除去了掃描截止點0AH)

此時buffer中已經無任何資料了。

96<回車>

第二次回車後,按同樣的流程,scanf會根據格式字串中的第二個%d對buffer按位元組順序讀取。最終b得到96.

此時buffer中已經無任何資料了。

輸出

97 96

程式2

#include "stdio.h"

void main()

{

 char a;

 char b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}

鍵盤輸入

9<回車>buffer:39H,0AH

因為scanf會按照第一個%c格式掃描buffer(只掃描一個位元組就結束),然後把掃描到的39H直接送到變數a(當以%d格式讀出來時,39H就是57)

此時,buffer中只有:0AH。

然後,scanft又遇到第二個%c,繼續掃描buffer,得到0aH並送入變數b.

此時buffer中已經無任何資料了

輸出

57 10

程式3

#include "stdio.h"

void main()

{

 char a[100];

 char b[100];

 scanf("%s",a);

 scanf("%s",b);

 printf("%s %s",a,b);

}

鍵盤輸入

abc<回車>

第一次回車後,buffer:61H,62H,63H,0AH。

scanf會按照%s的格式對buffer按位元組順序掃描,當掃描到0AH時,結束掃描(按照%s的要求,空格20H也是掃描結束點)。

然後把掃描到的(除去最後一個判斷掃描截至的位元組0AH)資料直接送入以a為起始地址的字串。

此時,buffer無任何資料了。

def<回車>

第二次回車後,buffer:65H,66H,67H,0AH.掃描的流程與上面的完全一致。

輸出

abc def

程式4

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf("%c",&j);/*注意這裡%前沒有空格*/

 printf("%d",j);

}

鍵盤輸入

1<回車>,

這裡scanf執行了兩次(i==0時,與i==1時),而且每次都是想對j賦值。

第一次scanf,按%c的要求,只掃描buffer中的一個位元組,但是buffer中並不資料,於是要求鍵盤輸入資料到buffer,此時的1<回車>代表向buffer中輸入了:31H,0AH。

然後按%c的要求,只掃描buffer中的一個位元組:31h,並將它直接送入變數j.

此時,buffer中還留下:0AH。

第二次scanf要求鍵盤輸入資料,按%c的要求,只掃描buffer中的一個位元組:0Ah,並將它直接送入變數j.

此時,buffer無資料了。

最後,你用%d格式輸出j的值(0AH換成整型就是10)

輸出

10

程式5

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf(" %c",&j);/*注意這裡%前有一個空格*/

 printf("%d",j);

}

1<回車>2<enter>的情況:

scanf會按照格式控制字串的要求,順序掃描buffer.

但是你其中有一個空格,這個很特殊,我也是第一次發現這個問題(一般我都不會在scanf中加入任何常量字元)

我測試了一下:我發現這個空格有吸收回車(0AH)和空格(20H)的“神奇功效”,吸收之後再要求buffer給一個位元組,直到這個位元組不是0AH或者 20H,此時把這個位元組交給下一個格式字串。

第一次迴圈時遇到格式字串空格,就掃描buffer中的一個位元組,但是buffer中無資料,要求從鍵盤輸入資料:1〈回車〉,buffer中有資料了——31H,0AH。再讀取到位元組31H,scanf發現這個並不是0AH/20H,就把這個位元組31H交給格式字元%c處理。

迴圈結束,此時buffer裡面還有:0AH.

第二次迴圈時遇到格式字串空格,就掃描buffer中的一個位元組——0AH,發現是0AH/20H,於是就要求buffer再來一個位元組。此時buffer裡面已經沒有資料了,要求鍵盤輸入:2<enter>.

buffer中有資料了——32H,0AH。於是再讀一個位元組31H,scanf發現這個並不是0AH/20H,就把這個位元組32H交給格式字元%c處理(j最終得到32H)。

迴圈結束,此時buffer裡面還有:0AH.

程式6

#include "stdio.h"

void main()

{

 int a;

 int b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}

鍵盤輸入

1<回車>

問題5:

你的編譯器VC認為%d資料應該是4個位元組,但是你採用的是%c讀資料,

 scanf("%c",&a);此句讀到的是1的ascii碼:31h.然後把31H直接送入地址&a(而並沒有改寫a的三個高位元組地址)。

 scanf("%c",&b);同理。

你可以用printf("a=%x,b=%x\n",a,b);來驗證我說的。它們的最低位元組肯定是31H,0AH。

PS1:

當你把 int a;int b;放在main()外進行定義時,a,b的初值就是0。此時你會得到正確的結果。

當你把 int a;int b;放在main()內進行定義時,a,b不會被初始化(它們的三個三個高位元組地址的內容是不確定的),你就會得到上面錯誤的結果。(定義的動態變數都不會被初始化,靜態變數會被初始化為0)

PS2:以下也是不正確的用法。

char c;

scanf("%d",&c);/當你用%d給c賦值時,會對從&c開始的連續4個位元組進行賦值。當從buffer得到的值是在一個位元組範圍內(-128~127),下面是可以正常輸出的。但是不管怎樣,這樣做是很危險的——越界。

printf("%d",c);

=================請你測試下這個程式========================

#include "stdio.h"

void main()

{

char c[4],i=4;

scanf("%d",c);/*請輸入258<回車>*/

while(i-->0)

printf("%02x ",c[i]);

printf("\n");

}/*如果得到的結果是00 00 00 01 02就說明我的結論是正確的(258的轉為16進位制數就是00 00 01 02H,然後scanf會把這個數放入以c為起始地址的) 

================以下程式也是======================

#include "stdio.h"

void main()

{

char c,i=4;

char *p=&c;

scanf("%d",&c);/*請輸入258<回車>*/

while(i-->0)

printf("%02x ",p[i]);

printf("\n");

}

提問者評價