Program gets hung up when I use "delete" function - c

I'm trying to write a program where it'll take in info from a user, and the user will have options on what to do. The options are to add, modify, delete, display certain info, find the average, and exit.
I have written code for all of them but I can't get my delete function to work.
Here is my code:
void delete_student(struct students *list, int *count) //Delete's Selected student
{
int id4;
int k;
if (*count != 0)
{
printf("\nEnter the ID of the student you wish to delete: ");
scanf("%d\n", &id4);
for (int i = 0; i < *count; i++) //Searches for the selected student
{
k = i;
if (list[i].id == id4)
{
printf("\nStudent found.\nDeleting...\n");
for (int c = k; c < *count; c++) //Deletes the student if found
{
list[c].id = list[c + 1].id;
strcpy(list[c].name, list[c + 1].name);
list[c].age = list[c + 1].age;
strcpy(list[c].dept, list[c + 1].dept);
list[c].grade = list[c + 1].grade;
}
*count = *count - 1;
printf("\nDeletion Complete.\n");
break;
}
else
printf("\nStudent not found. Please try again.\n");
break;
}
}
else
printf("\nNo student's to delete.\n");
}
Edit:
When I go through the program, and I select to use this function, it'll ask me for the "ID" and then it does nothing and has a blinking cursor.
Could someone tell me what I'm doing wrong?
There's more code if you need it.
Is this how the last element would be deleted:
list[*count].id = 0;
strcpy(list[*count].name, NULL);
list[*count].age = 0;
strcpy(list[*count].dept, NULL);
list[*count].grade = 0;

Your scanf() statement has an incorrect format string. You don't need to add the trailing newline; scanf takes care of it. Change that line to scanf("%d", &id4); (no newline) and it should work.
I just wrote a small stub program that compares scanf with and without the newline, and it replicates your error.

You have a break statement at the end of your for loop that probably shouldn't be there. Just delete that one.
else
printf("\nStudent not found. Please try again.\n");
break;
} ^
|
+------ this one
That printf is a bit inaccurate too; it will get printed out on every iteration of the loop where you didn't happen to find a matching ID.

This loop:
for (int c = k; c < *count; c++)
should probably instead be:
for (int c = k; c < *count - 1; c++)
As written, it reads one past the end of the valid array. The results are undefined. And in particular, the strcpy calls may go quite badly. When it gets to the last entry in that loop, list[c+1] refers to an entry that does not exist (based on *count).

Your scanf hangs as it never gets to read your number. No \n needed. Use %d only.

Related

new to c problem with simple game problem with function

