Alex, continuing from your last comment, to display a menu that will allow you to add values to your array, delete values from your array and view the array (along with the max, min and average of the values), can do something similar to the following. Note: the command line isn't a windowed user interface, so your menu operations are more like a printed receipt of your transactions with it. (you can do nice text windows and stationary menus, but that generally requires an text library, such as ncurses
which is well beyond the scope of your question.
As explained in the comment, your basic approach is simply to create a loop that repeats continually. It will display your menu and allow you to enter your selection from a list, e.g.:
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection:
After the user enters the selection, to make the comparison easier, the user's input in converted to lower-case. Also note, that the input is read as a string using fgets
(a line-oriented input function) which makes taking user input much easier than having to worry about whether the '\n'
remains in the input buffer (stdin
) just waiting to cause problems for your next input. (you can use the scanf
family of functions, but YOU are the one responsible for accounting for each character entered by the user (and emptying the input buffer).
Reading input with fgets
will read up to and including the '\n'
, so their is no chance of the '\n'
being left unread in stdin
. Also note that fgets
will read a string or characters, where you are only interested in the first. That is easily handled simply by referencing the first character in the buffer. (e.g. if you are reading user-input into a buffer called buf
, you can simply use buf[0]
to access the first character, or simply *buf
for that matter)
After the user input is read, the first character is passed to a switch
statement, where each case
of the statement is compared against the first character. If the character matches a case, then the actions associated with that case are taken, ending with the break
(you can read about fall-through processing if the break
is omitted on your own)
As mentioned in the comment, if you simply need to break out of one loop, then break
is all you need. However here, your switch
statement is inside the loop. A single break
will only get you out of the switch
but not the outside for
loop. To break out of nested loops, use the goto
statement. (you could also add more variables and set some type of exit flag, but why? This is what the goto
was meant to do. (there are some cases where a flag is equally good as well)
In each case
within the switch
, you are free to call whatever code is needed to handle that menu selection. You will note we simply call short helper functions from within the switch
to print the array, insert values, remove values, etc. You can put all the code in the switch
if you like, it just rapidly become unreadable.
Putting that altogether, you can do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
enum { MAXN = 64 }; /* constant - max numbers of vals & chars */
void showmenu ();
void prnarray (int *a, int n);
int addvalue (int *a, int n, int newval);
int delvalue (int *a, int n, int index);
int minvalue (int *a, int n);
int maxvalue (int *a, int n);
int sumvalues (int *a, int n);
int main (void) {
int vals[MAXN] = { 21, 18, 32, 3, 9, 6, 16 }, /* the array */
n = 7;
for (;;) { /* loop until user quits or cancels, e.g. ctrl+d */
showmenu(); /* show the menu */
char buf[MAXN] = "";
fgets (buf, MAXN, stdin); /* read user input */
/* convert to lower-case for comparison of all entries */
switch (tolower (buf[0])) { /* 1st char is entry */
case 'v' : prnarray(vals, n);
break;
case 'i' : printf ("\n enter the new value: ");
if (fgets (buf, MAXN, stdin) &&
isdigit (buf[0])) {
n = addvalue (vals, n, atoi (buf));
}
break;
case 'd' : printf ("\n enter the index to delete: ");
if (fgets (buf, MAXN, stdin) &&
isdigit (buf[0])) {
n = delvalue (vals, n, atoi (buf));
}
break;
case 'n' : printf ("\n Mininum of '%d' values is : %d\n",
n, minvalue (vals, n));
break;
case 'x' : printf ("\n Maxinum of '%d' values is : %d\n",
n, maxvalue (vals, n));
break;
case 'a' : printf ("\n Average of '%d' values is : %.2lf\n",
n, (double)sumvalues (vals, n)/n);
break;
case 's' : printf ("\n Sum of '%d' values is : %d\n",
n, sumvalues (vals, n));
break;
case 'q' : printf (" that's all folks...\n");
goto done;
default : if (!buf[0]) { /* check for manual EOF */
putchar ('\n'); /* tidy up */
goto done;
}
fprintf (stderr, "error: invalid selection.\n");
}
}
done:; /* goto label - breaking 'for' and 'switch' */
return 0;
}
void showmenu ()
{
fprintf(stderr, "\n ======== Program Menu =========\n\n"
" V) View the Array.\n"
" I) Insert New Value.\n"
" D) Delete Existing Value.\n"
" N) Display Minimum Value.\n"
" X) Display Maximum Value.\n"
" A) Display Average of Values.\n"
" S) Display Sum of Values.\n"
"\n"
" Q) Quit.\n"
"\n"
" Selection: ");
}
void prnarray (int *a, int n)
{
int i;
printf ("\n there are '%d' values in the array:\n\n", n);
for (i = 0; i < n; i++)
printf (" array[%2d] : %d\n", i, a[i]);
}
int addvalue (int *a, int n, int newval)
{
if (n == MAXN) {
fprintf (stderr, "error: all '%d' values filled.\n", n);
return n;
}
a[n++] = newval;
return n;
}
int delvalue (int *a, int n, int index)
{
if (index < 0 || index > n - 1) {
fprintf (stderr, "error: index out of range.\n");
return n;
}
int i;
for (i = index + 1; i < n; i++)
a[i-1] = a[i];
a[i] = 0;
return --n;
}
int minvalue (int *a, int n)
{
int i, min = INT_MAX;
for (i = 0; i < n; i++)
if (a[i] < min)
min = a[i];
return min;
}
int maxvalue (int *a, int n)
{
int i, max = INT_MIN;
for (i = 0; i < n; i++)
if (a[i] > max)
max = a[i];
return max;
}
int sumvalues (int *a, int n)
{
int i, sum = 0;
for (i = 0; i < n; i++)
sum += a[i];
return sum;
}
(note: there are always additional validation checks you can add to test whether you have read all the input the user provided, etc.. But given the crux of your question I'll leave that learning to you)
Example Use/Output
$ ./bin/menusimple
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection: v
there are '7' values in the array:
array[ 0] : 21
array[ 1] : 18
array[ 2] : 32
array[ 3] : 3
array[ 4] : 9
array[ 5] : 6
array[ 6] : 16
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection: i
enter the new value: 77
======== Program Menu =========
V) View the Array.
I) Insert New Value.
D) Delete Existing Value.
N) Display Minimum Value.
X) Display Maximum Value.
A) Display Average of Values.
S) Display Sum of Values.
Q) Quit.
Selection: v
there are '8' values in the array:
array[ 0] : 21
array[ 1] : 18
array[ 2] : 32
array[ 3] : 3
array[ 4] : 9
array[ 5] : 6
array[ 6] : 16
array[ 7] : 77
Look things over and let me know if you have any questions. Also, as you are just learning, make sure you are compiling your code with compiler warnings enabled and that you don't consider your code reliable until it compiles without warning. That means you should be compiling with at least the -Wall -Wextra
flags set. If you are using gcc
and the command line, then it would be:
gcc -Wall -Wextra -O2 -o simplemenu simplemenu.c
To compile the code in simplemenu.c
into an executable named simplemenu
with the -O2
optimizations applied. If you are really wanting to eliminate all warnings, add -pedantic
as well. For codeblock or other IDE, look through the compiler menu options, they all provide a place to input all of the options you would like. Good luck with your code.
Procedural Approach Without Functions & Input With scanf
OK, now that we know how far we need to backup, let's look at rewriting the code in a bare minimum, top-down approach without using functions and taking user input with scanf
(which will cause you more grief, especially taking mixed character and numeric input, but it can be done, if you account for the '\n'
left in stdin
)
First a note about taking input with scanf
. When you ask for user input, like with the menu selection, and the user enters V
and presses Enter, the input buffer stdin
contains "V\n"
(the '\n'
as the result of pressing Enter). When you then use scanf to read a character (e.g. char sel; scanf ("%c", &sel);
) the 'V'
is taken from stdin
and stored in sel
leaving '\n'
in stdin
. If you then attempt to read another character (e.g. char nextch; scanf ("%c", &nextch);
) it will appear that scanf
has skipped reading nextch
because it never allow you to enter a value. What has actually happened is scanf ("%c", &nextch);
has read the '\n'
that remained in stdin
as your next character and is quite content with the value of 0xa
(10 decimal) in nextch
.
You must always account for the '\n'
when using scanf
. You have two options, 1) leave a space before the conversion specifier (e.g. scanf (" %c", &nextch);
or 2) use the assignment suppression operator '*'
(e.g. scanf ("%c%*c", &nextch);
), the second %*c
telling scanf
to read and discard the following character without adding the conversion to the match count (which is the integer value returned by scanf
). Which brings up the most important point of all, always check the return of scanf
. (otherwise, you have no clue whether you have an actual value to work with or just garbage) I will leave the reading of man scanf
to you for further details on the effect of the space before the conversion specifier, and the assignment suppression operator.
The return for scanf
(the match count) is the number of successful conversions performed based on the number of conversion specifiers contained within the format string (e.g. scanf (" %c %d", &somechar, &someint);
contains 2
conversion specifiers %c
and %d
, so the return for scanf
after successful conversion of both would be 2
. If a matching or conversion failure occurs, the return will be less than 2
and if an error condition is encountered reading from the stream (stdin
in this case) EOF
is returned (generally a value of -1
) All of this, and more, is why scanf
is NOT the preferred method for taking user input in C. (that being said, it is what most tutorials, and most teachers, make the poor choice to expose new C programmers to without an understanding of the pitfalls)
With that out of the way, if you work through the example below, you will see that I have simply moved the code from the functions to within the if ... else if ... else
framework. (look at the one-to-one relationship from where I call functions from the switch
in the first example and the code below) This should also show why breaking the code up into logical functions, improves readability and improves code re-use. Compare the use of the switch
statement with the if ... else if ... else
daisy chain. Both are fine, but to me, the switch
is more easily readable at a glance.
You should make sure you understand both versions as they are both basic entry level approaches to using C. Take your time going through each and if you have questions that you cannot answer by consulting one of the references provided in the tag-wiki link, just ask.
#include <stdio.h>
#include <stdlib.h> /* for atoi */
#include <limits.h> /* for INT_MIN/INT_MAX */
enum { MAXN = 64 }; /* constant - max numbers of vals & chars */
int main (void) {
int vals[MAXN] = { 21, 18, 32, 3, 9, 6, 16 }, /* the array */
n = 7;
for (;;) {
char c;
/* show the menu */
fprintf(stderr, "\n ======== Program Menu =========\n\n"
" V) View the Array.\n"
" I) Insert New Value.\n"
" D) Delete Existing Value.\n"
" N) Display Minimum Value.\n"
" X) Display Maximum Value.\n"
" S) Display Sum of Values.\n"
" A) Display Average of Values.\n"
"\n"
" Q) Quit.\n"
"\n"
" Selection: ");
/* read selection (inside of if is OK), check EOF or quit */
if (scanf (" %c", &c) == EOF || c == 'q' || c == 'Q') {
printf ("\n that's all folks...\n");
break;
}
if (c == 'v' || c == 'V') { /* view array code */
printf ("\n there are '%d' values in the array:\n\n", n);
int i;
for (i = 0; i < n; i++)
printf (" array[%2d] : %d\n", i, vals[i]);
}
else if (c == 'i' || c == 'I') { /* insert value code */
if (n == MAXN) {
fprintf (stderr, "error: all '%d' values filled.\n", n);
continue;
}
int newval = 0;
printf ("\n enter the new value: ");
if (scanf (" %d", &newval) == 1) {
vals[n] = newval;
n++;
}
else
fprintf (stderr, "error: invalid input.\n");
}
else if (c == 'd' || c == 'D') { /* delete value code */
int i, index = 0;
printf ("\n enter the index to delete: ");
if (scanf (" %d", &index) != 1) {
fprintf (stderr, "error: invalid input.\n");
continue;
}
if (index < 0 || index > n - 1) {
fprintf (stderr, "error: index out of range.\n");
continue;
}
for (i = index + 1; i < n; i++)
vals[i-1] = vals[i];
vals[i] = 0;
n--;
}
else if (c == 'n' || c == 'N') { /* display minimum code */
int i, min = INT_MAX;
for (i = 0; i < n; i++)
if (vals[i] < min)
min = vals[i];
printf ("\n Mininum of '%d' values is : %d\n",
n, min);
}
else if (c == 'x' || c == 'X') { /* display maximum code */
int i, max = INT_MIN;
for (i = 0; i < n; i++)
if (vals[i] > max)
max = vals[i];
printf ("\n Maxinum of '%d' values is : %d\n",
n, max);
}
else if (c == 's' || c == 'S') { /* compute sum code */
int i, sum = 0;
for (i = 0; i < n; i++)
sum += vals[i];
printf ("\n Sum of '%d' values is : %d\n",
n, sum);
}
else if (c == 'a' || c == 'A') { /* compute avg code */
int i, sum = 0;
double avg = 0.0;
for (i = 0; i < n; i++)
sum += vals[i];
avg = (double)sum/n;
printf ("\n Average of '%d' values is : %.2lf\n",
n, avg);
}
else /* if not matched, then invalid selection */
fprintf (stderr, "error: invalid selection.\n");
}
return 0;
}
(the operation and output from both program versions will be identical)