Functions messes up numbers - c

When I add workers thru main in for loop, everything is fine. But when I add them with one of my functions insert_one_with_pointers or insert_multiple_workers, and check all workers with my third function display_all_workers, I see that something is wrong.
Their id values are not correct and sometimes the program crashes or it doesn't display them all of them, just the ones thet were inserted in the beginning in main.
I just started working with structures and functions so I'm not sure what exactly is causing this, but I think that it has to do with my probably incorrect usage of i. I've been trying to fix this for quite a while, but unsuccessfully.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Worker {
int id;
char name[40];
int age;
};
void insert_one_with_pointers(struct Worker *niz, int i) {
niz[i].id = i + 1;
printf("\nWhat is name of %d. worker: ", i + 1);
scanf("%s", niz[i].name);
printf("What is age of %d. worker: ", i + 1);
scanf("%d", &niz[i].age);
}
void insert_multiple_workers(struct Worker niz[], int i, int number) {
int j;
for (j = i; j < number; j++) {
niz[j].id = j + 1;
printf("\nWhat is name of %d. worker: ", j + 1);
scanf("%s", niz[j].name);
printf("What is age of %d. worker: ", j + 1);
scanf("%d", &niz[j].age);
}
}
void display_all_workers(struct Worker niz[], int i) {
printf("\nAll workers:\n");
for (int j = 0; j < i; j++) {
printf("\nID: %d", niz[j].id);
printf("\nName: %s", niz[j].name);
printf("\nAge: %d", niz[j].age);
}
}
int main() {
int choice, i, m, number;
printf("Add a number of workers and then their info: ");
scanf("%d", &i);
struct Worker niz[i];
for (int j = 0; j < i; j++) {
niz[j].id = j + 1;
printf("\nWhat is name of %d. worker: ", j + 1);
scanf("%s", niz[j].name);
printf("What is age of %d. worker: ", j + 1);
scanf("%d", &niz[j].age);
}
while(1) {
printf("\nChoose a function:\n1 - Add a worker using pointers\n2 - Add n workers\n3 - Display all workers\n4 - Leave\n");
scanf("%d", &choice);
switch(choice) {
case 1:
insert_one_with_pointers(niz, i); i++;
break;
case 2:
printf("\nHow many workers do you want to insert? ");
scanf("%d", &m);
number = i + m;
insert_multiple_workers(niz, i, number);
break;
case 3:
display_all_workers(niz, i);
break;
case 4:
return 0;
}
}
return 0;
}
Edit: I followed everything that replies to my post said, which fixed most of it.
The final change was that I added i = i + m; below the call for function insert_more_workers in main. Now everything works as it should.