im new to c i try to make a little and very simple game of hangedman and i dont know why doesent work get error in gcc "expected declaration or statement at the end of input"
im new to c and ii try very hard to learn it.
im missing something? my function is not right? some advice to learn alghorytmically thinking?
thanx in advance for the hel you gonna give me
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//function to find letter in string
int findletter(char y)
{
char c;
int i;
char secret[] = "outcast";
i = 0;
scanf("%c", &c);
while (c != secret[i] && i < strlen(secret))
i++;
if(c == secret[i])
return (1);
else
return (0);
}
//confirmation letter
int guessed(char a)
{
int z;
char guess [6] = {0};
z = 0;
while(findletter(guess[z]) != 1 && findletter(guess[z]) < 6)
{
z++;
if(findletter(guess[z]) == 1)
return 1;
else
return 0;
//word guessed
int tryguess(char v)
{
int x;
x = 0;
while(findletter(guess[x]) == 0)
{
x++;
if(findletter(guess[x] == 1))
return 1;
else
return 0;
}
}
int main()
{
char secret[] = "outcast";
char letter;
int lives;
char guess [6] = {0};
int i;
lives = 10;
i = 0;
printf("welcome to the hanged man\n");
while(i < 6)
{
if((findletter(secret[i] == 1)))
printf("%c", secret[i]);
else
printf("*\n");
i++;
}
return 0;
}
Correction to your code...
int guessed(char a)
{
int z;
char guess [6] = {0};
z = 0;
while(findletter(guess[z]) != 1 && findletter(guess[z]) < 6)
{
z++;
if(findletter(guess[z]) == 1)
return 1;
else
return 0;
} // you forgot closing while loop here
} // function closing parenthesis
//word guessed
Advice:
I don't know how much you had practice and how much you had learned yet..but on observing your mistake above I would like to suggest that whenever you create function or loop always write its prototype first, let's say you want to create a function for adding two numbers..
STEP 1: write prototype
int add(int x, int y)
{
//then do your stuff here...
return 0;
}
This will eliminate you chances of making error of parentheses...
There are a lot of issues with this program, from both a syntax standpoint and a logical one.
General issues include:
Function guessed and its while loop are not closed (missing }).
There is a lot of unused code (functions and variables).
The line if((findletter(secret[i] == 1))) compares the character value of secret[i] with 1 and passes that result to findletter. This doesn't matter though since you don't use this argument, and take user input within the function.
You have hardcoded strings and lengths, which makes your program less dynamic and harder to change in the future.
Using while loops as guards (in the unused functions tryguess and guessed), that are always exited on the first iteration.
findletter simply checks if secret contains the character c, returning on the first occurrence.
It could be more clearly expressed as:
int findletter(char unused) {
char secret[] = "secret",
c;
scanf(" %c", &c);
for (size_t i = 0; i < strlen(secret); i++)
if (secret[i] == c)
return 1;
return 0;
}
With that said, findletter would be better if you passed both the secret and c as arguments, so that you can use it more generically, and decouple user input from the function itself.
(Or you could simply use the standard library function strchr which achieves a very similar goal.)
The pattern of
if (a == b)
return 1;
else
return 0;
can simply be reduced to
return a == b;
Aside from the issues above, the structure of your program doesn't make much sense. If our program worked, you'd basically be asking the player to guess a word of unknown length, one character of the word at a time. They can also simply guess any letter to display the current one. One could 'solve' the entire word "secret" by simply inputting 's' repeatedly.
The structure of a very basic hangman program is:
Select the word to be guessed. Select number of lives.
Create a blanked version of word to track progress. Display this blanked version, which indicates the length to the player.
Ask the player to guess a letter. Skip those already guessed.
Update all positions in the blanked version where letter appears in the word.
Decrement lives on miss, end game if out of lives.
Check if the amount of characters changed in the blank version matches the length of word.
Win condition, or return to step 3.
There are many different ways to achieve this, and there are likely thousands of examples online.
Here is a rough program that is about as simple as it gets. This showcases the usual structure and flow of a game of hangman.
#include <stdio.h>
#include <string.h>
size_t update_all(char *to, const char *from, size_t len, char g) {
size_t changed = 0;
for (size_t i = 0; i < len; i++)
if (from[i] == g) {
to[i] = g;
changed++;
}
return changed;
}
void play_hangman(const char *word, unsigned lives) {
size_t word_length = strlen(word),
blanked_length = 0;
char blanked[word_length + 1],
guess = '\0';
for (size_t i = 0; i < word_length; i++)
blanked[i] = '*';
blanked[word_length] = '\0';
while (lives) {
printf("The word: [%s]\n"
"(Lives = %u) Enter a guess: ",
blanked,
lives);
scanf(" %c", &guess);
if (strchr(blanked, guess)) {
printf("[%c]: Already guessed!\n", guess);
continue;
}
size_t found = update_all(blanked, word, word_length, guess);
blanked_length += found;
if (!found) {
printf("[%c]: NOT FOUND!\n", guess);
lives--;
} else
printf("[%c]: FOUND!\n", guess);
if (!lives)
puts("Out of lives! You lose!");
else if (blanked_length == word_length) {
printf("You win! Word is [%s].\n", word);
return;
}
}
}
int main(void) {
play_hangman("secret", 10);
}
Note that this program is far from perfect, as it doesn't fully keep track of guessed letters, so the player can guess the same wrong letter multiple times, and lose a life every time. To fix this, we would need even more state, collecting each guess the player makes, and use that data instead of the naive if (strchr(blanked, guess)).
It also makes use of the '*' character as a sentinel value, which would cause confusion if our word contained '*'. To fix this, we could use an array of boolean values indicating the correctly guessed letters in the word thus far, and use this to print our word character-by-character. Or we could restrict character inputs with functions like isalpha.
This program simply serves as an example that for a proper approximation of the typical "Hangman" you need to handle more game state than you have.
(Error handling omitted for brevity throughout this answer.)

How can I integrate modularity into my code in C?

I don't know much about modularity except it's basically dividing up your code into smaller groups of functions (this question is for the C language).
I'm doing an assignment where I can only see my source code working with one giant nested loop in "main" (no if statements are allowed for the assignment). In other words, if I try and use functions to cut up the code, I don't see how the necessary nested loop will work. But the assignment requires an attempt at modularity.
Thus, can anyone tell me how I might break up the following code into smaller modules without messing up its actual execution?
#include <stdio.h>
int main(void)
{
int counter = 0;
int marknum = 0;
int sectioncode, studentnumber;
int dummyvariable = 0;
int index;
int marks;
int total = 0;
do
{
printf("Enter a Section Code: ");
scanf("%d", &sectioncode);
while(sectioncode > 4 || sectioncode < 1)
{
printf("Invalid value entered. Must be 1 to 4, please re-enter: ");
scanf("%d", &sectioncode);
}
do
{
printf("Enter the Student's ID: ");
scanf("%d", &studentnumber);
while (studentnumber < 1 || studentnumber > 999999999)
{
printf("Invalid value entered. Must be 1 to 999999999. Please re-enter: ");
scanf("%d", &studentnumber);
}
while (sectioncode != 0)
{
while (counter < 5)
{
counter++;
marknum++;
printf("Enter mark%d: ", marknum);
scanf("%d", &marks);
total = total + marks;
}
printf("%09d's total mark is %d\n", studentnumber, total);
counter = 0;
marknum = 0;
sectioncode = 0;
}
dummyvariable = 1;
} while (dummyvariable = 0);
} while (sectioncode != 0);
return 0;
}
Also, how would I incorporate modularity for this one (same question basically):
#include <stdio.h>
int main(void)
{
int num; //User inputted number
int i; //Increment variable
char ch; //Check for characters variable
do //Begin "do while" loop
{
printf("\nEnter a number:"); //User prompt
scanf ("%d", &num); //Scan for user inputted integer
while ( (ch = getchar()) != '\n') //Scan for character, check for non-numeric input
{
printf("Invalid number entered. Please re-enter: "); //Error message and prompt for invalid user input
scanf ("%d", &num); //Scan for user inputted integer
} //Repeat loop if condition becomes true again
for (i=0; i<num; i++) //Begin "for" loop; condition prints asterisks equal to user number; increment i by 1
{
printf("*"); //Prints a single asterisk each loop until i is less than num
}
} while (num!=0); //Stop "do while" loop if user enters 0
return 0;
}
Normally I'd suggest that you ask your instructor instead of asking homework questions here, but as Daniel points out in the comments, the use of loops and extra variables just to avoid having if statements in the code is stupid, and I'm not sure telling you to get advice from an instructor who thought that was a good idea would be entirely responsible behavior on my part. So, having said that:
What you want to look for in cases like this is multiple chunks of similar code, or chunks of code that conceptually do a single thing. Then see if you can split those chunks out into a function.
In the first example, you display a prompt, read user input, and verify the input for both sectioncode and studentnumber. That process could be split into a separate function. (Everything from printf("Enter ...") through the end of the while loop.) Then in the main function, you just have something like
sectioncode = readval("Enter a Section Code: ", 1, 4);
studentnumber = readval("Enter the Student's ID: ", 1, 999999999);
For the second example, that input/validation code isn't duplicated, but it's still probably worth splitting out into a function, since it does a single well-defined thing, and spans enough lines that splitting it out into a function might help make the logic of the remaining code more clear. But it's less important in this case than in the first.
Also, an unrelated issue: At the end of one of the do-whiles, you have while (dummyvariable = 0);. Note the single equal sign. You're assigning 0 to dummyvariable, not comparing.
In this particular case, it works anyway, since the whole expression evaluates to 0 (i.e. false), just like (dummyvariable == 0) would have. But if that constant had been anything else, or if you hadn't just set dummyvariable to 1 prior to the end of loop, that'd be a bug.
I strongly recommend always putting the constant on the left hand side of expressions like that in order to catch bugs of that sort at compilation. while (dummyvariable = 0) silently does something unexpected; while (0 = dummyvariable) will give an error at compile-time, and you can fix it.

Printf between two different for loops not working

I am trying to debug using printf. I have inserted it between for loops, but I do not receive the output of it. I am certain that the algorithm is continuing after it and arriving to the end, it's like the reader is ignoring it.
Here's the code :
for (i = 0; i < lower; ++i) {
buf3[i] = (buf[i] + buf2[i] )/2;
printf("\n %d",buf3[i]);
}
printf("hiiiiiiiiiiiiiiiiiiii %d",i);
for (i; i < upper; ++i) {
if (upper == num2) {
buf3[i] += buf2[i]/2;
printf("\n %d",buf3[i]);
}
else {
buf3[i] += buf[i]/2;
printf("\n %d",buf3[i]);
}
}
printf("\n %d",upper);
The "hiiiii..." message is the one not being seen on the screen. (I tried replacing it by many other messages such as int or anything else, but in vain. I also tried to put another printf right above the first for loop, again it returned nothing).
Please note that upper and lower aren't huge numbers.
Thanks in advance.
The printf in question does not print a newline, so the output is buffered until a later printf prints a newline.
Add the newline, and you should see the output:
printf("hiiiiiiiiiiiiiiiiiiii %d\n",i);
Try adding \n after %d.
Also i is not initialised in the second loop

Array of a struct with strings

I have defined a structure
struct subject
{
char name[100];
int year;
};
and since I need n of these and I have to use malloc I did the following in my main function:
int n, i;
scanf("%d", &n);
struct subject *ptr = malloc(n*sizeof(struct subject));
Unfortunately when I try to input something with this code:
for(i = 0; i < n; i++)
{
gets((ptr + i)->name);
scanf("%d", (ptr + i)->year);
}
It crashes after I type the first name. The task requires the use of malloc.
Here's the whole code (unfortunately it's in my native language so it's a little bit different)
#include <stdio.h>
#include<stdlib.h>
#ifndef DEBUG
#define DEBUG(...)printf(_VA_ARGS_)
#endif
struct kolegij
{
char naziv[100];
int semestar;
};
int main(){
int brPredmeta, i;
scanf("%d", &brPredmeta);
struct kolegij *ptr = malloc(brPredmeta*sizeof(struct kolegij));
if(ptr == NULL)
{
printf("error\n");
return 0;
}
for(i = 0; i < brPredmeta; i++)
{
//gets(ptr->naziv);
gets((ptr + i)->naziv);
scanf("%d", &(ptr + i)->semestar);
getchar();
}
for(i = 0; i < brPredmeta; i++)
{
printf("%s\n", ptr[i].naziv);
printf("%d\n", ptr[i].semestar);
}
return 0;
}
With regards to the duplicate issue. I believe this shouldn't be a duplicate since it's related to structs and pointers. I had issues with scanfs before and I haven't considered this as a solution so I think it shouldn't be flagged as a duplicate.
I believe the statement scanf("%d", (ptr + i)->year); to be responsible for your unstable behaviour, as you're passing an int where scanf expects an int *. Perhaps you meant something like scanf("%d", &(ptr + i)->year) (note the additional ampersand), though this also has perils associated. Namely, you're discarding the return value, so you can never be sure that the value stored will be sane. If the user enters invalid input, it might be best to re-prompt or exit:
int n;
do {
puts("Enter year: ");
n = scanf("%d", &(ptr + i)->year));
scanf("%*[^\n]"); // more on this later
getchar();
} while (n == 0);
if (n < 0) exit(EXIT_FAILURE);
As part of handling input is expecting the enter keystroke, we need to clear that before the next gets call, and I use scanf("%*[^\n]") followed by getchar() for that.
On the topic of the code which you presented near the end, that does indeed look very different. I notice you fixed the scanf type error (the additional ampersand), and you have getchar() after the scanf("%d", ...) so you won't usually get the trailing newline hanging around. I see no reason to be suspect of that (code at the end of your post); if you're concerned about a reason for closure, it appears that you don't have a problem anymore (we can't reproduce your crash)... I mean, aside from using gets; that's a minor problem, I guess (gets is deprecated; use fgets instead, trim the trailing '\n' and if there is none use scanf("%*[^\n]") followed by getchar() again to effectively truncate the input).
Malloc returns a void pointer type. In order to use it as a pointer to your structure you have to cast it first. Try changing the line with malloc into:
int n, i;
scanf("%d", &n);
struct subject *ptr = (struct subject*) malloc(n*sizeof(struct subject));
You also have to change your input into:
for(i = 0; i < n; i++)
{
gets((ptr + i)->name);
scanf("%d", &((ptr + i)->year));
}

