How would I best take input in a program that asks the user to enter a student name separated by a space and then the student score, EX:
zach 85
Because of the null terminator, will there be two enters that i will have to account for? I'm already using two scanfs in my program.
int main()
{
const int row = 5;
const int col = 10;
int i;
char names[row][col];
int scores[row];
int total;
// Read names and scores from standard input into names and scores array
// Assume that each line has a first name and a score separated by a space
// Assume that there are five such lines in the input
// 10 points
for(int i = 0; i<row; i++)
{
printf("Enter student name: \n");
scanf("%s",&names);
scanf("%s", &scores);
}
// Print the names and scores
// The print out should look the same as the input source
// 10 points
for(int i = 0; i<row; i++)
{
printf( "%s %d \n", names[i] /*, scores[i] */ );
}
}
Your type for scores (int scores[row];) does not correspond to your attempt to read scores with scanf (scanf("%s", &scores);). The "%s" conversion specifier is for converting whitespace separated strings, not integers. The "%d" conversion specifier is provided for integer conversions.
Before looking at specifics. Any time you have a coding task of coordinating differing types of data as a single unit, (e.g. student each with a name (char*) and a score (int), you should be thinking about using a struct containing the name and score as members. That way there is only a single array of struct needed rather than trying to coordinate multiple arrays of differing types to contain the same information.
Also, don't skimp on buffer size for character arrays. You would rather be 10,000 characters too long than 1-character too short. If you think your maximum name is 10-16 character, use a 64-char (or larger) buffer to insure you can read the entire line of data - eliminating the chance that a few stray characters typed could result in characters remaining unread in stdin.
A simple stuct is all that is needed. You can add a typedef for convenience (to avoid having to type struct name_of_struct for each declaration or parameter), e.g.
#include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
Now you have a structure that contains your student name and score as a single unit rather than two arrays, one char and one int you have to deal with.[1]
All that remains is declaring an array of student_t for use in your code, e.g.
int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
With the array of struct declared, you can turn to your input handling. A robust way of handling input is to loop continually, validating that you receive the input you expects on each iteration, handling any errors that arise (gracefully so that your code continues), and keeping track of the number of inputs made so that you can protect your array bounds and avoid invoking Undefined Behavior by writing beyond the end of your array.
You could begin your input loop, prompting and reading your line of input with fgets as mentioned in my comment. That has multiple advantages over attempting each input with scanf. Most notably because what remains unread in the input buffer (stdin here) doesn't depend on the conversion specifier used. The entire line (up to and including the trailing '\n') is extracted from the input buffer and place in buffer you give fgets to fill. You can also check if the user simply presses Enter which you can use to conveniently indicate end of input, e.g.
for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
(note you can (and should) additionally check the string length of the buffer filled to (1) check that the last character read is '\n' ensuring the complete line was read; and (2) if the last char isn't '\n' checking whether the length is equal to the maximum length (-1) indicating that characters may be left unread. (that is left to you)
Now that you know you have a line of input and it's not empty, you can call sscanf to parse the line into the name and score for each student while handling any failure in conversion gracefully, e.g.
/* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
If you are paying attention, you can see one of the benefits of using the fgets and sscanf approach from a robustness standpoint. You get independent validations of (1) the read of user input; and (2) the separation (or parsing) of that input into the needed values. A failure in either case can be handled appropriately.
Putting all the pieces together into a short program, you could do the following:
#include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
/* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
for (int i = 0; i < n; i++) /* output stored names and values */
printf ("%-16s %3d\n", student[i].name, student[i].score);
}
Example Use/Output
When ever you write an input routine -- Go Try and Break It!. Intentionally enter invalid data. If your input routine breaks -- Go Fix It!. In the code as noted the only check left for you to implement and handle is input greater than COL number of characters (e.g. the cat steps on the keyboard). Exercise your input:
$ ./bin/studentnamescore
[note; press Enter alone to end input]
Enter student name: zach 85
Enter student name: the dummy that didn't pass
error: invalid input, conversion failed.
Enter student name: kevin 96
Enter student name: nick 56
Enter student name: martha88
error: invalid input, conversion failed.
Enter student name: martha 88
Enter student name: tim 77
array full - input complete.
zach 85
kevin 96
nick 56
martha 88
tim 77
While you can use two separate arrays, a single array of struct is a much better approach. Look things over and let me know if you have further questions.
footnotes:
Be aware that POSIX specifies that names ending with suffix _t are reserved for its use. (size_t, uint64_t, etc...) Also be aware you will see that suffix used in common practice. So check before you make up your own (but we no there is no POSIX student_t type).
You are almost there. However you have to make sure you do things cleanly Let's just do this step by step:
Step 1, get ONE name and ONE score
#include <stdio.h>
#define MAX_NAME_LENGTH 30
int main() {
char name[MAX_NAME_LENGTH+1]; /* an array of characters making up ONE name (+1 for terminating NUL char in case of max-length name) */
unsigned int score; /* a score */
scanf("%30s", name); /* scan a name (assuming NO spaces in the name)*/
/* also notice that name has no & in front of it because it already IS a pointer to the array name[MAX_NAME_LENGTH] */
scanf("%u", &score);
printf("%s scored %u in the test\n", name, score);
return 0;
}
(See it run at http://tpcg.io/jS3woS)
STEP 2 -- Iterate to get multiple scores
Now let's read in 5 pairs and then print out 5 pairs.
#include <stdio.h>
#define MAX_NAME_LENGTH 30
/* i called rows iterations here just to provide contrast */
/* you can call them ROWS if you want but it then creates a confusion about name length */
#define ITERATIONS 5
int main() {
char name[ITERATIONS][MAX_NAME_LENGTH+1]; /* an array of names where each name is MAX_NAME_LENGTH long (notice the order) */
unsigned int score[ITERATIONS]; /* score */
int i;
for(i = 0; i < ITERATIONS; i++ ) {
scanf("%30s", name[i]); /* scan a name (assuming NO spaces in the name)*/
/* notice that name[i] has no & in front of it because name[i] is the pointer to the i-th row */
scanf("%u", &score[i]);
}
for(i = 0; i < ITERATIONS; i++ ) {
printf("%s scored %u in the test\n", name[i], score[i]);
}
return 0;
}
See it in action here (http://tpcg.io/iTj4ag)
first look scores and names are is defined as arrays so
scanf("%s",names[i]);
scanf("%s", &scores[i]);
second scores are int so "%d" instead of "%s"
scanf("%s",names[i]);
scanf("%d", &scores[i]);
third, you've already defined int i; so the one in for loop is not really making any sense, do it only at a single place.
fourth, if your input names contains spaces scanf is not the right option
from manual pages of scanf
Each conversion specification in format begins with either the character '%' or the character sequence "%n$" (see below for the distinction) followed by:
· An optional decimal integer which specifies the maximum field width. Reading of characters stops either when this maximum is reached or when a non‐
matching character is found, whichever happens first. Most conversions discard initial white space characters (the exceptions are noted below), and
these discarded characters don't count toward the maximum field width. String input conversions store a terminating null byte ('\0') to mark the end
of the input; the maximum field width does not include this terminator.
few more points you might want to restrict the number of characters to be scanned during scanf to certain limit example "%20s"
Related
I want to write a C program to add the numbers given by the user as long as they want... can anyone fix this program?
I tried to use a do-while loop.
Any other suggestions to improve my code?
I am unable to end the loop.
#include <stdio.h>
int main()
{
int x=0, sum = 0, y=0, fu;
printf("first number you want to add:\n");
scanf("%d", &x);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
do
{
printf("do you want to add numbers further? \nEnter 0:Yes or 1:No: \n");
scanf("%d", &fu);
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
while(fu>0);
sum=x;
printf("Sum of all integers = %d\n", sum);
return 0;
}
Ask for the 3rd and further numbers in an if and modify your while:
scanf("%d", &fu);
if(fu == 0) {
printf("next number you want to add:\n");
scanf("%d", &y);
x=x+y;
}
}
while(fu == 0);
Your prompt says:
Enter 0:Yes or 1:No:
so you need to continue that loop if 0 was entered:
while(fu == 0);
Also, you don't need to take another y after non-0 input.
The key to taking any input, either from the user, or from a file, is to validate every single input by checking the return. You cannot blindly use a variable holding input until you know whether the input succeeded or failed. Otherwise, if the input fails and you use a variable whose value is indeterminate, you invoke undefined behavior.
Also, if you are using a formatted input function such as scanf(), if a matching failure occurs, character extraction from stdin ceases at that point and the characters causing the failure are left in stdin -- unread, just waiting to bite you again on your next attempted input.
Instead, if you use a line-oriented input function such as fgets() or POSIX getline(), you read an entire line at a time. You can simply call sscanf() on the buffer filled by fgets() to convert a numeric input to an integer value. That way it does not matter if the conversion succeeds or fails, you do not leave anything unread in the input stream.
Just as you must validate every input, you so too must validate every conversion. Whether using sscanf() or strtol(), etc... a failure to validate every conversion will likely lead to undefined behavior when you fail to detect the conversion failure.
Another benefit of using fgets() or getline() is they read and store the '\n' from the user pressing Enter. So rather than having to prompt "do you want to add numbers further? \nEnter 0:Yes or 1:No: \n" and have to worry about yet another input and conversion -- you simply check if Enter was pressed on an empty line to know the user completed input (e.g. the first character in the buffer filed by fgets() is the '\n' character).
You also have to handle an invalid input correctly. What if the user enters "bananas" instead of a number?
Putting it altogether, you could do something similar to:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer (character array) to hold all user input */
int sum = 0, n = 0; /* sum and count of numbers */
puts ("press ENTER alone to exit:\n"); /* display instructions */
while (1) { /* loop continually */
int tmp; /* temporary int to add to sum */
/* prompt based on 1st or subsequent number */
fputs (n ? "next number : " : "first number : ", stdout);
/* read and validate input, break on EOF or empty line */
if (!fgets (buf, MAXC, stdin) || *buf == '\n') {
puts ("---------------------");
break;
}
/* validate conversion to int */
if (sscanf (buf, "%d", &tmp) == 1) { /* on success */
sum += tmp; /* add to sum */
n += 1; /* increment count */
}
else /* handle error */
fputs (" error: invalid integer input.\n", stderr);
}
printf (" sum : %d\n", sum); /* output final sum */
}
Example Use/Output
$ ./bin/sum
press ENTER alone to exit:
first number : 10
next number : -20
next number : 30
next number : -40
next number : bananas
error: invalid integer input.
next number : 50
next number :
---------------------
sum : 30
There are several ways to approach this, and if you wanted the user to be able to enter more than one-number per-line, you could parse buf with strtol() to extract all values. (you can do the same with sscanf() using an offset from the beginning of the string and the characters consumed on each conversion from the "%n" specifier) Many ways to go.
Let me know if you have further questions.
Basic encryption with input validation, program works as needed if input is within defined parameters requiring user input to advance through each stage, if input is larger than defined parameters, else should trigger saying password is invalid but instead runs through the entire program without user input to trigger advance.
#include <stdio.h> //library containing built in functions for C
#include <stdlib.h> // contains general purpose library for C
#include <string.h> //library containing methods for manipulating strings
int main()
{
// creates character input array of size 5
char input[20];
//initialize variable i
int i;
//initialize variable length
int length;
//prints the phrase
//printf("Your pass phrase is: ");
// used to read input string, gets replaced with fgets to secure input to array length
//fgets(input,11,stdin);
//assigns input to array length
//length = strlen(input);
//prints the phrase
printf("Your pass phrase is: ");
fgets(input,11,stdin);
//assigns input to array length
length = strlen(input);
// if loop that if entered text is longer than 0 but shorter than 6 will encrypt data
if ((length > 0 && length <=10)){
// encrypts array iteratting through elements
for (i=0; i<length; i++)
// uses bitwise xor cipher to encrypt using 0x7f which shifts the characters out of the standard ascii range
input[i] = 0x7F ^ input[i];
// prints the encrypted text in an unreadable format
puts("To see the encrypted text press enter: ");
getchar();
// iterates through the area printing the encrypted characters.
for(i=0;i<length; i++)
putchar(input[i]);
// uses xor cipher to shift data back into array by undoing the shift caused by 0x7f
for(i=0; i<length; i++)
input[i] = 0x7F ^ input[i];
// prints the now readable array
puts("\nTo see recovered text press enter:");
getchar();
//iterates through the array printing the contained characters
for(i=0; i<length; i++)
putchar(input[i]);
}
// prints the following phrase if the array is empty.
else{
puts("User input has been checked and a valid pass phrase was not entered.");
}
return 0;
}
I have tried to decipher what your objective was with the code, and I've read the tea-leaves and arrived at the conclusion (perhaps wrongly) that the crux of your question is aimed at input and branching control that on the encryption itself.
The "Confucius" comment was only half a half-hearted suggestion as the "Never Skimp on Buffer Size" goes to the heart of your ability to control what branches are taken by prompting the user for directions. Why?
If, as in your original code, you have a buffer size of 20, and tell fgets to read at most 10 chars, then if the user enters anything more than 10 chars, the remainder are left in stdin unread.
any characters that are left in stdin unread, will happily be used as input for each of your later getchar() statements causing you to lose complete control over how your code branches.
using an adequately sized buffer and allowing fgets to consume a complete line at a time will ensure there are no characters left unread in stdin (your input buffer). You will find this to be key on taking most user input in C.
So ensuring you have an adequately sized buffer and consume a line of input at each time is fundamental to being able to control where your code goes next.
An alternative to providing a buffer capable of holding each line of input is to read the wanted number of characters and then manually emptying stdin before the next input. That can be as easy as reading characters until a '\n' or EOF is reached. But... there is a catch. You have to know that characters remain before attempting to empty stdin with whatever function you use (e.g. getchar() will block -- waiting for input until input is present to be read). If you attempt to empty stdin in this manner when there is nothing to be emptied, your user will be left staring at a blinking cursor wondering what to do.
Back to your code. The logic is a bit hard to follow, but from the included comments, it appears that you consider a correct passphrase for purposes of encrypting it, a phrase with 1-5 characters. An empty passphrase or a passphrase with 6 or more characters is considered invalid. (it doesn't matter what the actual numbers are from a passphase standpoint -- this is an exercise about input and branching control)
If I understand the 1-5 char (encrypt) or 0 or 6 or more (invalid) logic, then you will want to adjust the structure of your code to follow that a bit closer. From an overview standpoint, you either have a good passphrase to encrypt or you don't. That would control the primary branches in your code. You can provide the logic in a single if {..} else {..} statement, e.g.
/* if input is longer than 0 but shorter than 6, encrypt data */
if (length && length < 6) {
/* all your good pass phrase code goes here */
}
else { /* invalid pass phrase to begin with */
fputs ("error: pass phrase empty or greater than 5 chars.\n", stderr);
return 1;
}
Contrast this above with the multiple independent statement groups you have in your example and it should make sense why you were having trouble with the decision tree in your code.
Now on to your prompts to the user to press Enter. When you attempt to catch that input with a single getchar() it is horribly fragile and subject to being skipped if any characters exist in stdin and it has the same problem of leaving characters in stdin unread if more than Enter is pressed. Here, just declare another character array to use for temporary input the same size as your input[] array. Now instead of getchar(), just use fgets() again even to capture the Enter alone.
(which it is quite capable of doing, since it reads and include the '\n' in the buffer it fills, it will not block waiting on input like getchar() will)
Concerned about size in using 512-bytes in two buffers -- don't be. Most OS's provide a minimum of 1-Meg of stack space, so that 512-bytes represents only 0.048% of the stack storage you have available. (you still have 1048576 - 512 = 1048064 byte of stack space avaialble)
With that change, your code can be rewritten to contain all logic related to handling a good/encrypted password within the first if {...} block, e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 256 /* if you need a constant, #define one (or more) */
int main (void) {
char input[MAXC], /* buffer to hold pass phrase */
tmp[MAXC]; /* temporary buffer for [enter] input */
size_t i, /* declares an UNINTIALIZED size_t */
length; /* (ditto) */
fputs ("Your pass phrase is: ", stdout); /* no conversion, fputs is fine */
if (!fgets (input, MAXC, stdin)) {
fputs ("(user canceled input)\n", stderr);
return 1;
}
/* remove '\n' by overwriting with '\0' (if present)
* saving length of input in length
*/
input[(length = strcspn(input, "\n"))] = 0;
/* if input is longer than 0 but shorter than 6, encrypt data */
if (length && length < 6) {
int encrypted = 0; /* flag keeping state of if pw encrypted */
for (i = 0; i < length; i++) /* encrypt the pass phrase */
input[i] ^= 0x7f;
encrypted = 1; /* set flag true */
/* print encrypted text in an hex format (change as desired) */
fputs ("\nTo see the encrypted text press [enter]: ", stdout);
if (fgets (tmp, MAXC, stdin) && *tmp == '\n') {
for (i = 0; i < length; i++)
printf (" %02x", input[i]);
putchar ('\n');
}
/* decrypt restoring plain-text pass phrase */
fputs ("\ndecrypted pass phrase, press [enter]: ", stdout);
if (fgets (tmp, MAXC, stdin) && *tmp == '\n') {
for (i = 0; i < length; i++)
input[i] ^= 0x7f;
encrypted = 0; /* set flag false after decryption */
}
else { /* if user pressed any other key (or generated EOF) */
fputs ("error: user chose not to decrypt pass phrase.\n", stderr);
return 1;
}
/* output decrypted plain-text pass pharase (if decrypted) */
fputs ("\nTo see recovered text press [enter]: ", stdout);
if (fgets (tmp, MAXC, stdin) && *tmp == '\n' && !encrypted) {
for (i = 0; i < length; i++)
putchar (input[i]);
putchar ('\n');
}
}
else { /* invalid pass phrase to begin with */
fputs ("error: pass phrase empty or greater than 5 chars.\n", stderr);
return 1;
}
return 0;
}
(note: above the encrypted flag is simply used to hold the state of whether the contents of input[] is currently encrypted or not to provide an additional conditional check before you attempt to print the decrypted passphrase)
Example Use/Output
Valid passphrse case:
$ ./bin/pass0-5
Your pass phrase is: abcde
To see the encrypted text press [enter]:
1e 1d 1c 1b 1a
decrypted pass phrase, press [enter]:
To see recovered text press [enter]:
abcde
User chooses not to decrypt case:
$ ./bin/pass0-5
Your pass phrase is: abcde
To see the encrypted text press [enter]:
1e 1d 1c 1b 1a
decrypted pass phrase, press [enter]: No, I don't want to!
error: user chose not to decrypt pass phrase.
Passphrase too long case:
$ ./bin/pass0-5
Your pass phrase is: abcdef
error: pass phrase empty or greater than 5 chars.
Passphrase empty case:
$ ./bin/pass0-5
Your pass phrase is:
error: pass phrase empty or greater than 5 chars.
I don't know if I read the tea-leaves properly, but from your comments and original question it looked like this area was the one that was actually your focus. Look things over and let me know if you have further questions, and if I got the main gist of your question wrong, let me know and I'm happy to help further.
I am trying to figure out if there is a simpler way in C to scanf() a certain part of an inputted number by the user.
Following the code:
printf("Enter opcode:\n");
scanf("%1d", &opcode);
If an user inputs the number 240, scanf("%1d", &opcode); will save the first digit only in opcode
Is there a way to select only the last two digits?
The easiest way to handle the task is to read the input as a string. Then perform validations, e.g. number of characters entered, that last two characters are valid hex-digits, etc.. and then use your conversion of choice to convert the last two digits to an unsigned value.
When taking input, it is recommended that you use a line-oriented function to read the entire line and then parse what you need from the line. The benefits are three-fold (1) you get an independent validation of the read; (2) you get an independent validation of the conversion; and (3) what remains in the input buffer doesn't depend on the scanf conversion specifier used.
A short example would be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer to hold line (don't skimp on size) */
printf ("Enter opcode: "); /* prompt */
if (fgets (buf, MAXC, stdin)) { /* read entire line */
char *p; /* pointer - to set to last 2 digits */
size_t len; /* length of the string entered */
buf[(len = strcspn (buf, "\r\n"))] = 0; /* get length/trim '\n' */
if (len < 2) { /* validate at least 2 characters entered */
fputs ("error: minimum 2-characters required.\n", stderr);
return 1;
}
p = buf + len - 2; /* set p to point to next to last char */
if (!isxdigit(*p) || !isxdigit(*(p+1))) { /* validate hex digits */
fputs ("error: last 2 chars are not hex-digits.\n", stderr);
return 1;
}
printf ("last 2 digits: %s\n", p); /* output last 2 digits */
/* perform conversion of choice here
* (suggest strtoul or sscanf)
*/
}
return 0;
}
(note: choosing the conversion is left to you. Also note how you handle the '\n' included in the buffer by fgets is also up to you. Above it is simply overwritten with the nul-terminating character)
Example Use/Output
$ ./bin/opcodelast2
Enter opcode: 240
last 2 digits: 40
Other results:
Enter opcode: 40
last 2 digits: 40
Enter opcode: 3240
last 2 digits: 40
Enter opcode: 324a
last 2 digits: 4a
Enter opcode: 4g
error: last 2 chars are not hex-digits.
Enter opcode: 4
error: minimum 2-characters required.
You can adjust the tests (e.g. isdigit or isxdigit) to meet your particular needs. You can (and should) include a test that len < MAXC - 1 to ensure the entire line was read and that additional characters do not remain unread (e.g. a cat went to sleep on the keyboard). Let me know if you have any further questions.
The code of #Craig Estey can be broken if you enter a big number.
int main()
{
char str[100];
do {
scanf("%99s", str);
} while (strlen(str) < 2);
int opcode = atoi(str + strlen(str) - 2);
}
Yes, it's not perfect because it's break when you enter a string with more than 100 char.
But, you can replace the scanf by another function who can take a infinite string len.
I'm just getting introduced to C, and I was assigned to write a program that would mimic a self check out line at a grocery store. This involves me having to populate an array with the prices of grocery items based on user input, and add them up and copy them to a file.
the easiest way to populate an integer array is with a for loop. But would that be different for an array of type float?
would it look something like this? Or is this incorrect?
int size, i;
float items[size];
printf("How many items are you checking out today?");
scanf("%d", &size);
for(i=0;i<size;i++){
printf("Enter the price of an item\n");
scanf("%f", items[i]);
}
I'm new to this site so thanks in advance
I would recommend always initializing variables as you declare them to prevent "garbage" values by accident. Also, I don't really recommend pre-declaring your loop counters. You see it in a lot of old code (it used to be required due to compiler limitations), but now I just think it's code noise. It would look like this:
for (int i = 0; i < size; i++) {
// stuff
}
Also, your code has a big problem. You're using what's known as a variable-size array, and they are not a good idea. You generally want to either declare the array size at compile time, or dynamically allocate the space for the array using malloc.
Going back to the initalization, though, this is how you would set every element in a stack-allocated array on declaration:
#define SIZE 4
int main(void)
{
float items[SIZE] = { 0 };
}
If you dynamically allocate the array, I recommend using calloc or memset to set the array elements to a default value for the same reason.
To answer your question about populating the array, yes, there is no difference as to how you would actually go about doing it. A for loop works just fine in both cases. Just remember to check the return value of scanf.
As has been correctly pointed out, you cannot declare float items[size]; until size has been validly initialized to a positive integer value. Your attempt to declare items before size contains a value invokes Undefined Behavior due to your attempt to access an uninitialized value. (the valid operation of your code is over at that point and it could do anything from appearing to run correctly to StackOverflow, or SegFaulting)
Any time you are taking user-input, you must account for each character that remains in the input buffer (stdin here). This is especially true when taking input with scanf (or family) due to the way scanf handles input or matching failures. When either occurs, no further characters are read, and any offending characters are left unread in the input buffer -- just waiting to bite you again on your next attempted read (generally resulting in an infinite loop if you are taking input within a loop)
(this is one of the primary reason a line-oriented function such as fgets is recommended for taking user input)
scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions
(return == EOF) the user canceling input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10);
(return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, etc..); and
otherwise, you must handle the matching or input failure and you must account for every character that may be left in your input buffer. (generally you will scan forward in the input buffer until a '\n' or EOF is found discarding any extraneous characters that remain)
If you do your job, you can successfully use scanf as needed.
Next, a general caution do not using floating point for currency (people get real mad when you start losing money due to rounding errors) While it is fine for your example program -- just understand, in a real currency handling program, you would handle currency as an unsigned value multiplied by 100 (or whatever is required) to insure all amounts can be represented exactly.
Putting the scanf requirements together, you could do something like the following safely:
#include <stdio.h>
/* function to empty extraneous characters from stdin
* (like the '\n' generated by pressing [Enter])
*/
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int size = 0, i;
float total = 0.0;
for (;;) { /* loop continually until valid size entered */
int rtn;
printf ("How many items are you checking out today?: ");
rtn = scanf ("%d", &size);
if (rtn == EOF) { /* handle EOF */
fprintf (stderr, "(user canceled input)\n");
return 1;
}
else if (rtn == 1 && size > 0) { /* valid int received */
empty_stdin();
break;
} /* otherwise, handle error */
fprintf (stderr, "error: invalid input.\n\n");
empty_stdin (); /* remove any chars from stdin up to '\n' */
}
float items[size]; /* declare VLA of size floats */
for (i = 0; i < size; i++) {
items[i] = 0.0; /* initialize each (or memset VLA) */
for (;;) { /* loop continually until valid item price entered */
int rtn;
printf (" price of item[%2d]: ", i + 1); /* prompt for price */
rtn = scanf ("%f", &items[i]);
if (rtn == EOF) { /* handle EOF */
fprintf (stderr, "(user canceled input)\n");
return 1;
}
else if (rtn == 1 && items[i] > 0) { /* valid price received */
empty_stdin();
break;
} /* otherwise, handle error */
fprintf (stderr, "error: invalid input.\n\n");
empty_stdin (); /* remove any chars from stdin up to '\n' */
}
total += items[i];
}
printf ("\ntotal (%d items): $%.2f\n", size, total);
}
Example Use/Output
(shown with intentional errors in entry)
$ ./bin/checkout
How many items are you checking out today?: what?
error: invalid input.
How many items are you checking out today?: 4
price of item[ 1]: free?
error: invalid input.
price of item[ 1]: 1.25
price of item[ 2]: 3.50
price of item[ 3]: discount?
error: invalid input.
price of item[ 3]: 2.25
price of item[ 4]: 3
total (4 items): $10.00
Look things over and let me know if you have further questions.
There is no difference in usage of arrays in terms of usage. But there are few changes required in your code.
#define MAX_SIZE (10)
int size=0, i=0; //It is always better to initialize the variables.
float items[MAX_SIZE] = {0.0f}; //Automatically the entire array will be initialized to zero.
printf("How many items are you checking out today?");
scanf("%d", &size);
if(size > MAX_SIZE)
size = MAX_SIZE;
for(i=0;i<size;i++){
printf("Enter the price of an item\n");
scanf("%f", &items[i]); //You need to pass address of variable to scanf
}
There are other ways to implement your code to handle array size. This is one of the way.
Im trying to save a 100 character max string into an array and then print an specified character of the array via an index, yet I get Segmentation error 11, here is the code:`
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str1[100];
int index;
printf("Enter text of max 100 characters: \n");
scanf("%s", str1);
printf("Enter the index to search\n");
scanf("%d", &index);
printf("your char is: %c\n", str1[index]);
return(0);
}
`
Any suggestions?
While user input is generally better handled by reading input with fgets and then parsing what you need from the resulting buffer with sscanf of simply with a pair of pointers and "inch-worming down" (a/k/a "walking") the string testing each char and handling as needed -- it is always worth a look at scanf to detail what you need to do to successfully use it for user input.
While fgets is not without needed validations, the number and types of validations needed with scanf and handling of characters that remain in the input buffer in the different cases of input failure or matching failures create a number of extra pitfalls for new (and not so new) C programmers.
The two primary problems with scanf are (1) there is no default limitation on the number of characters that it will read into any buffer (potentially overflowing your array); and (2) the fact that it does not remove the trailing '\n' (or any of the characters following an input or matching failure) from the input buffer (e.g. stdin). It is up to you to account for all characters in the input buffer and empty the buffer as needed.
Further complicating the picture are the ways the different scanf format specifiers handle leading-whitespace (numeric conversions typically skip leading whitespace, while a character conversion won't) Another issue is handling included whitespace. The "%s" format specifier will only read up to the first whitespace encountered, making it impossible to read "My dog has fleas" with a single %s specifier. (you can use a character class to read included whitespace -- as shown in the example below)
There are many other subtleties with scanf as well, so it is well worth the time it takes to read and understand man scanf.
From the comments, you now know if you ask for a string of 100 chars, you need, at minimum, 101 characters of storage -- we will assume that is learned.
When taking any input with scanf, you must always validate the return to insure that the number of conversion expected, in fact took place. For example, if you are reading "5 dogs" with the conversion specifier "%d %s", a return of 2 indicates a successful conversion to integer and string. However, you also know, at minimum a '\n' remains in the input buffer (and potentially many more characters, if say, "5 dogs and cats" were entered. It is up to you to remove the '\n' and any other characters that remain, before attempting to read more input with scanf.
The following example captures most of the pitfalls with your example and provides a couple of tools you can use when dealing with user input. The bottom line is learn to use fgets, but know how to use scanf as well. Your goal is to provide as robust and bullet-proof input routine as you can. Think about all the dumb things a user might do when prompted for input (or heaven forbid, a cat walks across the keyboard) There are always more validations you can add. Look at each of the included validations, and let me know if you have questions:
#include <stdio.h>
#include <string.h>
#define MAXC 100 /* if you need a constant, declare one */
/* helper function to remove any chars left in input buffer */
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char str1[MAXC+1] = ""; /* initialize to all zero */
int index, rtn, len; /* index, return, length */
for (;;) { /* loop until valid input obtained */
printf ("Enter text of max 100 characters: ");
rtn = scanf ("%100[^\n]", str1); /* read at most MAXC char */
if (rtn != 1) { /* validate scanf return */
if (rtn == EOF) { /* check if EOF, ctrl+d, ctrl+z (windoze) */
printf ("input canceled.\n");
return 0;
}
if (!str1[0]) /* was a character entered? */
fprintf (stderr, "error: string is empty.\n");
/* remove '\n' and any chars that remain in stdin */
empty_stdin();
}
else { /* all conditions met, good entry, empty stdin and break */
empty_stdin();
break;
}
}
len = (int)strlen (str1); /* get string length */
for (;;) { /* now do the same thing for integer */
printf ("Enter the index to search (0-%d): ", len - 1);
if ((rtn = scanf ("%d", &index)) != 1) {
if (rtn == EOF) {
printf ("input canceled.\n");
return 0;
}
fprintf (stderr, "error: invalid input - not integer.\n");
/* only need to strip if non-integer entered, because %d
* will skip leading whitespace, including '\n'.
*/
empty_stdin();
}
else if (index < 0 || len < index + 1) /* validate index value */
fprintf (stderr, "error: invalid index - out of range.\n");
else
break;
}
printf ("your char is: %c\n", str1[index]);
return 0;
}
Example Use/Output
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): -1
error: invalid index - out of range.
Enter the index to search (0-19): 0
your char is: 1
$ ./bin/scanfstr1
Enter text of max 100 characters: 12345678901234567890
Enter the index to search (0-19): foo
error: invalid input - not integer.
Enter the index to search (0-19): 6
your char is: 7
$ ./bin/scanfstr1
Enter text of max 100 characters: My dog has fleas.
Enter the index to search (0-16): d
error: invalid input - not integer.
Enter the index to search (0-16): 3
your char is: d
$ ./bin/scanfstr1
Enter text of max 100 characters:
error: string is empty.
Enter text of max 100 characters: My cats are fine.
Enter the index to search (0-16): meow
error: invalid input - not integer.
Enter the index to search (0-16): input canceled.