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");
}
提問者評價