How exactly does space work with scanf?

I am a math student, and I'm learning the very basics in programming in C. I need a program to read an input consisting in an array, the components of which must have certain requisites; I would like the program to ask the user for the components of the array. The user should then have to enter such components separating them with spaces. The details aren't important to get the main question across; I'll choose a simpler example then the one I am dealing with: let's say I want an array with 6 components not to contain the number 4. So I tried:
#include <stdio.h>
int main(void) {
int a[6];
printf("enter components: ");
int i;
for (i = 0; i < 6; i++) {
scanf("%d", &a[i]);
if (a[i] == 4) printf(" \n\n4 is not allowed, try again\n\n");
}
for (i = 0; i < 6; i++) {
printf("%d ", a[i]);
}
}
If I compile this and run it, and for example enter:
1 2 3 4 5 6
I will get my error message, but only after having pressed enter, that is after having entered all six components (not straight after having pressed space for the fourth time). So here are my questions (I am looking for solutions which don't make use of strings or pointers, unless it is impossible to do without them):
Is there a way to get the program to read a component (and to act accordingly) straight after its subsequent space has been entered? I'm guessing there isn't because scanf only works after the user presses enter, and not space, correct?
If there isn't, is there a way to get the program to read the components all at once after having pressed enter at the end, but letting the user pick up from the last right component? For example, with the above input, I would like the program to display something like this:
4 is not allowed
1 2 3 _
so that the user can correct his/her input (possibly changing the first three digits as well).
Sorry if this question is too dumb! Thank you for your help!!
EDIT: Well, thanks for the great answers, you have all been very helpful! It's a pity I can't accept more than one.
In for loop, after each iteration, the counter add by one automatically. If you get an invalid input, you should prevent the counter increasing. To do this, just add i--; to your code when you give an invalid input.
#include <stdio.h>
int main(void) {
int a[6];
printf("enter components: ");
int i;
for (i = 0; i < 6; i++) {
scanf("%d", &a[i]);
if (a[i] == 4){
printf(" \n\n4 is not allowed, try again\n\n");
i--;
}
}
for (i = 0; i < 6; i++) {
printf("%d ", a[i]);
}
}
Please see the bellow code:
#include <stdio.h>
int main(void) {
int a[6];
int i;
bool hasError = false;
int errorIndex = 0;
do{
hasError = false;
printf("enter components: ");
for (i = 0; i < errorIndex; i++)
printf("%d ", a[i]);
for (i = errorIndex; i < 6; i++) {
scanf("%d", &a[i]);
if (a[i] == 4 && hasError == false){
printf(" \n\n4 is not allowed, try again\n\n");
hasError = true;
errorIndex = i;
}
}
}while(hasError == true);
for (i = 0; i < 6; i++) {
printf("%d ", a[i]);
}
}
This is related to your terminal being in "cooked" mode. Characters aren't even sent to the program until the user presses enter.
You could do something like this:
int i,a[6];
for (int i=0;i<6;i++) {
scan: scanf("%d",&a[i]);
}
for (int i=0;i<6;i++) if (a[i]==4) {
printf("4 is not allowed. re-enter the last %d numbers\n",6-i);
goto scan;
}
note that in most case, it's better to avoid using goto, but in this case I think that it's natural.
If you really want, you can print the first i numbers (before the goto), but it's complicated (and platform-depended) to let the user change those numbers.
Improving on Mir Milad Hosseiny answer (I wrongly identified it as being an out of control infinite loop... it's actually exactly the infinite loop I describe in my comment)...
I would write a small function that has either a "white list" (things you want) or a "black list" things you don't want, and check each value to either belong or not (depending on the approach) to the list. That way you can keep a separate place where your store the values that you are willing to accept or the values you are not, so your primary function doesn't get really messy with exceptions or inclusions in the "if"
so your code would be
if(isAllowed(a[i]){
myList[j] = a[i]; //j is your alternate counter
}

Resources