Continuing from my comment, the primary problem you face is that you set the number of workers (i) at the beginning of your program and you use that value to size your niz VLA (Variable Length Array). Once set, it cannot be changed. So when you attempt to add additional workers later using you menu, you attempt to write beyond the end of your array (resulting in Undefined Behavior) causing the problem output you see.
Your alternatives are either to dynamically allocate storage for your workers and keep track of how many you have allocated and how many you have filled, and when filled == allocated you reallocate more storage. Another option is simply to declare some reasonable max number of workers you will have and then declare an array of that many, keeping track of the number added and when the number of workers added equals the array size -- just indicate the array is full. (though you can't expand the number of workers here)
An additional note about using VLAs. Beginning with the C11 standard, compiler support for VLAs was made optional -- though I don't know of any compilers that don't continue to support them.
Weaknesses In Your Code
The most glaring weakness is your failure to check the return of scanf() when a numeric conversion is required. That invites problems. Why? scanf() can fail in two ways (1) a matching-failure where the input provided doesn't match the conversion specified. (e.g. user enters "twenty-one" for age instead of 21). When a matching failure occurs, character extraction from the input buffer ceases at the point of failure -- leaving "twenty-one" unread in the input buffer -- just waiting to bite you on your next attempted input.
In fact if a matching-failure occurs before you enter you menu while(1) loop -- your code locks into an infinite loop.
(2) scanf() can fail with an input-failure where EOF is reached before the first valid conversion takes place. (a user can cancel input by generating a manual EOF with Ctrl + d, or `Ctrl + z on windows) If you don't check the return, you cannot handle either case.
Another weakness with your approach is using scanf() for user-input. Why? Because in the matching-failure case, or after any use, characters are left in stdin unread. In the normal case the '\n' is left unread, and if your next input isn't taken with scanf() and you don't extract the unread character(s), your next input will fail. This is one of the (many) pitfalls associated with using scanf(). Instead, as a general proposition, all user-input should be taken using fgets() and then any conversions should be handled by sscanf() from the buffer filled by fgets(). That way, with a sufficiently sized buffer, you are guaranteed to consume each line of input regardless of whether the subsequent conversion fails. No characters are left unread to impact the next input attempt.
Alternatives Approach
While the two different input functions may be a homework requirement, there is no need for different input functions to read one, and multiple workers. Just write a single function that can handle both. Since your input function can succeed or fail, you need to provide a meaningful return to the caller, so the success or failure of your input function can be validated at the point it was called. void return type is only sufficient for functions that have no bearing on the continued operation of your code (like print functions, etc..).
In your switch() statement, you should handle menu entries that are out-of-range. What if the user slips and enters 5 or -32 or "foo" for that matter? We addressed the matching-failure above, but to handle input that is out-of-range, simply providing a default: case can suffice. For example:
default: fputs (" error: selection out of range.\n", stderr);
break;
Is all that is needed to cover:
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
5
error: selection out of range.
To make your code more readable, understand that adjacent string-literals are joined together during compilation. This allows you to provide much more readable code. For example, your menu can be written as:
printf ("\nChoose a function:\n"
" 1 - Add a worker using pointers\n"
" 2 - Add n workers\n"
" 3 - Display all workers\n"
" 4 - Leave\n");
Your display_all_workers() function could also benefit from that approach reducing the number of calls to printf() from 3-to-1.
Putting all the suggestions above together, and using a standard array of 200 elements for your workers array, you could rewrite your code as:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXWRKRS 200 /* if you need a constant, #define one (or more) */
#define MAXC 1024
struct Worker
{
int id;
char name[40];
int age;
};
int insert_workers (struct Worker *workers, int *nworkers, int toadd)
{
char buf[MAXC] = "";
int i = 0, start_index = *nworkers;
size_t len = 0;
while (i < toadd) { /* loop nworkers times */
int current = i + start_index;
if (current == MAXWRKRS) { /* check array bounds */
puts ("(workers array full)");
return i;
}
printf ("\nWhat is name of %d. worker: ", current + 1);
if (!fgets (buf, MAXC, stdin)) { /* read all input into buf */
return i;
}
buf[(len = strcspn (buf, "\n"))] = 0; /* trim '\n', save length */
memcpy (workers[current].name, buf, len + 1);
for (;;) { /* loop continually until valid integer entered */
printf ("What is age of %d. worker: ", current + 1);
if (!fgets (buf, MAXC, stdin)) { /* read all input into buf */
return i;
}
/* parse needed information from buf with sscanf() */
if (sscanf (buf, "%d", &workers[current].age) != 1) { /* check return */
fputs (" error: invalid integer input.\n", stderr);
continue;
}
else {
workers[current].id = current + 1; /* set worker ID, done */
*nworkers += 1; /* all conditions met - update worker count */
break;
}
}
i++;
}
return i; /* return number of workers added (can validate in caller) */
}
void display_all_workers (struct Worker *workers, int nworkers)
{
puts("\nAll workers:");
for (int j = 0; j < nworkers; j++) {
printf ("\n ID : %d\n"
" Name : %s\n"
" Age : %d\n",
workers[j].id, workers[j].name, workers[j].age);
}
}
int main()
{
char buf[MAXC] = ""; /* buffer for all user input */
int i, nwrkrs = 0; /* tmp counter & number of workers */
struct Worker workers[MAXWRKRS]; /* array of workers */
printf("Add a number of workers and then their info: ");
if (!fgets (buf, MAXC, stdin)) {
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &i) != 1) {
fputs ("errur: invalid integer input.\n", stderr);
return 1;
}
printf ("%d workers added\n\n", insert_workers (workers, &nwrkrs, i));
while(1) {
int choice;
printf ("\nChoose a function:\n"
" 1 - Add a worker using pointers\n"
" 2 - Add n workers\n"
" 3 - Display all workers\n"
" 4 - Leave\n");
if (!fgets (buf, MAXC, stdin)) {
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &choice) != 1 ) {
fputs ("error: invalid integer input.\n", stderr);
continue;
}
switch (choice) {
case 1 : insert_workers (workers, &nwrkrs, 1);
break;
case 2 : printf("\nHow many workers do you want to insert? ");
if (!fgets (buf, MAXC, stdin)) {
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &i) != 1) {
fputs ("error: invalid integer input.\n", stderr);
break;
}
insert_workers (workers, &nwrkrs, i);
break;
case 3 : display_all_workers (workers, nwrkrs);
break;
case 4 : return 0;
default: fputs (" error: selection out of range.\n", stderr);
break;
}
}
return 0;
}
(note: how the number of workers nworkers is passed as a pointer to the insert_workers() function so that the number of workers can be updated within the function when a valid name and age has been entered)
One immediate benefit of the use of fgets() for input is you can now enter names containing spaces. Another is that errors in numeric input are easily handled, e.g.
...
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
1
What is name of 6. worker: Bugs Bunny
What is age of 6. worker: dunno - really
error: invalid integer input.
What is age of 6. worker: 101
...
The error handling shown in the example code isn't intended to be exhaustive, and there is much more that can be added. However, note by using fgets() and checking if the return is NULL (e.g. if (!fgets(...))) you are handling the case where a manual EOF is generated by the user to cancel input.
If you are still stuck with your code, or if you have further questions about the changes above or why one approach has advantages over another, just drop a comment below and I'm happy to help further.
Example Use/Output
Here is a short set of input/output from testing the code:
./bin/workers_fn_new
Add a number of workers and then their info: 2
What is name of 1. worker: Mickey Mouse
What is age of 1. worker: 99
What is name of 2. worker: Minnie Mouse
What is age of 2. worker: 97
2 workers added
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
1
What is name of 3. worker: Pluto (the dog)
What is age of 3. worker: 92
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
ID : 3
Name : Pluto (the dog)
Age : 92
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
2
How many workers do you want to insert? 2
What is name of 4. worker: Daffy Duck
What is age of 4. worker: 93
What is name of 5. worker: Daisy Duck
What is age of 5. worker: 91
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
ID : 3
Name : Pluto (the dog)
Age : 92
ID : 4
Name : Daffy Duck
Age : 93
ID : 5
Name : Daisy Duck
Age : 91
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
1
What is name of 6. worker: Bugs Bunny
What is age of 6. worker: dunno - really
error: invalid integer input.
What is age of 6. worker: 101
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
ID : 3
Name : Pluto (the dog)
Age : 92
ID : 4
Name : Daffy Duck
Age : 93
ID : 5
Name : Daisy Duck
Age : 91
ID : 6
Name : Bugs Bunny
Age : 101
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
4

Related

How do I get this to register -1 as an exit & show the numbered list as starting from 1?

Okay so the prompt was:
"Write a simple program (to demonstrate use of an array) that asks a user to type in numbers and keep typing in numbers until the user enters -1 or the total number of numbers entered reaches 20. Your program will stop asking for new input based on either condition above. Once one of the above conditions are met, the program will output all numbers entered as follows: 1. 888 2. 999 3. 4 …..and so on. Lastly, the program needs to display the sum of all values entered, excluding the -1 used to terminate the user input loop. The array should be used to store the user's inputs, and display them back before the program terminates."
Now, I have most of the prompt completed. It stops at 20 entered variables and displays them and the sum correctly. However, no matter how hard I try, I can't get it to register -1 as an exit. Nor can I get the bulleted list to start from 1. and end at 20. Everything else works just fine.
#include <stdio.h>
#define NUMBER_INPUTS 20
int main(void){
double userInput[NUMBER_INPUTS] = { 0 };
int i = 0;
while (i < NUMBER_INPUTS){
printf("Enter number: \n", i);
scanf("%lf", &userInput[i]);
i++;
}
for(i=0; i<NUMBER_INPUTS; i++)
printf("%i. %.1lf \n", i, userInput[i]);
double total=0;
for(i=0; i<NUMBER_INPUTS; i++)
total+=userInput[i];
printf("Total: %.1lf \n", total);
return 0;
}
This is the code that works just fine. I've tried different do-whiles in different places to register the -1 but it messes up the output list AND doesn't stop the run.
#include <stdio.h>
#define NUMBER_INPUTS 20
int main(void){
double userInput[NUMBER_INPUTS] = { 0 };
int i = 0;
do{
while (i < NUMBER_INPUTS){
printf("Enter number: \n", i);
scanf("%lf", &userInput[i]);
i++;
}
}while (userInput[i] != -1);
for(i=0; i<NUMBER_INPUTS; i++)
printf("%i. %.1lf \n", i, userInput[i]);
double total=0;
for(i=0; i<NUMBER_INPUTS; i++)
total+=userInput[i];
printf("Total: %.1lf \n", total);
return 0;
}
Like this? Just stops at the 20th variable entered. Doesn't matter if I've entered a -1 it just keeps going.
And for the list I attempted to use
i=i+1
to get the list from 1. to 20. to try and bump the i variable up one but for some reason that just shorted the output list to only show 20. and the total? I don't know what I'm doing wrong and would appreciate some input. Thank you.
Be very careful in comparison on floating-point numbers with exact values. (the point of the two links). While double can represent -1 exactly, don't fall into the habit of believing that floating-point numbers can represent any value exactly -- they can't. See: Is floating point math broken? and Why Are Floating Point Numbers Inaccurate?
To stop reading at the input of -1, you need to preserve the value of i (the number of elements) that have a valid double stored when you leave the read loop (that is the number of array elements filled). int n = i; will work (or just use the separate counter n and save i for use as a loop variable). Then in your print loop. for (i = 0; i < n; i++). As you have it, you are attempting to print NUMBER_INPUTS every time. If the user enters -1 after 3 inputs -- that's not going to work.
In order to use any input function correctly, you must validate the return. (this is especially true with scanf with contains many pitfalls for the new C programmer) Otherwise on a matching-failure, your array element will be left indeterminate, character extraction from stdin will cease leaving the offending characters in stdin unread just waiting to bite you again on your next attempted input. Instead -- validate, validate, validate. Lest ye violate Commandment No. 6. Example:
if (scanf ("%lf", &userInput[n]) != 1) { /* validate input */
fputs ("error: invalid double input.\n", stderr);
return 1;
}
Putting the rest together, and giving an example of checking a floating-point value within a tolerance of +/- 1E-5, you could do:
#include <stdio.h>
#define NUMBER_INPUTS 20
int main (void) {
double userInput[NUMBER_INPUTS] = { 0 }, total = 0;
int i = 0, n = 0;
while (n < NUMBER_INPUTS){
fputs ("Enter number: ", stdout);
if (scanf ("%lf", &userInput[n]) != 1) { /* validate input */
fputs ("error: invalid double input.\n", stderr);
return 1;
}
/* check for -1 (with +/- 1E-5 tolerance) */
if (userInput[n] > -1.00001 && userInput[n] < -0.99998)
break;
total += userInput[n];
n++;
}
putchar ('\n');
for(i = 0; i < n; i++)
printf("%d. %.1lf\n", i + 1, userInput[i]);
printf ("\nTotal: %.1lf \n", total);
return 0;
}
Example Use/Output
$ ./bin/total
Enter number: 10
Enter number: 20
Enter number: 30
Enter number: 40
Enter number: 50
Enter number: -1
1. 10.0
2. 20.0
3. 30.0
4. 40.0
5. 50.0
Total: 150.0
Look things over and let me know if you have further questions. You were quite close.
Edit - updated output element indication to i + 1 to meet your requirement of Showing The Numbered List Starting From 1.

Having a hard time adding results together in C (For Loop)

I wrote this code in C. What it basically do is it takes an input (number) for the user and, depending the number the user chose, it will run a for loop. The for loop will asks the user to input more numbers depending on the statement that it is asking. The code will then take those input from the user and apply it to the formula.
Let say the user inputs 2 at first. What it is supposed to do is go to through the loop twice and take the sum from the first and second time and added them together. Unfortunately, it doesn't do that and will only give back the first result from the loop separated by the second result.
This is the code:
#include <stdio.h>
int main(){
int i = 0;
int robotMany = 0;
int enginePower = 0;
int resistance = 0;
int weight = 0;
int height = 0;
printf("Depending the numbers of robot that you enter.You will need to do the following\n");
printf("Engine power\nResistance\nWeight\nHeight\n");
printf("Enter the amount of robots\n");
scanf("%d",&robotMany);
printf("You chose %d robot\n", robotMany);
//Depending the user input, it will run # of times.
for(i = 0; i < robotMany; i++){
//for loop begins
printf("Enter the engine power.(1-20)\n");
scanf("%d", &enginePower);
printf("Enter the resistance.(1-3)\n");
scanf("%d", &resistance);
printf("Enter the weight of the robot\n");
scanf("%d", &weight);
printf("Enter the height of Robot.\n");
scanf("%d", &height);
int product;
//take the user inputs and apply it to the formual.
product = (enginePower + resistance) * (weight - height);
printf("This is your total power\n");
printf("%d\n", product);
}
return 0;
}
You have a fundamental logic error in failing to keep a running total for the product calculated within each loop iteration.
However you have a larger problem in failing to validate the return of scanf (or any function you use for user-input). It is critical you validate EVERY user input, and even more so when using scanf to avoid invoking Undefined Behavior in your code. Why? If the user enters a stray character that does not match the conversion specifier in the format string, a matching failure occurs and character extraction from stdin ceases at that point leaving the offending character in the input buffer unread. In your case this would result in the same matching failure occurring for every call to scanf throughout the remainder of your program.
Take for example the user reaches for the '2' key but accidentally taps the 'w' key instead when entering robotMany. Not only does the input for robotMany fail, but since the 'w' is left in stdin unread, it causes input for enginePower, resistance, weight & height to fail as well. (if this occurs within a loop, your loop will likely go spinning out of control until the process is manually terminated)
That is why it is critical to always validate the return of any input function, but especially the scanf family of functions. To catch and handle any input failure (matching failure, input failure, or manually generated EOF), just check the return, e.g.
if (scanf ("%d", &robotMany) != 1) { /* VALIDATE EVERY USER INPUT! */
fputs ("error: invalid input, or user canceled - robotMany\n", stderr);
return 1;
}
On to your logic errors. First, you need a variable to track the sum of each product calculated on each trip through the loop. Since it appears this will be a total power, just add a totalpwr variable and initialize it to zero (as you have done a good job doing with all your variables), e.g.
int i = 0,
robotMany = 0,
enginePower = 0,
resistance = 0,
weight = 0,
height = 0,
totalpwr = 0; /* sum of product from each iteration */
Now within your loop, you want to add product to totalpwr each time it is computed, e.g.
/* Depending the user input, it will run # of times. */
for (i = 0; i < robotMany; i++) {
...
/* take the user inputs and apply it to the formula. */
int product = (enginePower + resistance) * (weight - height);
totalpwr += product; /* add product to total power */
}
To output the totalpwr, you will output the value after you exit the loop, e.g.
...
}
/* you output the total power required outside the loop */
printf ("\ntotal power required: %d\n", totalpwr);
return 0;
}
Putting it altogether in an example (without passing on the correctness of the product calculation or the range of values needed) you can do something similar to the following:
#include <stdio.h>
int main (void) {
int i = 0,
robotMany = 0,
enginePower = 0,
resistance = 0,
weight = 0,
height = 0,
totalpwr = 0; /* sum of product from each iteration */
/* you only need a single printf (fputs would do due to no conversion) */
printf ("Depending the numbers of robot that you enter, you will need "
"to enter:\n\n"
" Engine power | Resistance | Weight | Height\n\n"
"Enter the amount of robots: ");
if (scanf ("%d", &robotMany) != 1) { /* VALIDATE EVERY USER INPUT! */
fputs ("error: invalid input, or user canceled - robotMany\n", stderr);
return 1;
}
printf ("\nYou chose: %d robots\n\n", robotMany);
/* Depending the user input, it will run # of times. */
for (i = 0; i < robotMany; i++) {
printf ("Enter the engine power.(1-20): ");
if (scanf("%d", &enginePower) != 1) { /* validate! */
fputs ("error: invalid input - enginePower\n", stderr);
return 1;
}
printf ("Enter the resistance.(1-3): ");
if (scanf("%d", &resistance) != 1) { /* validate! */
fputs ("error: invalid input - resistance\n", stderr);
return 1;
}
printf ("Enter the weight of the robot: ");
if (scanf("%d", &weight) != 1) { /* validate! */
fputs ("error: invalid input - weight\n", stderr);
return 1;
}
printf ("Enter the height of Robot: ");
if (scanf("%d", &height) != 1) { /* validate! */
fputs ("error: invalid input - height\n", stderr);
return 1;
}
/* take the user inputs and apply it to the formula. */
int product = (enginePower + resistance) * (weight - height);
/* the following outputs the per-loop/per-robot product (you can remove) */
printf (" iteration %d product: %d\n\n", i+1, product);
totalpwr += product; /* add product to total power */
}
/* you output the total power required outside the loop */
printf ("\ntotal power required: %d\n", totalpwr);
return 0;
}
(note: only one printf is required to output multiple lines of output. There is no need to make repeated calls one after the other just to output separate lines. All sequential strings will be concatenated by the compiler)
Example Use/Output
$ ./bin/robotmany
Depending the numbers of robot that you enter, you will need to enter:
Engine power | Resistance | Weight | Height
Enter the amount of robots: 3
You chose: 3 robots
Enter the engine power.(1-20): 4
Enter the resistance.(1-3): 2
Enter the weight of the robot: 10
Enter the height of Robot: 31
iteration 1 product: -126
Enter the engine power.(1-20): 5
Enter the resistance.(1-3): 3
Enter the weight of the robot: 9
Enter the height of Robot: 31
iteration 2 product: -176
Enter the engine power.(1-20): 3
Enter the resistance.(1-3): 1
Enter the weight of the robot: 8
Enter the height of Robot: 31
iteration 3 product: -92
total power required: -394
You may want to add constraints on the weight and height entry to prevent a negative power-required result -- but that is left to you. Look things over and let me know if you have further questions.

Measurement with structure and searching for least, largest and average temperature

I have to write a program for recording the measured temperatures,
into which new measurements can be entered, or display statistics calculated from previous measurements. In the case of printing statistics, it is necessary to find and display the lowest and highest recorded temperature and calculate the average temperature.
#include <stdio.h>
int least_in_array(int array[], int n)
{
int i, least=array[0];
for(i=0; i<n; i++)
{
if(array[i]<least)
{
least=array[i];
}
}
return least;
}
int largest_in_array (int array[], int n)
{
int i, largest=array[0];
for(i=0; i<n; i++)
{
if(array[i]>largest)
{
largest=array[i];
}
}
return largest;
}
float average_array(int array[], int n)
{
int amount=0, i;
for(i=0; i<n; i++)
{
amount+=array[i];
}
return (1.0*amount)/n;
}
int main() {
int choose;
struct Data1 {
int dayInMonth;
int month;
float temperature;
} da;
printf("New measurement(1) or statistic(2)?\n");
scanf("%d", &choose);
if(choose==1) {
printf("Enter the day in month : ");
scanf("%d", &da.dayInMonth);
printf("Enter month : ");
scanf("%d", &da.month);
printf("Enter temperature (*C) : ");
scanf("%f", &da.temperature);
printf("Data saved!");
printf("-----------\n");
return 0;
}
if(choose==2) {
//printf("Least temperatures in quarter: %d", least_in_array(dayInMonth) );
return 0;
}
else {
printf("----------------\n");
printf("Wrong choose!\n");
}
return 0;
}
In case 2, I commented line where I get an error. Can anyone give me any advice to fix this problem?
|71|error: 'dayInMonth' undeclared (first use in this function)
|71|error: too few arguments to function 'least_in_array'
Since it is Christmas Eve, lets help out fellow man... who obviously is badly in need. For code that is supposed to pass an array to least_in_array, largest_in_array and average_array -- you have no array! That somewhat puts you behind the eight-ball from the word "Go!".
Your thought to use a stuct to capture and coordinate dayInMonth, month and temperature is 100% correct. (but note: C-style generally avoids camelCase variable names in favor of all lowercase). Also, defining struct Data1 within main() prevents your functions above that point from knowing what struct Data1 is, so you need to move your struct definition to the top of the file. (or provide a forward declaration, but we will save that for later) For now, let's just move your Data1 to the top (I used a typedef to alleviate having to prefix each use with stuct and renamed Data1 data:
#define NDAYS 365
typedef struct {
int day,
month;
double temperature;
} data;
Note the defined constant NDAYS. Since you are going to need an array, and since a dynamically allocated and reallocated approach seem well beyond your needs here, let's just define a constant, a maximum, for the number of elements we will have in our array.
Now, before we go further, let's think about our array. If we are going to use a struct to hold the associated date and temperature measurement, then we are going to need an array of... what?? An array of struct. And with our constant NDAYS, we will declare a maximum of 365 elements in our array. So how would this work? We have our stuct typedeffed to data, so if we want an array of data we need:
data da[NDAYS] = {{0, 0, 0.0}};
Note: the older initializer format initializing each field was used as it is unclear whether you could be using a C89 compiler. C99 and later added named initializers , e.g. {{.day = 0}} and any uninitialized elements are implicitly initialized to zero.
Now we have an array of data and can capture measurements. Of course you are responsible for keeping track of the indexes you fill. A simple counter, incremented each time a successful measurement in entered is all you need (ndx is used below)
How do we make least_in_array, largest_in_array and average_array work with an array of data? Recall, we moved the definition of the struct to the beginning of the source file, so now each function knows what data is. We simply need to pass our array of data as a parameter, e.g.
float least_in_array (data *array, int n)
{
int i;
float least = INT_MAX; /* sufficiently high temp */
for (i = 0; i < n; i++)
if (array[i].temperature < least)
least = array[i].temperature;
return least;
}
(note, each of the quantities of least_in_array, largest_in_array and average_array refer to the temperature values and therefore need to return type float.)
The remainder of putting together largest_in_array and average_array follows the same pattern.
The crux of your remaining problems relate to handling user input, and the particular pitfalls associated with taking user input with scanf. It can be done, but you must always validate the return, and test for user cancellation of input by testing for EOF (the result of the user pressing Ctrl+d (or Ctrl+z on windoze)).
You must validate that the number of expected conversions did in fact take place, or you must handle the error, and most importantly, you must account for any characters left in the input buffer (e.g. stdin), otherwise, if an invalid entry is made and you don't remove it from stdin, you will loop forever... (with scanf if a matching or input failure occurs -- no additional character are processed -- leaving them where? (oh, in stdin)
You can make this fairly easy on yourself if you declare a few helper functions to empty stdin, and make the normal process of getting an integer or float from the user relatively safe. For example, to empty stdin, you simply read all remaining characters until a '\n' or EOF is encountered, e.g.
/** simple function to empty all characters that remain
* in stdin. Needed when using scanf for user input.
*/
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
To ask the user for an int or float, you simply validate the return, check the number of successful conversion (what scanf returns), check whether EOF was generated by the user, and finally handle the error if invalid input was given and empty_stdin as required. You generally want to loop until the user either provides valid input, or cancels. (an invalid input does you no good).
For example, getting integer input can be handled as follows. (note: I put it in a function to keep from repeating this block of code for each input you take in the main body of your code...
/** abbreviated function to read integer value from user.
* returns value on success, exits if user cancels.
* empties stdin after call to scanf.
*/
int getint ()
{
int n = 0;
for (;;) { /* loop until valid input or EOF */
int rtn = 0; /* variable to capture return of scanf */
rtn = scanf ("%d", &n);
if (rtn == 1) { /* we got an integer */
empty_stdin();
break;
}
else if (rtn == EOF) { /* if user cancels, exit */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
if (rtn == 0) /* handle error */
fprintf (stderr, "getint() error: invalid integer input.\n");
empty_stdin(); /* empty all chars remaining in stdin */
}
return n;
}
(taking input for a float works the same way).
Lastly, you generally want to avoid using float in most cases, using double instead, just due to loss of precision and limitations for representing floating-point numbers with a 23-bit mantissa. However, here we are taking temperature, and float will suffice for this code.
Putting it altogether, you could do something like the following. Note, I have left your functions before main() as you had them, and added the helper function definitions after main(), but provided declarations before main() so they are visible to main() :
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define NDAYS 365
typedef struct {
int day,
month;
double temperature;
} data;
float least_in_array (data *array, int n)
{
int i;
float least = INT_MAX; /* sufficiently high temp */
for (i = 0; i < n; i++)
if (array[i].temperature < least)
least = array[i].temperature;
return least;
}
float largest_in_array (data *array, int n)
{
int i;
float largest = INT_MIN; /* sufficiently low temp */
for (i = 0; i < n; i++)
if (array[i].temperature > largest)
largest = array[i].temperature;
return largest;
}
float average_array (data *array, int n)
{
int i;
float sum = 0.0;
for (i = 0; i < n; i++)
sum += array[i].temperature;
return sum / (float)n;
}
void empty_stdin();
int getint();
float getfloat();
int main (void) {
int ndx = 0;
float max = 0.0, min = 0.0, avg = 0.0;
data da[NDAYS] = {{0, 0, 0.0}};
for (;;) {
int choose = 0;
printf ("\n (1) New measurement\n"
" (2) Statistics\n"
" (3) Exit\n\n"
" choice: ");
choose = getint();
putchar ('\n');
switch (choose) {
case 1: if (ndx == NDAYS) {
fprintf (stderr, "error: array full.\n");
break;
}
printf (" Enter day in month : ");
da[ndx].day = getint();
printf (" Enter month : ");
da[ndx].month = getint();
printf (" Enter temperature : ");
da[ndx].temperature = getfloat();
printf (" da[%3d] saved\n"
" ----------------------------\n", ndx++);
break;
case 2: if (ndx == 0) {
fprintf (stderr, "error: no measurements taken.\n");
break;
}
min = least_in_array (da, ndx);
max = largest_in_array (da, ndx);
avg = average_array (da, ndx);
printf (" minimum temp : %.2f\n"
" maximum temp : %.2f\n"
" average temp : %.2f\n"
" ----------------------------\n",
min, max, avg);
break;
case 3:
goto done;
break;
default:
fprintf (stderr, "error: invalid choice.\n");
empty_stdin();
break;
}
}
done:;
return 0;
}
/** simple function to empty all characters that remain
* in stdin. Needed when using scanf for user input.
*/
void empty_stdin()
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
/** abbreviated function to read integer value from user.
* returns value on success, exits if user cancels.
* empties stdin after call to scanf.
*/
int getint ()
{
int n = 0;
for (;;) { /* loop until valid input or EOF */
int rtn = 0; /* variable to capture return of scanf */
rtn = scanf ("%d", &n);
if (rtn == 1) { /* we got an integer */
empty_stdin();
break;
}
else if (rtn == EOF) { /* if user cancels, exit */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
if (rtn == 0) /* handle error */
fprintf (stderr, "getint() error: invalid integer input.\n");
empty_stdin(); /* empty all chars remaining in stdin */
}
return n;
}
/** abbreviated function to read float value from user.
* returns value on success, exits if user cancels.
* empties stdin after call to scanf.
*/
float getfloat ()
{
float v = 0.0;
for (;;) { /* loop until valid input or EOF */
int rtn = 0; /* variable to capture return of scanf */
rtn = scanf ("%f", &v);
if (rtn == 1) { /* we got an float */
empty_stdin();
break;
}
else if (rtn == EOF) { /* if user cancels, exit */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
if (rtn == 0) /* handle error */
fprintf (stderr, "getint() error: invalid float input.\n");
empty_stdin(); /* empty all chars remaining in stdin */
}
return v;
}
Note: how the array bounds are limited to 365 or less in your take measurement part of the code, and how a check is made that there is at least one measurement present before any statistics can be provided. You always need to validate each step of the process. Look for corner-cases, think about what could go wrong with input (what if a cat stepped on the keyboard), and then go provide validations and checks to handle each case. The validations above are just the bare minimum, there are always more you can add to more fully report and handle each error condition.
Example Use/Output
$ ./bin/tempmxmnavg
(1) New measurement
(2) Statistics
(3) Exit
choice: 2
error: no measurements taken.
(1) New measurement
(2) Statistics
(3) Exit
choice: 1
Enter day in month : 8
Enter month : 2
Enter temperature : 20.0
da[ 0] saved
----------------------------
(1) New measurement
(2) Statistics
(3) Exit
choice: 1
Enter day in month : 9
Enter month : 2
Enter temperature : 30.0
da[ 1] saved
----------------------------
(1) New measurement
(2) Statistics
(3) Exit
choice: 1
Enter day in month : 10
Enter month : 2
Enter temperature : 70.0
da[ 2] saved
----------------------------
(1) New measurement
(2) Statistics
(3) Exit
choice: 2
minimum temp : 20.00
maximum temp : 70.00
average temp : 40.00
----------------------------
(1) New measurement
(2) Statistics
(3) Exit
choice: 3
Look things over and let me know if you have any further questions (although given the evening, I won't be here much longer...)

Can I check a condition for one variable

I am writing a program to scan the name, gender, and three monthly checks for a person. Here is an example on what I want entered:
Jack m 200 250 300
If the user types "Enough" and presses enter without filling the other details I want the loop to end. I tried using two scanf's, one for the string alone and one for the others but it doesn't loop properly. Here is my code:
int main()
{
int i;
char names[SIZE][NAME_LEN] = {0}, gender[SIZE] = {0};
int sales[SIZE][SALES_LEN] = {0};
printf("Enter the name, gender and three month sales for name %d: ", i+1);
for (i=0; i<SIZE; i++){
if (strcmp(names[i], "Enough") == 0 || strcmp(names[i], "enough") == 0)
break;
scanf("%s %c %d %d %d",names[i], &gender[i], &sales[i][0],&sales[i][1],&sales[i][2]);
}
return 0;
}
Your code is broken is many places: badly ordered statements and wrong kind of reading and parsing. This may do the trick:
for (i=0; i<SIZE; i++) {
char buffer[100]; // A full line
printf("Enter the name, gender and three month sales for name %d: ", i+1);
if (fgets(buffer,sizeof buffer,stdin)==NULL // if nothing can be read
|| strncasecmp(buffer,"-stop",5)) { // or if user typed "-stop" and whatever, an impossible lastname?
break;
}
// try to convert the line into fields of given types...
if (sscanf(buffer,"%s %c %d %d %d",names[i], &gender[i], &sales[i][0],&sales[i][1],&sales[i][2])!=5) {
// do something if conversions failed
}
}
You could use something like this:
while(gets(line)) { ... }
If user presses only the return key the gets function returns NULL and the cycle stops. This way the user doesn't have to type "Enough".
*Don't use the gets() function because it's unsafe (risk of buffer overflow).
I usually wrap it in a function which controls the length of the input.

Program ignores some functions in order

I've created a variable-sized array (VLA) and I want to fill it with using fgets functions in a for loop. However program passes the some functions and it strangely ignores the first fgets action.
My code is;
void editorMain(void)
{
printf("Please enter the number of items: ");
scanf("%u", &itemQuantity);
printf("\n\n");
char itemNames[itemQuantity][ITEM_NAME_LEN+1];
memset(&itemNames, 0, sizeof(itemNames));
printf("Please enter the names of items: \n");
char line[ITEM_NAME_LEN+1];
memset(&line, 0, sizeof(line));
for (int i = 0; i < itemQuantity; ++i) {
printf("#%d: ", i + 1);
memset(&line, 0, sizeof(line));
fgets(line, ITEM_NAME_LEN+1, stdin);
for (int a = ITEM_NAME_LEN+1; a >= 0; --a) {
if (line[a] == '\n') {
line[a] = 0;
}
else;
}
snprintf(itemNames[i], ITEM_NAME_LEN+1, "%s", line);
}
...
}
And it outputs;
Please make a choice: 2
Please enter the number of items: 4
Please enter the names of items:
#1: #2: Marlboro
#3: Parliament
#4: Winston
Please enter the prices of items:
#1: 25
#2: 950
#3: 1000
#4: 800
................... AVAILABLE ITEMS oo
# Item Name Price
= ========= =======
1. 0.25 TL
2. Marlboro 9.50 TL
3. Parliament 10.00 TL
4. Winston 8.00 TL
Enter your item selection:
What's your suggestion?
The scanf("%u" call reads up to the newline character, but leaves that character in the input stream. When you call your first fgets, it just reads that newline character and gives you an empty string.
There are many ways to clear the input stream, in this case you could just add a dummy call to fgets after your scanf.

Resources