This program is meant to display a video game inventory based off of the user's input. The user needs to determine how long the loop runs with num_of_games. We have to use a switch loop to determine the genre. It compiles but it does not display correctly.
#include <stdio.h>
#include <string.h>
#define MAX 16
typedef enum genre_type{ action = 0, rpg = 1, simulation = 2,
strategy = 3, sports = 4} genre_t;
typedef struct{
char title[MAX];
int num_sold;
genre_t genre;
}game_s;
// suppose to use functions and enum list for determining the genre
void get_game_info(game_s *ptr);
void display_inventory(game_s game[], int num_of_games);
int
main(void){
int i=0, num_of_games;
int c_game=0;
game_s game[num_of_games];
printf("How many games are there in inventory? ");
scanf("%d", &num_of_games);
fflush(stdin);
printf("\n");
while(c_game < num_of_games){
printf("\n");
get_game_info(&game[c_game]);
c_game++;
}
printf("\n");
display_inventory(game, num_of_games);
return(0);
}
void get_game_info(game_s *ptr)
{
int i, str_len, genre, num_sold;
printf("Title of game <maximum of 15 characters>: ");
gets(ptr->title);
str_len = strlen(ptr->title);
if(str_len >= 15){
printf("Title will be truncated\n");
ptr->title[MAX]= '\0';
}
printf("Number sold: ");
scanf("%d", &ptr->num_sold);
fflush(stdin);
printf("Genre (0-action, 1-rpg, 2-simulation, 3-strategy, 4-sports): ");
scanf("%d", &ptr->genre);
fflush(stdin);
if(ptr->genre>4){
printf("Not a valid genre");
fflush(stdin);
}
}
void display_inventory(game_s game[], int num_of_games)
{
int i, genre;
printf("Title\t\t\t\t\t\tQuantity Sold\t\t\t\t\t\tGenre");
printf("\n=====\t\t\t\t\t\t=============\t\t\t\t\t\t=====\n");
for(i=0; i < num_of_games; i++){
switch(genre){
case action: printf("Action"); break;
case rpg: printf("RPG"); break;
case simulation: printf("Simulation"); break;
case strategy: printf("Strategy"); break;
case sports: printf("Sports"); break;
default: puts("Not a choice. Try again"); break;
I think this is the thing that is causing most of the problem. I don't know if I am calling the struct right with the game[I].title and others.
the problem is with the struct calls I think. If I use game_s[I].title I get "expected expression before" error and if I use game[I].title it doesn't print properly
printf("%s\t\t\t\t\t\t%d\t\t\t\t\t\t%s", game[i].title, game[i].num_sold, game[i].genre);
printf("\n");
}
}
}
I think the problem is game_s game[num_of_games]; because num_of_games isn't initiate.
It should be:
printf("How many games are there in inventory? ");
scanf("%d", &num_of_games);
game_s * game_s game = (game_s *) malloc(num_of_games * sizeof(game_s));
Your effort is commendable. You provide code and a good-faith attempt to understand why things don't work, but unfortunately, your code was riddled with errors. Not the least of which was your almost immediate invocation of Undefined Behavior by attempting to declare a VLA with an uninitialized num_of_games. No matter what you did in your code from that point forward, it was nothing but a dice-roll whether you actually processed something or SegFaulted.
Your lack of output in display_inventory (regardless what was actually in memory) is due the placement of your printf statements within the body of the switch statement. When you break from a switch you don't just jump below the default case, you jump out of the switch completely. So even if the rest of your code was correct, you would never produce output.
fflush(stdin) is wrong and invokes undefined behavior on all but windoze. It is only defined for seekable streams for the rest of the world -- don't use it. Instead, you can define a simple function to empty stdin and call it as required instead.
When you compile, always compile with compiler warnings enabled, and do not accept code until it compiles cleanly without warning. To enable warnings add -Wall -Wextra to your gcc compile string. (add -pedantic for several additional warnings). For VS (cl.exe on windoze), add /Wall. For clang, add -Weverything. Read and understand each warning. They will identify any problems, and the exact line on which they occur. You can learn as much about coding by simply listening to what your compiler is telling you as you can from most tutorials.
The number of errors in your code are too voluminous to itemize and speak to every point, so I have included comments inline below addressing the errors.
#include <stdio.h>
#include <stdlib.h> /* for EXIT_FAILURE */
#include <string.h>
#define MAX 16
typedef enum genre_type {
action = 0,
rpg = 1,
simulation = 2,
strategy = 3,
sports = 4
} genre_t;
typedef struct {
char title[MAX];
int num_sold;
genre_t genre;
} game_s;
// suppose to use functions and enum list for determining the genre
void get_game_info (game_s *ptr);
void display_inventory (game_s *game, int num_of_games);
void fflush_stdin();
int main (void) {
int num_of_games,
c_game = 0,
scnfrtn; /* scanf return - must always check EOF */
printf ("How many games are there in inventory? ");
for (;;) { /* loop until valid input or EOF (user cancels) */
if ((scnfrtn = scanf ("%d", &num_of_games)) == 1) {
fflush_stdin(); /* manually empty stdin */
break;
}
else if (scnfrtn == EOF) { /* user cancels? */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
/* handle error */
fprintf (stderr, "error: invalid input.\n");
fflush_stdin();
}
putchar ('\n'); /* don't printf a single-char */
/* declare VLA only AFTER num_of_games has a value */
game_s game[num_of_games];
memset (game, 0, sizeof game); /* optional, zero VLA */
while (c_game < num_of_games) {
putchar ('\n');
get_game_info (&game[c_game]);
c_game++;
}
putchar ('\n');
display_inventory (game, num_of_games);
return 0;
}
void get_game_info (game_s *ptr)
{
int scnfrtn; /* scanf return - must always check EOF */
size_t len = 0; /* strlen return is size_t */
printf ("Title of game <maximum of 15 characters>: ");
fgets (ptr->title, MAX, stdin); /* NEVER, NEVER, NEVER use gets */
len = strlen (ptr->title);
if (len && ptr->title[len-1] == '\n') /* check for trailing \n */
ptr->title[--len] = '\0'; /* overwrite with nul-character */
else /* warn of truncation */
fprintf (stderr, "error: title too long, truncated.\n");
for (;;) { /* loop until valid input or EOF (user cancels) */
printf ("Number sold: ");
if ((scnfrtn = scanf ("%d", &ptr->num_sold)) == 1) {
fflush_stdin();
break;
}
else if (scnfrtn == EOF) {
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
fprintf (stderr, "error: invalid input.\n");
fflush_stdin();
}
for (;;) { /* loop until valid input or EOF (user cancels) */
printf ("Genre (0-action, 1-rpg, 2-simulation, "
"3-strategy, 4-sports): ");
if ((scnfrtn = scanf ("%d", (int*)&ptr->genre)) == 1) {
fflush_stdin();
break;
}
else if (scnfrtn == EOF) {
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
else if (ptr->genre > 4) /* additional check for genre */
fprintf (stderr, "error: invalid genre.\n");
else
fprintf (stderr, "error: invalid input.\n");
fflush_stdin();
}
}
void display_inventory (game_s *game, int num_of_games)
{
int i;
printf ("%11s%-24s %-24s %s\n========================="
"==============================================\n",
" ", "Title","Quantity Sold", "Genre");
for (i = 0; i < num_of_games; i++) {
switch (game[i].genre) {
case action:
printf ("%-11s", "Action");
break;
case rpg:
printf ("%-11s", "RPG");
break;
case simulation:
printf ("%-11s", "Simulation");
break;
case strategy:
printf ("%-11s", "Strategy");
break;
case sports:
printf ("%-11s", "Sports");
break;
default:
puts ("Not a choice. Try again");
break;
}
printf ("%-24s %-24u %d\n", game[i].title,
game[i].num_sold, game[i].genre);
}
}
void fflush_stdin()
{
for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}
}
(note: use printf minimum field with modifier to control spacing instead of a bunch of tabs smushed together)
Example Use/Output
$ ./bin/gamegenre
How many games are there in inventory? 3
Title of game <maximum of 15 characters>: first title
Number sold: 12
Genre (0-action, 1-rpg, 2-simulation, 3-strategy, 4-sports): 2
Title of game <maximum of 15 characters>: second title
Number sold: 13
Genre (0-action, 1-rpg, 2-simulation, 3-strategy, 4-sports): 1
Title of game <maximum of 15 characters>: third title
Number sold: 14
Genre (0-action, 1-rpg, 2-simulation, 3-strategy, 4-sports): 3
Title Quantity Sold Genre
=======================================================================
Simulation first title 12 2
RPG second title 13 1
Strategy third title 14 3
(note: I didn't know where you intended your genre descriptions to go, so they are simply output at the beginning of each line above)
Look things over and let me know if you have further questions.
Related
I hope that someone can help me since I'm clueless about the problem in my code. I am a new starter in programming and a week ago I tried an exercise which I completely failed. I tried to compare the elements in the array with the input text on the compiler. But the ("if") is always giving a false. Even if I write down an existing element of the array on the compiler. I know that there must be a lot of logical mistakes but I am really helpless. I am really looking for your answers. Thanks for the help!
#include <stdio.h>
#include <stdlib.h>
struct category {
char item_name[50];
int item_quantity;
float item_price;
};
void items_search (struct category a[], int length)
{
char answer[] = { "yes" };
int number = 0;
int item_quantity = 0;
char name[200];
int i = 0;
printf ("What are you looking for?\n");
scanf ("%s", &name);
if (a[i].item_name == name) {
printf ("What is the desired number of the item?");
scanf ("d", &number);
fflush (stdin);
if (number == a[i].item_quantity) {
printf ("Order is possible. Should the order be placed?\n");
scanf ("%s", &answer);
fflush (stdin);
if (answer == "yes") {
puts ("The order is placed");
}
else {
puts ("Order was cancelled");
}
}
else {
printf ("There are only %d pieces of the item. Should the "
"order be canceled?\n", a[i].item_quantity);
scanf ("%s", &answer);
fflush (stdin);
if (answer == "yes") {
puts ("Order was cancelled. Should be bought later?");
scanf ("%s", &answer);
fflush (stdin);
if (answer == "yes") {
puts ("The order is placed");
}
else {
puts ("The order was cancelled");
}
}
else {
puts ("The order is placed");
}
}
}
else {
printf ("The specified item does not exist\n");
}
}
int main (void)
{
struct category laptops[] = {
{"Asus_365", 7, 499.00},
{"Lenovo_L49", 30, 699.91},
{"HP_Alien", 20, 649.99},
{"Acer Alpha touch", 10, 899.99}
};
items_search (laptops, sizeof (laptops) / sizeof (struct category));
}
You should not compare a pointer to a string:
if(answer=="yes"){
Instead, you should use strncmp:
if(strncmp( answer, "yes", 3) == 0)
For starters these calls of scanf
scanf ("%s", &name);
and
scanf ("%s", &answer);
are incorrect. You should write
scanf ("%s", name);
and
scanf ("%s", answer);
Arrays do not have the comparison operator. So in these if statements
if (a[i].item_name == name) {
and
if (answer == "yes") {
there are compared two pointers to first elements of two different arrays (due to the implicit conversion of array designators to pointers to their first elements) that occupy different extents of memory. So their comparison will always evaluate to logical false.
Instead you need to use the C string function strcmp like
if ( strcmp( a[i].item_name, name ) == 0 ) {
and
if ( strcmp( answer, "yes" ) == 0 ) {
Also calls of the function fflush with the stream stdin
fflush (stdin);
has undefined behavior. Remove such calls.
Pay attention to that the function declaration does not make a great sense relative to its definition because within the function there is used only the first element of the passed array.
There are a large number of small problems in your code. The most critical are the attempts to use == for string-equality instead of strcmp(). The next being the inclusion of '&' before the array name when using scanf(). Upon access, an array is converted to a pointer to its first element, so there is no need for '&' before the array name in scanf() after "%s". See C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
Another issue is you must protect your array bounds when using the "%s" conversion specifier. It cannot be used safely otherwise. Why? "%s" will write as many non-whitespace characters as it is provided to the address given in the argument list. If given 500 bytes of shell-code, it will happily write all 500 bytes corrupting your stack everywhere "%s" is used with scanf(). (especially with answer which you declare as only 4-bytes long -- which is horribly insufficient) LESSON - Never skimp on buffer size!
To use the field-width modifier with scanf() when using "%s" you add the maximum number of bytes to read (not including the nul-terminating character), e.g. "199s" when writing to name. This ensures scanf() will not attempt to write more than 199 characters to name (plus the nul-terminating character)
All of this (and a lot more) are the pitfalls of scanf() and are why it is recommended to take all user input using fgets() or POSIX getline(). Provided a sufficiently sized buffer, the line-oriented input functions will consume an entire line of data. This eliminates all the problems inherent with scanf() and the only thing you need do is to trim the trailing '\n' read and included in the buffer they fill. This is trivial using strcspn(). It simply counts the number of initial characters that are not in its reject string -- providing an easy way to know how long the string is up to the '\n'. A better way to prompt, read/VALIDATE and trim the '\n' is:
fputs ("What are you looking for?: ", stdout); /* no conversion */
/* read/VALIDATE all input with fgets..
* it will consume a line at-a-time
* (given sufficient storage)
*/
if (!fgets (name, MAXNM, stdin)) {
return 0;
}
name[strcspn (name, "\n")] = 0; /* trim '\n' from end of name */
The other issue you have is using fflush(stdin);. The way you are using it is Undefined according to the C standard. fflush is not defined for input streams, see: C11 Standard - 7.21.5.2 The fflush function(p2) and fflush(stdin); leads to undefined behaviour on most systems.. It is not portable. For example, on Linux, fflush is only valid on seekable streams. Which only applies to stdin if a file is redirected as input (e.g. ./prog <somefile). See: fflush(3) - Linux manual page. Full discussion Standard C and POSIX leave fflush(stdin) as undefined behaviour
(MS is the only compiler I know of that provides a Non-Standard implementation that does clear stdin the way you are using it. -- Don't do that, it is virtually 100% non-portable to any other compiler)
I won't go over all the points detailed in the other good answers. But one additional note as you are learning to program. Always compile with warnings enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables). You can add -Werror to have all warnings treates as errors (recommended) For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. The warnings will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.
To put things altogether for you what I've done is just to change your implementation to use fgets() instead of scanf() while leaving your code commented out below so you can easily see exactly what changes are necessary. I have commented the code heavily so you can understand why the changes were made. I also changed the return type from void to int so you can provide a meaningful return indicating whether your function succeeded or failed. You will see how that benefits your code in main().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXINM 50 /* if you need a constant, #define one (or more) */
#define MAXNM 200
struct category {
char item_name[MAXINM];
int item_quantity;
float item_price;
};
/* choose a meaningful return type that can indicate whether
* the function succeeded or failed. (0 - not found, 1 - found)
*/
int items_search (struct category a[], int length)
{
char answer[MAXNM] = "yes", /* a string-literal initializer is fine */
name[MAXNM] = "",
tmp[MAXNM]; /* temporary array for input use */
int i = 0, /* group declarations by type */
// item_quantity = 0, /* UNUSED VARIABLE */
number = 0,
transaction = 0; /* flag indicating success/failure */
fputs ("What are you looking for?: ", stdout); /* no conversion */
/* read/VALIDATE all input with fgets..
* it will consume a line at-a-time
* (given sufficient storage)
*/
if (!fgets (name, MAXNM, stdin)) {
return 0;
}
// scanf ("%s", &name); /* scanf is full of pitfalls */
/* no & before name, it's a pointer */
name[strcspn (name, "\n")] = 0; /* trim '\n' from end of name */
for (; i < length; i++) { /* loop over each item in struct */
if (strcmp (a[i].item_name, name) == 0) { /* strcmp for equality */
fputs ("Desired number of the item?: ", stdout);
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
/* minimum validation of conversion with sscanf,
* for full error reporting, use strtol()
*/
if (sscanf (tmp, "%d", &number) != 1) {
fputs ("error: invalid integer input.\n", stderr);
return 0;
}
// scanf ("d", &number);
// fflush (stdin); /* defined for "seekable" streams */
if (number <= a[i].item_quantity) { /* less than or equal */
printf ("Order is possible.\n"
"Total Price: $%.2lf\n"
"Should the order be placed?: ",
number * a[i].item_price);
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
// scanf ("%s", &answer);
// fflush (stdin);
if (strcmp (answer, "yes") == 0) {
puts ("The order is placed");
}
else {
puts ("Order was cancelled");
}
}
else {
printf ("There are only %d pieces of the item.\nShould the "
"order be canceled?: ", a[i].item_quantity);
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
answer[strcspn (answer, "\n")] = 0; /* trim '\n' */
// scanf ("%s", &answer); /* answer is already a pointer */
// fflush (stdin);
if (strcmp (answer, "yes") == 0) { /* strcmp for equality */
puts ("Order was cancelled. Should be bought later?");
/* read/VALIDATE using fgets() */
if (!fgets (tmp, MAXNM, stdin)) {
return 0;
}
answer[strcspn (answer, "\n")] = 0; /* trim '\n' */
// scanf ("%s", &answer);
// fflush (stdin);
if (strcmp (answer, "yes") == 0) { /* strcmp */
puts ("\nThe order for later is placed");
}
else {
puts ("\nThe order was cancelled");
}
}
else {
puts ("The order is placed");
}
}
transaction = 1; /* set transaction complete flag */
}
// else { /* not needed here */
// printf ("The specified item does not exist\n");
// }
}
return transaction; /* return transaction status */
}
int main (void)
{
struct category laptops[] = {
{ "Asus_365", 7, 499.00 },
{ "Lenovo_L49", 30, 699.91 },
{ "HP_Alien", 20, 649.99 },
{ "Acer Alpha touch", 10, 899.99 }
};
if (!items_search (laptops, sizeof laptops / sizeof *laptops)) {
fputs ("error: incomplete transaction.\n", stderr);
}
}
Example Use/Output
$ ./bin/transaction
What are you looking for?: HP_Alien
Desired number of the item?: 2
Order is possible.
Total Price: $1299.98
Should the order be placed?: yes
The order is placed
or
$ ./bin/transaction
What are you looking for?: HP_Gorilla
error: incomplete transaction.
Look things over and let me know if you have any further questions. NOTE: this isn't the only way to approach this, just one way that improves over the UNVALIDATED use of scanf() and eliminates the potential for exploit through buffer overflow from the unlimited use of "%s".
Where to start...I feel like its the 80's again
buffer overruns = BAD
scanf does no validation
there are other issues with your code I leave that as an exercise for you to figure out the differences.
here is a more functional version
...
struct category {
const char* item_name; // const is good
int item_quantity;
double item_price; // float was too small
};
void items_search(category Category[])
{
char answer[4] = { "\0" }; // buffer was too small before
int number = 0;
int item_quantity = 0;
char name[200] = { "\0" };
int item = -1, Index = 0;;
printf("What are you looking for?\n");
scanf_s("%199s", &name, _countof(name)); // validation!
bool loop = true;
for (; nullptr != Category[Index].item_name && loop; Index++) {// does lower case comparison
if (_stricmp(Category[Index].item_name, name) == 0) {
item = Index;
loop = false;
}
}
if (-1 != item) { // did we find the item ?
printf("What is the desired number of the item?");
scanf_s("%d", &number);
fflush(stdin);
if (number <= Category[item].item_quantity) { // you only accepted exact answer
printf("Order is possible. Should the order be placed?\n");
scanf_s("%3s", &answer, _countof(answer));
fflush(stdin);
if (_stricmp(answer, "yes") == 0) {
puts("The order is placed");
}
else {
puts("Order was cancelled");
}
}
else {
printf("There are only %d pieces of the item. Should the order be canceled ? \n", Category[item].item_quantity);
scanf_s("%3s", &answer, _countof(answer));
fflush(stdin);
if (_stricmp(answer, "yes") == 0) {
puts("Order was cancelled. Should be bought later?");
scanf_s("%3s", &answer, _countof(answer));
fflush(stdin);
if (_stricmp(answer, "yes") == 0) {
puts("The order is placed");
}
else {
puts("The order was cancelled");
}
}
else {
puts("The order is placed");
}
}
} else {
printf("The specified item does not exist\n");
}
}
the rest should be clear
Also in main
struct category laptops[] = {
{"Asus_365", 7, 499.00},
{"Lenovo_L49", 30, 699.91},
{"HP_Alien", 20, 649.99},
{"Acer Alpha touch", 10, 899.99},
{nullptr,0,0 }
};
items_search(laptops); // see items_search above
I am making a typedef to a structure for a "person" The person has a name, ssn and yearOfBirth. I am getting errors I do not understand with my for loops.
[Error] cannot convert 'person_t' to 'person_t*' for argument '1' to
'void getOnePerson(person_t*)'
This is the first file:
#include <stdio.h>
#include <string.h>
#include "structures.h"
void getOnePerson(person_t *p)
{
printf("Enter full name: ");
scanf("%99[^\n]", p -> name);
printf("Enter ssn: ");
scanf("%99[^\n]", p -> ssn);
printf("Enter year of birth: ");
scanf("%d", &p -> yearOfBirth);
}
void printOnePerson(person_t p)
{
printf("%s:", p.name);
printf("%s:", p.ssn);
printf("%s\n", p.yearOfBirth);
}
void getPeople(person_t p[], int numOfPeople)
{
for(int i = 0; i < sizeof(p); i++)
{
getOnePerson(p[i]);
}
}
void printPeople(person_t p[], int numOfPeople)
{
for(int i = 0; i < sizeof(p); i++)
{
printOnePerson(p[i]);
}
}
This is my structure file:
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct
{
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
First of all, it seems to be pointers and references task. You may need to read this to understand them. In other words, cannot convert person_t to person_t* means you are trying to use your object person instead of reference to that specific person. * means reference, so you need to pass an address to it using &. Im not best explainer, check out the link instead and all answers, not only accepted one.
Code seems quite messy, I tried to fix it to compilable code, although I dont have C compiler (you may need to edit/fix according to your homework details):
#include <stdio.h>
#include <string.h>
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct
{
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
int main()
{
person_t people[NUM_PEOPLE];
printf("Get people\n");
getPeople(&people, 3);
printf("\nPrint people\n");
printPeople(people, 3);
return 0;
}
void getOnePerson(person_t *person)
{
printf("Enter full name: ");
scanf("%s", person -> name);
printf("\nEnter ssn: ");
scanf("%s", person -> ssn);
printf("\nEnter year of birth: ");
scanf("%s", person -> yearOfBirth);
}
void printOnePerson(person_t p)
{
printf("%s:%s:%d\n", p.name, p.ssn, p.yearOfBirth);
}
void getPeople(person_t *person[], int num)
{
int i;
for(i=0; i<num; i++)
{
getOnePerson(&person[i]);
}
}
void printPeople(person_t person[], int num)
{
int i;
for(i=0; i<num; i++)
{
printOnePerson(person[i]);
}
}
So, briefly, your getPeople(person_t *person[], int num) function's first parameter is person_t *person[], therefore you need to pass a &people. Same as getOnePerson(person_t *person) parameter person_t *person means you need to pass address to a single person object &person[i]. The meaning behind them that using references, you can edit the values in these objects directly in the function. While printPeople(person_t person[], int num) and printOnePerson(person_t p) are used for reading (not editing) thereby you can pass values themselves.
You have such a large number of small problems, it is difficult to know where to begin. First a nit, you never include spaces around "->" when referencing a structure member. Use p->name, not p -> name. Continuing...
You fail to validate the return of scanf. You must check the return Every Time, or you are tempting Undefined Behavior. You also must change "%99[^\n]" to " %79[^\n]" because neither "%c" or "%[...]" consume leading whitespace. Failing to add the " " before %12[^\n] would make it impossible to read p->ssn and lead to a matching failure reading p->yearOfBirth.
Note the change from 99 to 79. You #define NAME_SIZE 80 and declare char name[NAME_SIZE];, what do you think you are doing using a field-width modifier of 99 when at most 79 characters can be stored in name? (You have the same problem with #define SSN_SIZE 13). You use the field-width modifier with scanf to protect your array bounds. Setting the *field-width modifier greater than your array size (-1) removes the protection it should provide altogether.
Your failure to check the return of scanf and handle the three cases of return necessary will lead to Undefined Behavior if the user accidentally makes a single error in input. Failure to check the return of scanf is one of the most common pitfall new C programmer fall into. It is mandatory for every user input. Otherwise, you can have no confidence your code is actually processing valid data.
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 canceled 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 (early versions));
(return < expected No. of conversions) a matching or input failure occurred. For a matching failure you must account for every character left in your input buffer. (scan forward in the input buffer reading and discarding characters until a '\n' or EOF is found); and finally
(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, within a needed range, etc..).
A short function implementation to empty all remaining characters in stdin in the event of matching failure could be as simple as:
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
(implementing in your code is left as an exercise for you)
Further, using type void as the return of an input function makes no sense. You must choose your return to provide the return of required information AND provide an indication of whether the input succeeded or failed. Using void for getOnePerson() means you have no way of knowing whether you received all valid input, or just received name, but not ssn, or if the user simply generated a manual EOF canceling input at each prompt. A simple integer return is all you need (e.g. return 0; on failure or return 1; only after all 3-inputs are validated) You could do something like:
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
/* validate each input for all 3 cases */
fputs ("\nEnter full name: ", stdout); /* no need for printf, no conversion */
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1; /* indicates all 3 input successfully received */
}
(note: input is complete when EOF is encountered, either manually generated by the user or encountered in the input stream)
void is also meaningless as a return for getPeople(). You can't use a for loop and just assume all inputs were successful, instead, you need to take input only while input is available, while protecting your array bounds, and then return the number of input actually received (which may be less than NUM_PEOPLE). Further, choose your type properly. For counters, size_t is the proper type (you can't have a negative number of persons), e.g.
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
When you pass an array as a parameter to a function, the array is converted to a pointer to the first element. So when you do sizeof(p) within a function -- that is not what you want and does not provide the number of elements in the array referenced by p -- what it does provide is sizeof(a_pointer), which is fixed by your compiler (e.g. 8-bytes on x86_64, 4-bytes on x86). You pass numOfPeople -- use it, e.g.
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
You will also want to fix printf("%s\n", p.yearOfBirth); (yearOfBirth is not a string...)
Your header is fine, but it is missing something. Always include header guards around the content of your header files to prevent multiple inclusions of the file, e.g.
#ifndef mystructures_h
#define mystructures_h 1
...
/* your header content */
...
#endif
(note: the 1 isn't required, but if you are defining a constant, it is never a bad idea to give it an affirmative value of your choosing)
There are probably more that were corrected, but those were the major points. Putting it altogether, you could do:
structures.h
#ifndef mystructures_h
#define mystructures_h 1
#include <stdio.h>
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct {
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
size_t getPeople (person_t *p, size_t numOfPeople);
void printPeople (person_t *p, size_t numOfPeople);
#endif
(can you figure out why #include <stdio.h> was moved from structures.c into structures.h? do you know why the function prototypes for getPeople() and printPeople() are required in the header and not the rest?)
structures.c
#include "structures.h"
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
fputs ("\nEnter full name: ", stdout);
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1;
}
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
void printOnePerson (person_t p)
{
printf("%s:", p.name);
printf("%s:", p.ssn);
// printf("%s\n", p.yearOfBirth);
printf("%d\n", p.yearOfBirth);
}
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
A short test program peopletest.c
#include "structures.h"
int main (void) {
person_t people[NUM_PEOPLE] = {{ .name = "" }};
size_t npeople = getPeople (people, NUM_PEOPLE);
printPeople (people, npeople);
}
Example Use/Output
$ ./bin/peopletest
Enter full name: Person A. One
Enter ssn: 123456789
Enter year of birth: 2001
Enter full name: Person B. Two
Enter ssn: 234567890
Enter year of birth: 2002
Enter full name: Person C. Three
Enter ssn: 345678901
Enter year of birth: 2003
Enter full name: (input complete)
Stored People
Person A. One:123456789:2001
Person B. Two:234567890:2002
Person C. Three:345678901:2003
Look things over and let me know if you have further questions.
The code that I'm trying to write is supposed to take given data by the user and put it into a data structure, which works perfectly. What is going wrong is when I try to remove a entry( if(choice == 2). All it has to do is take that entry and make it empty but I didn't like the idea of empty space. The for loop that I tried to make was to take the top entry and move everything down one but what it does is take the first entry and copy it to the second, leaving the others alone. Any help on how to take the data and just delete the entry to move them down one would be great.
#include <stdio.h>
#include <string.h>
typedef struct book { //This is the data structure
char personName[15];
char personLname[15];
char phoneNumber[15];
char null[4];
} Book;
static int loopValue;// this is going to loop the program forever until the
//loop is broke
main()
{
int index = 0;
int choice;
Book *ptrBook = (Book*)malloc(sizeof(Book));
int i,j;
int stopValue = 1;
while(stopValue=0)
{
printf("\t\tPhone Book Application\n");
printf("\t1. Add Contact\t\t2. Remove Contact\n");
printf("\t3. Show Contacts\t10. Exit\n");
printf("\tChoice: ");
scanf("%i",&choice);
printf("\n\n");
if(choice == 1) // this is the add person, it takes the print if
/and puts it into the data structure
{
ptrBook = (Book*)realloc(ptrBook, sizeof(Book)*(index + 1));
printf("What is the FIRST name: ");
scanf("%s",ptrBook[index].personName);
printf("What is the LAST name: ");
scanf("%s",ptrBook[index].personLname);
printf("What is the number: ");
scanf("%s",ptrBook[index].phoneNumber);
printf("\nAdded to the Phone Book.\n");
printf("\nName: %s %s\n",ptrBook[index].personName,
ptrBook[index].personLname);
printf("Phone Number: %s",ptrBook[index].phoneNumber);
index++;
}
else if (choice == 2) // this removes people from the data
//structure
{
loopValue == 0;
printf("Who would you like to remove?\n\n");
for(i=0;i<index;i++) // this loops prints out the names to
//choose from
{
printf("%i. %s %s\n",i+1,ptrBook[i].personName,ptrBook[i].personLname);
}
printf("Who would you like to remove? ");
scanf("%i",choice);
for(i=choice;i<0;i--) //THIS IS WHAT NEED HELP WITH PLZ
{ //
strcpy(ptrBook[i-2].personName,ptrBook[i-1].personName); //
strcpy(ptrBook[i-2].personLname,ptrBook[i-1].personLname); //
strcpy(ptrBook[i-2].phoneNumber,ptrBook[i-1].phoneNumber); //
} `//
printf("\n");
scanf("%i",&choice);
}
if(choice == 3) // this loops to print out all the values in the
//data structure
{
for(i=0;i<index;i++)
{
printf("%s %s\n",ptrBook[i].personName,ptrBook[i].personLname);
printf("%i. %s\n\n\n",index,ptrBook[i].phoneNumber);
}
}
else if(choice == 4)
{
//make code to sort names
}
else if(choice == 5)
{
//find a phone number for a given name
}
else if(choice == 6)
{
//random person for you to call
}
else if(choice== 7)
{
//delete everyone
}
else if(choice == 8) // this exits the program by changing the
//loop variable to something that makes the loop false
{
printf("Exiting");
stopValue = 0;
}
}
}
Your code has a LOT of issues. In the future, please make sure to clean up your code really thoroughly before posting a question. I've taken the time to clean up a lot of it because I hate myself, so I hope you appreciate. First, a bug list:
1 and 2) Your condition in the while loop is actually an assignment: please turn on warnings on your compiler, because they will catch stuff like this. It's also erroneous with your initalization of stopValue.
3) The loopValue is used inconsistently throughout the code, and is not the thing that controls your while loop.
4) Inside if (choice == 2), loopValue == 0 means compare it to 0, not set it to 0. That has no effect on your program, but you've made the =, == error twice now. That's really basic stuff that you absolutely have to get a grip on.
5) In the same if section, your scanf where you set choice didn't have & in front of choice, meaning the variable wouldn't actually be set to the value typed in.
6) Same section, the last printf and scanf are probably not meant to be there? In any case they bring the input to an unintuitive halt, which would render the program useless to anyone who didn't have the source code.
7) The if (choice == 3) wasn't an else if.
8) The quit option is 8 in the source code, but the menu says it's 10.
After all that, here's the code (with a lot of stuff that didn't have anything to do with the problem you were having taken out)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct book { //This is the data structure
char personName[15];
char personLname[15];
char phoneNumber[15];
char null[4];
} Book;
static int loopValue;
int main(void) {
int index = 0;
int choice;
Book *ptrBook = (Book*) malloc(sizeof(Book));
int i, j;
int stopValue = 0; // was 1
while (stopValue == 0) { // was =
printf("\t\tPhone Book Application\n");
printf("\t1. Add Contact\t\t2. Remove Contact\n");
printf("\t3. Show Contacts\t8. Exit\n");
printf("\tChoice: ");
scanf("%i", &choice);
printf("\n\n");
if (choice == 1) {
ptrBook = (Book*) realloc(ptrBook, sizeof(Book)*(index + 1));
printf("What is the FIRST name: ");
scanf("%s",ptrBook[index].personName);
printf("What is the LAST name: ");
scanf("%s",ptrBook[index].personLname);
printf("What is the number: ");
scanf("%s",ptrBook[index].phoneNumber);
printf("\nAdded to the Phone Book.\n");
printf("\nName: %s %s\n",ptrBook[index].personName,
ptrBook[index].personLname);
printf("Phone Number: %s",ptrBook[index].phoneNumber);
index++;
} else if (choice == 2) {
loopValue = 0; // was ==
printf("Who would you like to remove?\n\n");
for (i = 0; i < index; i++) {
printf("%i. %s %s\n", i+1, ptrBook[i].personName, ptrBook[i].personLname);
}
printf("Who would you like to remove? ");
scanf("%d", &choice); // didn't have &
for (int i = (choice - 1); i < (index - 1); i++) {
ptrBook[i] = ptrBook[i + 1];
}
index--;
// used to be redundant/confusing/wrong printf and scanf
} else if (choice == 3) { // used to not be else if
for (i = 0; i<index; i++) {
printf("%s %s\n", ptrBook[i].personName, ptrBook[i].personLname);
printf("%i. %s\n\n\n", index, ptrBook[i].phoneNumber);
}
} else if (choice == 8) { // Should've been 10
printf("Exiting");
stopValue = 1;
}
}
}
There are so many little problems with your code that it is hard to know where to begin. Probably the most glaring is the failure to validate the the return of scanf and your failure to empty stdin of any extraneous characters after each input leaving your program just waiting to enter an endless-loop on the first errant keystroke. You cannot use the "%s" conversion specifier to read names (many are two-part names) and you will stop reading at the first whitespace and the remainder of the name will be taken as the next input. The same applies to reading a phone number (which would trigger an endless loop if a whitespace and punctuation is included).
Which brings up a larger recommendation of using fgets instead of scanf for all line-oriented input (such as taking user input). You can use sscanf to parse integer values from the input and you avoid a whole host of pitfalls inherent in new C programmers taking input with scanf. Don't skimp on buffer size. For example #define MAXC 1024 and then char buf[MAXC]; would be adequate for most user input.
Next, when doing a menu, consider using switch () { case 1: ...; break; case 2: ...; break } instead of the long chain of if (...) {...} else if (...) {...}, etc.... It provides more readable code;
There are many, many more corrections made to your code which are explained in the comments inline below, e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NULLSZ 4 /* if you need a constant #define one (or more) */
#define MAXNMNUM 15
typedef struct book {
char personName[MAXNMNUM]; /* use constants for array sizes */
char personLname[MAXNMNUM];
char phoneNumber[MAXNMNUM];
char null[NULLSZ]; /* no clue what this is for */
} Book;
/* helper function to empty extraneous characters from stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != EOF && c != '\n')
c = getchar();
}
int main (void) /* See http://port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p1 */
{
int index = 0;
Book *ptrBook = malloc (sizeof *ptrBook); /* do NOT cast malloc return */
for (;;) { /* loop continually */
int rtn, /* return for scanf */
choice = 0;
/* you only need a single fputs - not muliple printf calls
* there is no conversion taking place, so printf not neeeded.
*/
fputs ( "\nPhone Book Application\n\n"
" 1. Add Contact 2. Remove Contact\n"
" 3. Show Contacts ... 8. Exit\n"
"\nChoice: ", stdout);
rtn = scanf ("%d", &choice); /* ALWAYS VALIDATE EVERY INPUT */
if (rtn == EOF) { /* check if user canceled input with Ctrl+d */
fputs ("(user canceled input)\n", stderr);
break;
}
else if (rtn < 1) { /* check for matching or input failure */
fputs ("error: invalid integer input.\n", stderr);
empty_stdin(); /* always empty stdin before next input */
continue;
}
empty_stdin(); /* ditto */
if (choice == 1) { /* add book */
char *p;
/* don't realloc every addition - very inefficient.
* don't realloc the pointer itself, use a temp pointer
* or you create memory leak on failure.
*/
void *tmp = realloc (ptrBook, sizeof *ptrBook * (index + 1));
if (!tmp) {
perror ("realloc-ptrBook");
break;
}
ptrBook = tmp; /* assign new block to ptrBook */
fputs ("What is the FIRST name: ", stdout);
/* don't read line-oriented input with scanf, use fgets
* if (scanf ("%s", ptrBook[index].personName) != 1) {
*/
if (!fgets (ptrBook[index].personName, MAXNMNUM, stdin)) {
fputs ("(user canceled input.)\n", stderr);
break;
}
p = ptrBook[index].personName; /* set convenience pointer */
p[strcspn (p, "\r\n")] = 0; /* trim '\n' from end of str */
fputs ("What is the LAST name: ", stdout);
if (!fgets (ptrBook[index].personLname, MAXNMNUM, stdin)) {
fputs ("(user canceled input.)\n", stderr);
break;
}
p = ptrBook[index].personLname; /* set convenience pointer */
p[strcspn (p, "\r\n")] = 0; /* trim '\n' from end of str */
fputs ("What is the number: ", stdout);
if (!fgets (ptrBook[index].phoneNumber, MAXNMNUM, stdin)) {
fputs ("(user canceled input.)\n", stderr);
break;
}
p = ptrBook[index].phoneNumber; /* set convenience pointer */
p[strcspn (p, "\r\n")] = 0; /* trim '\n' from end of str */
printf ("\nAdded to the Phone Book.\n"
"\nName: %s %s\nPhone Number: %s\n",
ptrBook[index].personName,
ptrBook[index].personLname,
ptrBook[index].phoneNumber);
index++; /* increment index */
}
else if (choice == 2) { /* remove entry */
putchar ('\n');
for (int i = 0; i < index; i++) {
printf (" %d. %s %s\n", i + 1, ptrBook[i].personName,
ptrBook[i].personLname);
}
fputs ("\nWho would you like to remove? ", stdout);
rtn = scanf ("%d", &choice);
if (rtn == EOF) {
fputs ("(user canceled input)\n", stdout);
break;
}
else if (rtn < 1) {
fputs ("error: invalid integer input.\n", stderr);
empty_stdin();
continue;
}
else if (choice - 1 < 0 || index - 1 < choice - 1) {
fputs ("error: out of range of valid indexes.\n", stderr);
empty_stdin();
continue;
}
/* remvove entry with memmove copying over removed entry */
memmove (ptrBook + choice - 1, ptrBook + choice,
(index - choice) * sizeof *ptrBook);
index -= 1; /* decrement index */
/* realloc to remove entry */
void *tmp = realloc (ptrBook, index * sizeof *ptrBook);
if (!tmp) {
perror ("realloc-index");
break;
}
ptrBook = tmp;
}
if (choice == 3) { /* output the entries */
putchar ('\n');
for (int i = 0; i < index; i++) {
printf ("%s %s\n%d. %s\n\n", ptrBook[i].personName,
ptrBook[i].personLname, i + 1,
ptrBook[i].phoneNumber);
}
} else if (choice == 4) {
//make code to sort names
} else if (choice == 5) {
//find a phone number for a given name
} else if (choice == 6) {
//random person for you to call
} else if (choice == 7) {
//delete everyone
} else if (choice == 8) {
printf ("Exiting\n");
break;
}
}
}
(note: the use of p as a temporary pointer is simply for convenience and readability rather than having to type, e.g. the full ptrBook[index].personLname over and over and causing the strcspn command to span multiple lines)
Also, consider removing null from your struct (no clue what that is for) and consider adding index or nentries as a member so the number of entries is always part of the struct itself (which makes things much more convenient when passing or returning the struct (or pointer to it) to/from other functions)
Below note how the program can now recover from invalid input. Try the same input with your code at the menu, e.g. "I don't know" and see what happens...
Example Use/Output
$ ./bin/book_remove
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: I don't know
error: invalid integer input.
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Mark
What is the LAST name: Twain
What is the number: (444) 555-1212
Added to the Phone Book.
Name: Mark Twain
Phone Number: (444) 555-1212
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Samuel
What is the LAST name: Clements
What is the number: (444) 555-1213
Added to the Phone Book.
Name: Samuel Clements
Phone Number: (444) 555-1213
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Won Hung
What is the LAST name: Lo
What is the number: (444) 555-1214
Added to the Phone Book.
Name: Won Hung Lo
Phone Number: (444) 555-1214
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 1
What is the FIRST name: Fred
What is the LAST name: Flintstone
What is the number: (444) 555-1215
Added to the Phone Book.
Name: Fred Flintstone
Phone Number: (444) 555-1215
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 3
Mark Twain
1. (444) 555-1212
Samuel Clements
2. (444) 555-1213
Won Hung Lo
3. (444) 555-1214
Fred Flintstone
4. (444) 555-1215
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 2
1. Mark Twain
2. Samuel Clements
3. Won Hung Lo
4. Fred Flintstone
Who would you like to remove? 2
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 3
Mark Twain
1. (444) 555-1212
Won Hung Lo
2. (444) 555-1214
Fred Flintstone
3. (444) 555-1215
Phone Book Application
1. Add Contact 2. Remove Contact
3. Show Contacts ... 8. Exit
Choice: 8
Exiting
Look things over and make sure you understand what and why the changes were made. If not, then just ask for further clarification.
This is my code below:
enum DifficultyKind
{
Normal,
Hard,
Insane
} DifficultyKind;
typedef struct Target_Data
{
my_string name;
int hit_id;
int dollarvalue;
enum DifficultyKind difficulty;
} Target_Data;
enum DifficultyKind read_difficulty_kind (const char *prompt)
{
int temp;
enum DifficultyKind result;
printf("%s\n", prompt);
printf("\n");
printf("1: Normal Difficulty \n");
printf("\n");
printf("2: Hard Difficulty \n");
printf("\n");
printf("3: Insane Difficulty \n");
printf("\n");
temp = read_integer("Please make a selection between 1 and 3: \n");
if (temp < 1) {
printf("\n");
printf("You did not make a selection between 1 and 3\n");
printf("\n");
temp = read_integer("Please make a selection between 1 and 3: \n");
}
if (temp > 3) {
printf("\n");
printf("You did not make a selection between 1 and 3\n");
printf("\n");
temp = read_integer("Please make a selection between 1 and 3: \n");
}
result = temp - 1;
return result;
}
Target_Data read_target_data (const char *prompt)
{
Target_Data result;
enum DifficultyKind Difficulty;
printf("%s\n", prompt);
result.name = read_string("Enter name: ");
result.hit_id = read_integer("Enter hit ID: ");
if (result.hit_id < 0) {
printf("Please enter a value of 0 or higher \n");
result.hit_id = read_integer("Enter hit ID: ");
}
result.dollarvalue = read_integer("Enter $ value of target: ");
if (result.dollarvalue < 0) {
printf("Please enter a value of 0 or higher \n");
result.dollarvalue = read_integer("Enter $ value of target: ");
}
Difficulty = read_difficulty_kind("Please select the level of difficulty this bounty is from the below options:");
return result;
}
void print_target_data (Target_Data *toPrintData)
{
printf("\nDifficulty: %d, Target: %s, Hit ID: %i, $%i,\n", toPrintData->difficulty, toPrintData->name.str, toPrintData->hit_id, toPrintData->dollarvalue);
}
int main()
{
Target_Data *Target_Data;
read_target_data("Please enter the details of your bounty: ");
print_target_data(&Target_Data);
}
when the program runs and after entering the details, I get the below:
Please enter the details of your bounty:
Enter name: Jonathan
Enter hit ID: 10
Enter $ value of target: 500
Please select the level of difficulty this bounty is from the below options:
1: Normal Difficulty
2: Hard Difficulty
3: Insane Difficulty
Please make a selection between 1 and 3:
1
Difficulty: 10, Target: , Hit ID: 0, $0,
I have tried so many different ways and looked all over for a solution but don't really know what to do.
Why does the difficulty read out as the number I entered for hit ID... and the rest of the details don't even show?
this is also the warning message I get when I compile it:
BountyHunter.c:96:20: warning: incompatible pointer types passing
'Target_Data **' (aka 'struct Target_Data **') to parameter of type
'Target_Data *' (aka 'struct Target_Data *'); remove &
[-Wincompatible-pointer-types]
print_target_data(&Target_Data);
^~~~~~~~~~~~
BountyHunter.c:87:38: note: passing argument to parameter 'toPrintData' here
void print_target_data (Target_Data *toPrintData)
Someone please help!
You have a huge number of errors in your code, made much more difficult to identify given the lack of all necessary code needed to validate your reads. That said, I've reworked your example to use a statically declared Target_Data which is passed by address to read_target_data which eliminates the need to dynamically declare a struct within either read_target_data or read_difficulty_kind (which would likely over-complicate your learning at the moment)
I don't know if you come from java, or tried to copy java code, but toPrintData->name.str makes no sense at all.
I've also added err = -1 to your DifficultyKind enum to allow validation of the value returned within the code. It's up to you how you do it, but you need to validate each input to insure you are actually working with real values and not attempting to process uninitialized values.
(as a side note, C generally makes use of lower-case variable names, leaving camelCase and UpperCase names for java and C++, and reserving UPPERCASE names for macros, etc... Granted, it is style, so it is completely up to you)
With that said, here is the reworked example that I believe keeps with your intent. Look it over and let me know if you have any further questions. The input routines are simply calls to scanf (which should more properly be calls to fgets with subsequent calls to sscanf, but that is for another day)
#include <stdio.h>
#include <stdlib.h>
#define MAXC 128
typedef enum { err = -1, Normal = 1, Hard, Insane } DifficultyKind;
typedef struct {
char name[MAXC];
int hit_id;
int dollarvalue;
DifficultyKind difficulty;
} Target_Data;
DifficultyKind read_difficulty_kind (const char *prompt)
{
int rtn, temp;
printf ("%s\n\n"
" 1: Normal Difficulty\n"
" 2: Hard Difficulty\n"
" 3: Insane Difficulty\n\n", prompt);
while ((rtn = scanf (" %d", &temp)) != 1 || (temp < 1 || temp > 3)) {
if (rtn == EOF)
return err; /* trap cancel of input (ctrl+d, ctrl+z) */
fprintf (stderr, "error: invalid selection\n"
"Please make a selection between 1 and 3:\n\n"
" 1: Normal Difficulty\n"
" 2: Hard Difficulty\n"
" 3: Insane Difficulty\n\n");
}
return temp;
}
Target_Data *read_target_data (Target_Data *result, const char *prompt)
{
int rtn;
printf ("%s\n\nEnter name: ", prompt);
while ((rtn = scanf (" %127[^\n]%*c", result->name) != 1))
if (rtn == EOF) {
fprintf (stderr, "warning: input canceled, exiting.\n");
exit (EXIT_FAILURE);
}
printf ("Enter hit ID: ");
while ((rtn = scanf (" %d", &(result->hit_id)) != 1) ||
result->hit_id < 0) {
if (rtn == EOF) {
fprintf (stderr, "warning: input canceled, exiting.\n");
exit (EXIT_FAILURE);
}
fprintf (stderr, "Please enter a value of 0 or higher \n");
printf ("Enter hit ID: ");
}
printf ("Enter $ value of target: ");
while ((rtn = scanf (" %d", &(result->dollarvalue)) != 1) ||
result->dollarvalue < 0) {
if (rtn == EOF) {
fprintf (stderr, "warning: input canceled, exiting.\n");
exit (EXIT_FAILURE);
}
fprintf (stderr, "Please enter a value of 0 or higher \n");
printf ("Enter $ value of target: ");
}
if ((result->difficulty = read_difficulty_kind ("Please select the"
" level of difficulty from the options below:")) == err) {
fprintf (stderr, "warning: input canceled, exiting.\n");
exit (EXIT_FAILURE);
}
return result;
}
void print_target_data (Target_Data *toPrintData)
{
printf ("\nDifficulty: %d, Target: %s, Hit ID: %i, $%i,\n",
toPrintData->difficulty, toPrintData->name,
toPrintData->hit_id, toPrintData->dollarvalue);
}
int main (void) {
Target_Data Target_Data = { .name = "" };
read_target_data (&Target_Data,
"Please enter the details of your bounty: ");
print_target_data (&Target_Data);
return 0;
}
Example Use/Output
$ ./bin/difficultystruct
Please enter the details of your bounty:
Enter name: Some Funny Name
Enter hit ID: 123
Enter $ value of target: 234
Please select the level of difficulty from the options below:
1: Normal Difficulty
2: Hard Difficulty
3: Insane Difficulty
0
error: invalid selection
Please make a selection between 1 and 3:
1: Normal Difficulty
2: Hard Difficulty
3: Insane Difficulty
4
error: invalid selection
Please make a selection between 1 and 3:
1: Normal Difficulty
2: Hard Difficulty
3: Insane Difficulty
2
Difficulty: 2, Target: Some Funny Name, Hit ID: 123, $234,
Completely guessing, looking at the code structure, and assuming the read_xxx functions are well implemented, your main should be:
int main (void)
{
Target_Data Target_Data;
Target_Data = read_target_data("Please enter the details of your bounty: ");
print_target_data(&Target_Data);
}
I was working on this sample exercise, and everything works as I would like it to, but there is one behavior I don't understand.
When providing input: if I make consecutive invalid entries everything seems to work great. But if I enter a number different from 1,2,3 in the case of the first question, or 1,2 in the case of the second question, the program just sits there until a new input is given. If another invalid entry is made, it goes back to the error "invalid entry" message, and if an appropriate number is entered, everything moves along fine.
I do not understand why it stops to wait for a second input...anyone?
Thanks guys.
#include <stdio.h>
static int getInt(const char *prompt)
{
int value;
printf("%s",prompt);
while (scanf("%d", &value) !=1)
{
printf("Your entry is invalid.\nGive it another try: %s", prompt);
getchar();
scanf("%d", &value);
}
return value;
}
int main() {
int wood_type, table_size, table_price;
printf("Please enter " );
wood_type = getInt("1 for Pine, 2 for Oak, and 3 for Mahogany: ");
printf("Please enter ");
table_size = getInt("1 for large, 2 for small: ");
printf("\n");
switch (wood_type) {
case 1:
table_price = (table_size == 1)? 135:100;
printf("The cost of for your new table is: $%i", table_price);
break;
case 2:
table_price = (table_size == 1)? 260:225;
printf("The cost of for your new table is: $%i", table_price);
break;
case 3:
table_price = (table_size == 1)? 345:310;
printf("The cost of for your new table is: $%i", table_price);
break;
default:
table_price = 0;
printf("The cost of for your new table is: $%i", table_price);
break;
}
}
You most likely need to flush your input buffer (especially with multiple scanf calls in a function). After scanf, a newline '\n' remains in the input buffer. fflush does NOT do this, so you need to do it manually. A simple do...while loop works. Give it a try:
edit:
static int getInt(const char *prompt)
{
int value;
int c;
while (printf (prompt) && scanf("%d", &value) != 1)
{
do { c = getchar(); } while ( c != '\n' && c != EOF ); // flush input
printf ("Invalid Entry, Try Again...");
}
return value;
}
The blank line you get if you enter nothing is the normal behavior of scanf. It is waiting for input (some input). If you want your routine to immediately prompt again in the case the [Enter] key is pressed, then you need to use another routine to read stdin like (getline or fgets). getline is preferred as it returns the number of characters read (which you can test). You can then use atoi (in <stdlib.h>) to convert the string value to an integer. This will give you the flexibility you need.
example:
int newgetInt (char *prompt)
{
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* number of characters read */
size_t n = 0; /* numer of chars to read, 0 no limit */
static int num = 0; /* number result */
while (printf ("\n %s ", prompt) && (read = getline (&line, &n, stdin)) != -1)
{
if ((num = atoi (line)))
break;
else
printf ("Invalid Input, Try Again...\n");
}
return num;
}
If some invalid input is entered, it stays in the input buffer.
The invalid input must be extracted before the scanf function is completed.
A better method is to get the whole line of input then work on that line.
First, put that input line into a temporary array using fgets(),
then use sscanf() (safer than scanf because it guards against overflow).
#include <stdio.h>
int main(int argc, const char * argv[]) {
char tempbuff[50];
int result, d , value;
do
{
printf("Give me a number: ");
fgets( tempbuff, sizeof(tempbuff), stdin ); //gets string, puts it into tempbuff via stdin
result = sscanf(tempbuff, "%d", &value); //result of taking buffer scanning it into value
if (result < 1){ //scanf can return 0, # of matched conversions,
//(1 in this case), or EOF.
printf("You didn't type a number!\n");
}
}while (result < 1);
//some code
return 0;
}
Knowledge from: http://www.giannistsakiris.com/2008/02/07/scanf-and-why-you-should-avoid-using-it/