యూజర్ ఇన్పుట్ (scanf), ఫార్మాట్ స్పెసిఫైయర్లు & బఫర్ హ్యాండ్లింగ్
ఒక ప్రోగ్రామ్ ఎల్లప్పుడూ ఒకే విలువలను స్క్రీన్ మీద చూపిస్తూ ఉంటే (ఉదాహరణకు ఎప్పుడూ a = 10, b = 20 అనుకుంటే) — అది ఒక డెడ్ ప్రోగ్రామ్ లాంటిది! నిజమైన ప్రపంచ సాఫ్ట్వేర్ ఎల్లప్పుడూ జీవంతో ఉండాలి; అంటే కీబోర్డ్ ద్వారా యూజర్ ఇచ్చే ప్రశ్నలు, నంబర్లు, పేర్లను స్వీకరించి క్షణాల్లో ప్రాసెస్ చేసి డైనమిక్ ఫలితాలను అందించాలి.
కానీ C లాంగ్వేజ్లో యూజర్ నుండి ఇన్పుట్ తీసుకోవడం ఇతర ఆధునిక భాషలంత సులభం కాదు! పైథాన్లో input() లేదా జావాలో Scanner కేవలం డేటాను చదివి ఇచ్చేస్తాయి. కానీ C లో ఉపయోగించే scanf() (Scan Formatted) నేరుగా కంప్యూటర్ మెమరీ RAM అడ్రస్లతో ముడిపడి ఉంటుంది. ఇక్కడ మీరు & (Address-of) ఆపరేటర్ ఎందుకు ఇవ్వాలో అర్థం చేసుకోకపోతే ప్రోగ్రామ్ భయంకరమైన Segmentation fault (core dumped) తో క్రాష్ అవుతుంది!
అంతేకాదు, కీబోర్డ్పై నొక్కే Enter కీ వల్ల బ్రౌజర్/టెర్మినల్ బఫర్లో మిగిలిపోయే అదృశ్య న్యూలైన్ క్యారెక్టర్ (\n) బిగినర్లకు రాత్రుళ్ళు నిద్రపట్టకుండా చేసే బఫర్ ట్రాప్లను సృష్టిస్తుంది. ఈ మూడవ అధ్యాయంలో, యూజర్ ఇన్పుట్ మెకానిక్స్, ఫార్మాట్ స్పెసిఫైయర్లు, అడ్రస్ ఆపరేటర్ మరియు బఫర్ హ్యాండ్లింగ్ టెక్నిక్స్ ను లైవ్ డెమోలతో నేర్చుకుందాం!
- Standard I/O Streams:
stdin,stdout,stderrల అంతర్గత ఆర్కిటెక్చర్. - scanf() అనాటమీ: Scan Formatted సింటాక్స్ మరియు కంపైలర్ పనితీరు.
- & (Address-of) ఆపరేటర్ రహస్యం: విలువను పాస్ చేయడం వర్సెస్ మెమరీ అడ్రస్ను పాస్ చేయడం.
- ఫార్మాట్ స్పెసిఫైయర్లు సమగ్ర పట్టిక:
%d,%f,%lf,%c,%sల కచ్చితమైన వినియోగం. - ది డేంజరస్ న్యూలైన్ బఫర్ ట్రాప్: ఒక సంఖ్య తర్వాత క్యారెక్టర్ రీడ్ చేసేటప్పుడు
scanfఎందుకు స్కిప్ అవుతుంది? - బఫర్ క్లియరింగ్ టెక్నిక్స్: లీడింగ్ స్పేస్
" %c"మ్యాజిక్ మరియుgetchar()లూప్. - fflush(stdin) వివాదం: ఇది ఎందుకు C ప్రమాణాల ప్రకారం Undefined Behavior?
- మల్టిపుల్ ఇన్పుట్స్: ఒకే
scanfలో స్పేస్, కామాలతో బహుళ వేరియబుల్స్ స్వీకరించడం. - ఇన్పుట్ వాలిడేషన్ (scanf Return Value): యూజర్ అక్షరాలు టైప్ చేస్తే కనిపెట్టే ప్రొఫెషనల్ కోడ్.
- బిగినర్స్ చేసే ఘోరమైన తప్పులు: & మర్చిపోవడం, డబుల్ కోసం
%fవాడటం. - ఇంటరాక్టివ్ క్విజ్: 5 లోతైన ప్రాక్టికల్ ప్రశ్నలు & వివరణలు.
ఆపరేటింగ్ సిస్టమ్ ఒక C ప్రోగ్రామ్ను ప్రారంభించినప్పుడు, దానికి ఆటోమేటిక్గా 3 ప్రామాణిక డాక్యుమెంట్ స్ట్రీమ్లను (Standard Streams) కేటాయిస్తుంది:
| స్ట్రీమ్ (Stream) | సాధారణ డివైస్ | ఫైల్ డిస్క్రిప్టర్ | ప్రధాన బాధ్యత |
|---|---|---|---|
| stdin (Standard Input) | కీబోర్డ్ (Keyboard) | 0 |
యూజర్ టైప్ చేసిన డేటాను ప్రోగ్రామ్కు ప్రవహింపజేస్తుంది. |
| stdout (Standard Output) | మానిటర్ స్క్రీన్ (Terminal) | 1 |
printf() ద్వారా అవుట్పుట్ను స్క్రీన్పై ముద్రిస్తుంది. |
| stderr (Standard Error) | మానిటర్ స్క్రీన్ | 2 |
ఎర్రర్ మెసేజ్లను బఫర్ లేకుండా తక్షణమే స్క్రీన్పై చూపిస్తుంది. |
ఇన్పుట్ బఫర్ (Input Buffer) అంటే ఏమిటి? మీరు కీబోర్డుపై టైప్ చేసిన ప్రతి అక్షరం వెంటనే ప్రోగ్రామ్కు వెళ్ళదు! అది తాత్కాలికంగా ఆపరేటింగ్ సిస్టమ్ యొక్క మెమరీ క్యూలో (Buffer) నిల్వ ఉంటుంది. మీరు ఎప్పుడైతే Enter కీ ప్రెస్ చేస్తారో — అప్పుడు మాత్రమే ఆ బఫర్ నుండి డేటా మీ C ప్రోగ్రామ్లోని scanf() కి చేరుతుంది.
scanf అనేది Scan Formatted కు సంక్షిప్త రూపం. ఇది <stdio.h> హెడర్ ఫైల్లో నిర్వచించబడిన ఒక ఇన్పుట్ ఫంక్షన్:
#include <stdio.h>
int main(void) {
int userAge;
printf("దయచేసి మీ వయస్సు నమోదు చేయండి: ");
// scanf లో %d తో పాటు &userAge తప్పనిసరి!
scanf("%d", &userAge);
printf("మీ వయస్సు: %d సంవత్సరాలు.\n", userAge);
return 0;
}
పై కోడ్లో scanf("%d", &userAge); ఎలా పనిచేస్తుంది?
"%d": కీబోర్డ్ నుండి రాబోయే డేటా ఒక పూర్ణాంకం (Integer) అని కంపైలర్కు సూచిస్తుంది.&userAge: ఆ పూర్ణాంకాన్ని రాసి పెట్టవలసిన RAM మెమరీ అడ్రస్ నుscanfకి అందజేస్తుంది.
చాలామంది బిగినర్స్ అడుగుతుంటారు: "printf లో userAge అని రాస్తే సరిపోయింది కదా, scanf లో ఈ & గుర్తు ఎందుకు రాయాలి?"
దీనిని ఒక వాస్తవ ఉదాహరణతో అర్థం చేసుకుందాం: మీ స్నేహితుడు మీ ఇంటి గోడకు పెయింట్ వేయాలి అనుకోండి. మీరు అతనికి మీ ఇంటి ఫోటో ఇస్తే సరిపోతుందా? సరిపోదు! మీ ఇంటి అడ్రస్ (చిరునామా) ఇవ్వాలి, అప్పుడే అతను మీ ఇంటికి వచ్చి గోడపై రంగు వేయగలడు!
- printf("..", userAge): కేవలం వేరియబుల్లో ఉన్న విలువను (Value Copy) చదవాలి కాబట్టి అడ్రస్ అక్కర్లేదు; కాపీ ఇస్తే చాలు.
- scanf("..", &userAge):
scanfఫంక్షన్ మెమరీ లొకేషన్లోకి కొత్త విలువను రాయాలి (Modify చేయాలి). C లో ఫంక్షన్లు నేరుగా వేరియబుల్స్ మార్చలేవు (Call by Value); అందుకే ఆ వేరియబుల్ ఉన్న మెమరీ అడ్రస్&userAgeని పంపాలి!
ఒకవేళ మీరు
scanf("%d", userAge); అని రాస్తే — వేరియబుల్లోని గార్బేజ్ విలువనే (ఉదా: 0 లేదా 142345) scanf మెమరీ అడ్రస్గా భావించి, ఆ నిషేధిత ప్రాంతంలో డేటా రాయడానికి ప్రయత్నిస్తుంది. వెంటనే ఆపరేటింగ్ సిస్టమ్ సెక్యూరిటీని కాపాడటానికి మీ ప్రోగ్రామ్ను బలవంతంగా చంపేస్తుంది (Crash చేస్తుంది)! దీనినే Segmentation Fault (Core Dumped) అంటారు!
వివిధ డేటా టైప్స్ ఇన్పుట్ తీసుకోవడానికి మరియు ప్రింట్ చేయడానికి ఉపయోగించే అధికారిక ఫార్మాట్ స్పెసిఫైయర్ల సూచిక:
| డేటా టైప్ | ఇన్పుట్ (scanf) | అవుట్పుట్ (printf) | ఉదాహరణ ఉపయోగం |
|---|---|---|---|
int (Signed Integer) |
%d లేదా %i |
%d |
scanf("%d", &n); |
unsigned int |
%u |
%u |
scanf("%u", &pos); |
short |
%hd |
%hd |
scanf("%hd", &s); |
long |
%ld |
%ld |
scanf("%ld", &val); |
long long |
%lld |
%lld |
scanf("%lld", &bigNum); |
float |
%f |
%f |
scanf("%f", &rate); |
double (అత్యంత కీలకం!) |
%lf (తప్పనిసరిగా lf వాడాలి) |
%f లేదా %lf |
scanf("%lf", &pi); |
char (Single character) |
%c |
%c |
scanf("%c", &ch); |
char[] (String / Word) |
%s (దీనికి & అవసరం లేదు!) |
%s |
scanf("%s", name); |
printf లో float మరియు double రెండింటికీ %f వాడవచ్చు. కానీ scanf లో double చదవడానికి తప్పనిసరిగా %lf (Long Float) మాత్రమే వాడాలి! పొరపాటున %f వాడితే మెమరీలో సగం బైట్లు మాత్రమే నిండి తీవ్రమైన గార్బేజ్ వస్తుంది!
C ప్రోగ్రామింగ్ నేర్చుకునే ప్రతి బిగినర్ ఎదుర్కొనే అత్యంత వింతైన, భయానకమైన బగ్ ఇది. క్రింది చిన్న కోడ్ చూడండి:
#include <stdio.h>
int main(void) {
int id;
char section;
printf("రోల్ నంబర్ నమోదు చేయండి: ");
scanf("%d", &id);
printf("సెక్షన్ లెటర్ నమోదు చేయండి (A, B, C): ");
scanf("%c", §ion); // ఇక్కడ ప్రోగ్రామ్ ఆగదు! ఆటోమేటిక్గా స్కిప్ అయిపోతుంది!
printf("ID: %d, సెక్షన్: [%c]\n", id, section);
return 0;
}
ఈ బగ్ ఎందుకు జరిగింది? (Under the Hood Postmortem):
మీరు రోల్ నంబర్ 25 అని టైప్ చేసి కీబోర్డ్పై Enter నొక్కారు. అప్పుడు ఇన్పుట్ బఫర్లోకి రెండు క్యారెక్టర్లు వెళ్ళాయి: '2', '5', మరియు '
' (న్యూలైన్ క్యారెక్టర్).
- మొదటి
scanf("%d", &id)నంబర్లను మాత్రమే చదువుతుంది; అది25ని తీసుకుని,' 'ను బఫర్లోనే వదిలేసింది! - తర్వాతి
scanf("%c", §ion)రాగానే — అది మీ కోసం ఆగదు! బఫర్లో అప్పటికే మిగిలి ఉన్న ఆ' 'ను ఒక క్యారెక్టర్గా భావించి వెంటనే చదివేసుకుంది! దాంతోsection = ' 'అయిపోయి ప్రోగ్రామ్ స్కిప్ అయిపోయింది!
ఈ బఫర్ ట్రాప్ను పరిష్కరించడానికి ఆధునిక C లో రెండు సురక్షితమైన మార్గాలు ఉన్నాయి:
పరిష్కారం 1: లీడింగ్ స్పేస్ ట్రిక్ (The Leading Space Magic) — బెస్ట్ & సింప్లెస్ట్!
scanf ఫార్మాట్ స్ట్రింగ్లో %c కి ముందు ఒక చిన్న ఖాళీ స్పేస్ ఇవ్వండి: scanf(" %c", §ion);
ఆ ఒక్క చిన్న స్పేస్ కంపైలర్కు చెబుతుంది: "బఫర్లో ఎన్ని ఖాళీ స్పేస్లు, ట్యాబ్లు లేదా
న్యూలైన్లు ఉన్నా వాటన్నింటినీ ముందు విస్మరించి (Ignore చేసి), ఆ తర్వాత వచ్చే అసలైన అక్షరాన్ని మాత్రమే చదువు!"
పరిష్కారం 2: getchar() తో బఫర్ క్లియర్ చేయడం:
నంబర్ రీడ్ చేసిన వెంటనే getchar() పిలిస్తే అది బఫర్లోని
ని మింగేస్తుంది:
#include <stdio.h>
int main(void) {
int id;
char section;
printf("రోల్ నంబర్ ఇవ్వండి: ");
scanf("%d", &id);
// పరిష్కారం: %c కి ముందు ఒక స్పేస్ ఉంచండి!
printf("సెక్షన్ ఇవ్వండి (A/B/C): ");
scanf(" %c", §ion);
printf("విజయవంతంగా స్వీకరించబడింది -> ID: %d, సెక్షన్: %c\n", id, section);
return 0;
}
చాలా కాలేజీ ల్యాబ్స్లో పాత Turbo C++ లో
fflush(stdin); రాయిస్తారు. కానీ ANSI/ISO C ప్రమాణాల ప్రకారం fflush అనేది కేవలం అవుట్పుట్ స్ట్రీమ్స్ (stdout) కోసం మాత్రమే నిర్దేశించబడింది! ఇన్పుట్ స్ట్రీమ్ (stdin) పై fflush వాడటం అధికారికంగా Undefined Behavior (చెల్లని కోడ్). ఇది Linux లేదా GCC లో పనిచేయదు. ఎల్లప్పుడూ " %c" మాత్రమే వాడాలి!
యూజర్ నుండి ఒకేసారి అనేక విలువలను స్వీకరించడానికి ఒకే scanf లో బహుళ ఫార్మాట్ స్పెసిఫైయర్లను అమర్చవచ్చు:
#include <stdio.h>
int main(void) {
int day, month, year;
printf("మీ పుట్టిన తేదీని DD/MM/YYYY ఫార్మాట్లో నమోదు చేయండి: ");
// స్లాష్ (/) లతో సహా ఫార్మాట్ మ్యాచ్ చేయడం
scanf("%d/%d/%d", &day, &month, &year);
printf("మీరు నమోదు చేసిన తేదీ: %02d-%02d-%04d\n", day, month, year);
return 0;
}
యూజర్ 15/08/1947 అని టైప్ చేస్తే — scanf ఆ స్లాష్ గుర్తులను ఆటోమేటిక్గా స్కిప్ చేసి, మూడు సంఖ్యలను సరైన వేరియబుల్స్లో నింపుతుంది!
యూజర్ పేరు లేదా చిరునామా వంటి పదాలను చదవడానికి %s ఉపయోగిస్తాం. కానీ scanf("%s", name) కి ఒక పెద్ద పరిమితి ఉంది: ఇది స్పేస్ (Space) రాగానే చదవడం ఆపేస్తుంది!
ఉదాహరణకు యూజర్ "రమేష్ నాయక్" అని టైప్ చేస్తే — scanf("%s") కేవలం "రమేష్" మాత్రమే తీసుకుంటుంది; స్పేస్ తర్వాత ఉన్న "నాయక్" ని బఫర్లోనే వదిలేస్తుంది!
#include <stdio.h>
int main(void) {
char fullName[50];
printf("మీ పూర్తి పేరు నమోదు చేయండి (స్పేస్లతో సహా): ");
// fgets(వేరియబుల్, సైజు, stdin) వాడటం అత్యంత సురక్షితం!
fgets(fullName, sizeof(fullName), stdin);
printf("స్వాగతం, %s", fullName);
return 0;
}
scanf("%s", name); లో name ముందర & గుర్తు పెట్టాల్సిన అవసరం లేదు. ఎందుకంటే C లాంగ్వేజ్లో ఒక అర్రే లేదా స్ట్రింగ్ పేరు స్వతహాగా దాని మొదటి ఎలిమెంట్ యొక్క మెమరీ అడ్రస్నే సూచిస్తుంది (Array Decay). దీని గురించి చాప్టర్ 14 & 16 లో విపులంగా నేర్చుకుందాం.
చాలామందికి తెలియని విషయం: scanf() ఒక రిటర్న్ విలువను ఇస్తుంది!
scanf ఎన్ని ఇన్పుట్ విలువలను విజయవంతంగా చదివిందో ఆ సంఖ్యను రిటర్న్ చేస్తుంది. ఉదాహరణకు scanf("%d", &age) ఒక సంఖ్యను విజయవంతంగా చదివితే అది 1 ని రిటర్న్ చేస్తుంది. ఒకవేళ యూజర్ సంఖ్యకు బదులుగా "abc" అని అక్షరాలు టైప్ చేస్తే అది 0 ని రిటర్న్ చేస్తుంది!
#include <stdio.h>
int main(void) {
int balance;
printf("డిపాజిట్ మొత్తం నమోదు చేయండి: ");
// scanf రిటర్న్ విలువ 1 కాకపోతే యూజర్ తప్పు డేటా ఇచ్చాడని అర్థం!
if (scanf("%d", &balance) != 1) {
printf("❌ ఎర్రర్: దయచేసి సరైన సంఖ్యను మాత్రమే నమోదు చేయండి!\n");
return 1;
}
printf("✅ విజయవంతంగా ₹%d డిపాజిట్ చేయబడింది!\n", balance);
return 0;
}
scanf("%d", num); // తప్పు! Segmentation Fault వస్తుంది!
స్ట్రింగ్స్ తప్ప మిగతా అన్ని ప్రాథమిక డేటా టైప్స్కు (int, float, char) తప్పనిసరిగా &num అని అడ్రస్ ఇవ్వాలి.
scanf("%d\n", &num); // ఘోరమైన తప్పు! ప్రోగ్రామ్ ఫ్రీజ్ అయిపోతుంది!
scanf లోపల ఎప్పుడూ
రాయకూడదు! అలా రాస్తే యూజర్ ఎన్నిసార్లు ఎంటర్ నొక్కినా ప్రోగ్రామ్ ఆగకుండా తదుపరి నాన్-వైట్స్పేస్ క్యారెక్టర్ కోసం నిరంతరం ఎదురుచూస్తూనే ఉంటుంది.
double salary;
scanf("%f", &salary); // తప్పు! double కోసం తప్పనిసరిగా %lf వాడాలి!
float 4 బైట్లు, double 8 బైట్లు. %f ఇస్తే కంపైలర్ 4 బైట్లు మాత్రమే రాస్తుంది, మిగిలిన 4 బైట్లు కరప్ట్ అయిపోతాయి.
ఈ మూడవ అధ్యాయంలో మీరు నేర్చుకున్న ఇన్పుట్ మరియు బఫర్ భావనలను పరీక్షించుకోవడానికి క్రింది ప్రశ్నలపై క్లిక్ చేయండి:
❓ ప్రశ్న 1: scanf లో వేరియబుల్ పేరుకు ముందు & (Address-of) ఎందుకు తప్పనిసరిగా ఇవ్వాలి?
scanf ఫంక్షన్ వేరియబుల్ యొక్క మెమరీ లొకేషన్లో నేరుగా డేటాను రాయాలి (Modify చేయాలి). C లో కాల్-బై-వాల్యూ విధానం ఉంటుంది కాబట్టి, అసలైన వేరియబుల్ ఉన్న RAM అడ్రస్ను పంపడానికి & అవసరం.
❓ ప్రశ్న 2: ఒక నంబర్ రీడ్ చేసిన తర్వాత క్యారెక్టర్ రీడ్ చేసేటప్పుడు " %c" లో స్పేస్ ఎందుకు పెడతాం?
Enter కీ వల్ల బఫర్లో మిగిలిపోయిన న్యూలైన్ క్యారెక్టర్ (
) ను విస్మరించడానికి (Ignore చేయడానికి).
❓ ప్రశ్న 3: double డేటా టైప్ ఉన్న వేరియబుల్ను scanf ద్వారా రీడ్ చేయడానికి ఏ ఫార్మాట్ స్పెసిఫైయర్ వాడాలి?
%f వాడితే మెమరీ కరప్షన్ జరుగుతుంది.
❓ ప్రశ్న 4: scanf("%d %d", &a, &b); రన్ చేసినప్పుడు యూజర్ రెండు సరైన సంఖ్యలు ఇస్తే scanf రిటర్న్ చేసే సంఖ్య ఎంత?
scanf విజయవంతంగా చదివిన ఇన్పుట్ ఐటమ్ల సంఖ్యను రిటర్న్ చేస్తుంది.
❓ ప్రశ్న 5: fflush(stdin); వాడటం ఎందుకు చెడ్డ ప్రాక్టీస్?
fflush కేవలం అవుట్పుట్ కోసం మాత్రమే; ఇన్పుట్ పై వాడటం అధికారికంగా Undefined Behavior.
పైన వివరించిన scanf కోడ్ మరియు బఫర్-సేఫ్ " %c" కోడ్ను మన ఆన్లైన్ C కంపైలర్లో ఓపెన్ చేసి స్వయంగా ఇన్పుట్స్ ఇచ్చి టెస్ట్ చేయండి. ఇంటరాక్టివ్ టెర్మినల్ ద్వారా లైవ్ C ప్రోగ్రామింగ్ అనుభవాన్ని పొందండి!
📌 క్విక్ సారాంశం (Key Takeaways):
- stdin: కీబోర్డ్ ఇన్పుట్ బఫర్ ద్వారా ప్రవహించే ప్రామాణిక స్ట్రీమ్.
- & ఆపరేటర్: మెమరీ అడ్రస్ లొకేషన్ను సూచిస్తుంది; scanf కి ఇది ప్రాణాధారం.
- బఫర్ ట్రాప్: Enter కీ మిగిల్చే
ను క్లియర్ చేయడానికి" %c"లో స్పేస్ ఉంచాలి. - double: దీనికి తప్పనిసరిగా
%lfవాడాలి. - వాలిడేషన్:
scanfరిటర్న్ విలువను చెక్ చేయడం ద్వారా తప్పుడు ఇన్పుట్లను అడ్డుకోవచ్చు.