I am trying to create a record in which I can keep track of students' names and their scores. after running, I enter 1 student record and after entering 2 records, I am getting a segmentation fault error. I don't understand what is causing this error as I am a beginner to the C language. here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Students {
char name[100];
int roll;
float mark;
} Student;
int main() {
int N,i;
printf("How many students do you want to enter: ");
scanf("%i",&N);
Student *st = malloc(N*sizeof(Student));
for (i = 0; i < N; i++){
printf("Enter name: ");
scanf("%s",st[i].name);
st[i].roll = i;
printf("Enter score for %s", st[i].name);
scanf("%f",&st[i].mark);
printf("%i. %s ",i,st[i].name);
printf("%s: %f ",i,&st[i].mark);
printf("\n");
}
return 0;
}
While you have an explanation for where you segfault originated, if you are going to learn C, do not learn bad habits early. There are a number of issues you should address before you consider your code reliable, and a number of others you can address to make your code work like it appears you intended.
First and foremost, validate ALL user input!. If you fail to validate all user input you have no idea if your code is processing garbage or straying off into undefined behavior from the first input you take. A user can input anything -- or a cat can walk on the keyboard, etc... It is your responsibility to verify you have the input you are expecting.
For example, from your very first input, you should, at minimum check the return of scanf to insure you have the number of successful conversions you expect. It is simple to do, e.g.
if (scanf ("%d", &n) != 1) { /* validate number */
fprintf (stderr, "error: invalid input (number).\n");
return 1;
}
When taking string input you need to limit the number of characters you accept to the size of the storage available. You can accomplish this with scanf using the width modifier. Since you have 100 characters available in name, you can store 99 chars plus the nul-terminating byte. Additionally, since names can contain whitespace, you can use a character class to read all characters up to the '\n' character. (here you are better served using line-oriented input functions such as fgets). e.g.
if (scanf (" %99[^\n]%*c", st[i].name) != 1) {
fprintf (stderr, "error: invalid input (name).\n");
return 1;
}
You also need to understand you are leaving the '\n' in the input buffer (stdin) and you must account for that if your next input is character input of scanf will happily take the '\n' as your input. All line oriented input functions (fgets, getline) read and include the '\n', scanf does not. You can however use the assignment suppression operator '*' to read-and-discard the specified input (%*c to read/discard the next character (the '\n')).
In any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. e.g.
free (st); /* free allocated memory */
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an uninitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind is the normal choice, but there are similar programs for each OS.
Last, while not an error, the standard coding style for C avoids caMelCase variables in favor of all lower-case. See e.g. NASA - C Style Guide, 1994
Putting all of that together, and tweaking the output and formatting to what it seems you intended (I could be totally wrong), you could rewrite you code something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct students {
char name[100];
int roll;
float mark;
} student;
int main ()
{
int n = 0, i = 0, maxc = 0; /* initialize variables */
student *st = NULL;
printf ("How many students do you want to enter: ");
if (scanf ("%d", &n) != 1) { /* validate number */
fprintf (stderr, "error: invalid input (number).\n");
return 1;
}
/* allocate & validate */
if ((st = malloc (n * sizeof (student))) == NULL) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
for (i = 0; i < n; i++) { /* take input & validate */
printf ("enter name: "); /* limit & accept full name */
if (scanf (" %99[^\n]%*c", st[i].name) != 1) {
fprintf (stderr, "error: invalid input (name).\n");
return 1;
}
st[i].roll = i;
printf ("enter score for %s: ", st[i].name);
if (scanf ("%f", &st[i].mark) != 1) {
fprintf (stderr, "error: invalid input (mark).\n");
return 1;
}
}
for (i = 0; i < n; i++) { /* compute max length for name */
int len = (int)strlen (st[i].name);
if (len > maxc)
maxc = len;
}
printf ("\nroll %-*s mark\n\n", maxc, "name");
for (i = 0; i < n; i++)
printf (" %3d %-*s %.2f\n",
st[i].roll, maxc, st[i].name, st[i].mark);
free (st); /* free allocated memory */
return 0;
}
Example Use/Output
$ ./bin/structloop
How many students do you want to enter: 4
enter name: John J. Franklin
enter score for John J. Franklin: 83.1
enter name: Betty C. Smith
enter score for Betty C. Smith: 91.2
enter name: Jennifer L. Burgen-Kwiatkowski
enter score for Jennifer L. Burgen-Kwiatkowski: 88.7
enter name: Alfred R. Murrow
enter score for Alfred R. Murrow: 73.5
roll name mark
0 John J. Franklin 83.10
1 Betty C. Smith 91.20
2 Jennifer L. Burgen-Kwiatkowski 88.70
3 Alfred R. Murrow 73.50
Look over all the answers and comments and let me know if you have any questions.
Using a debugger and backtracking at the core dump, you get that it crashes here;
printf("%s: %f ",i,&st[i].mark);
The line should probably look something like this instead;
printf("%d: %f ",i,st[i].mark);
Related
I'm new to C and programming in general. It's a guessing game where the user has a certain amount of guesses to guess whatever the hidden message is.
Unfortunately, even if I enter the correct word, it does not register it as being correct.
#include <stdio.h>
#include <stdlib.h>
Lost(){
printf("You lost");
}
Won(){
printf("You won");
}
int main(void)
{
char HiddenMessage[7] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while(Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0){
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries){
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0){
Won();
}
}
return 0;
}
How do I structure this to ensure that if a Guess occurs correctly within the number of tries it runs Won();
You should have received a warning about:
char HiddenMessage[7] = "Cameron";
if your compiler was doing its job properly :-)
Seven characters is not enough to store Cameron and the end-of-string marker. Try it again with:
char HiddenMessage[] = "Cameron";
This will make sure enough characters are set aside for the full string.
If you're interested, this is covered in C11 6.7.9 Initialization /14 (emphasis added):
An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.
So what your code will give you is the sequence of characters { 'C', 'a', 'm', 'e', 'r', 'o', 'n' } but with no \0 at the end to make it a C string. That means strcmp cannot be guaranteed to give you the results you want (it will possibly run off the end of the array during the comparison).
And, as an aside, scanf with the unbounded %s format specifier is considered rather dangerous in non-trivial code, since there's no way to protect against buffer overflow.
If you're looking for a fairly bullet-proof user input function that can detect and mitigate this, have a look at this earlier answer of mine.
It allows prompting, prevents buffer overflow, properly handles line-based user input (no half lines delivered to user) and so on.
It's what I use for simple console applications (where no better user input method is available).
There are, essentially, four errors in your code:
As paxdiablo has stated, your HiddenMessage array is not large enough to hold the text you have given plus the nul terminator.
You need to define (or at least declare) your Lost and Won functions before you use them.
The definitions of these two functions are incorrect, as you have not specified a return type: void is suitable for functions that don't return anything.
You must include stdio.h to get the definitions of printf and scanf
Here's a fixed (and working) version of your code with the corrections made:
#include <stdlib.h>
#include <stdio.h> // MUST include this for "printf" and "scanf" functions
// You can keep these DEFINITIONS after "main" if you like, but then you'll need to
// have "pre-declarations" of them (the two commented lines immediately below).
// void Lost();
// void Won();
void Lost() {
printf("You lost");
}
void Won() {
printf("You won");
}
int main()
{
char HiddenMessage[8] = "Cameron"; // Added enough space for the nul terminator
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0) {
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries) {
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0) {
Won();
}
}
return 0;
}
Feel free to ask for further clarification and/or explanation.
Another significant problem is your failure to validate any of the input. Any time (means ANY time) you take input, you must validate the input succeeds before relying on the data. Failure to validate invites undefined behavior.
Validations are simple, if you read input with a function check the return to validate you got all the input you were expecting. For example:
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
(note: the use of the field-width modifier to limit the number of characters that can be read. Failing to control the number of characters than can be input will read to attempt to write beyond the bounds of your array if the user enters 50 (or more) characters.)
Your logic is a bit awkward. All you need to do is loop continually until the user either wins the game or exhausts the number of tries available. Rearranging your logic slightly, you could do something like:
(Edited to empty-stdin)
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
Putting it altogether in an example, you could do:
#include <stdio.h>
#include <string.h>
#define MAXTRIES 5
#define MAXGUESS 50
int main (void) {
char hiddenmsg[] = "Cameron",
guess[MAXGUESS] = "";
int tries = 0;
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
}
Example Use/Output
Typical try and fail game:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Alligator
try no. 2 - Guess the seceret word: Campers
try no. 3 - Guess the seceret word: Scam
try no. 4 - Guess the seceret word: Horses
try no. 5 - Guess the seceret word: Bananna
you have exhausted all guesses - you lost :(
A lucky win:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Lightning
try no. 2 - Guess the seceret word: Thunder
try no. 3 - Guess the seceret word: Geroge
try no. 4 - Guess the seceret word: Cameron
congratulations - you won.
(Testcase After Edit w/Example from Comment)
$./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCameron
try no. 2 - Guess the seceret word: foo
try no. 3 - Guess the seceret word: Cameron
congratulations - you won.
Look things over and let me know if you have any further questions.
As strcmp compares the difference between each character till the end of string that is \0 So your buffer needs to be large enough to hold your string i.e. char HiddenMessage[8] = "Cameron";
PS you can avoid a lot of lengthy code
#include <stdlib.h>
int main()
{
char HiddenMessage[8] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries)
{
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (strcmp(HiddenMessage, Guess) == 0)
{
Won();
return 0;
}
}
Lost();
return 0;
}
void Lost()
{
printf("You lost");
}
void Won()
{
printf("You won");
}
There are multiple problems with your code. The size of HiddenMessage reported by #paxdiablo is just one of them.
You should use fgets instead of scanf because scanf won't consume the newline. You will be stuck on the second iteration.
You increment Tries and test it against MaxTries before testing if the guess was correct. As a consequence the program will tell that the user lost before testing the validity of the last guess.
Once the user guessed the word and won, you must break from the while loop and terminate the program. With your code, after the program reported that the user won, it will ask for another guess if it wasn't the last guess.
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.
I have created a code reverse a string but for some reason it is not working. But I think my logic is right. Then why is it not working??
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
printf("\nThe Reverse is: ");
while (i<=0) {
printf("%s",words[i]);
i--;
}
return 0;
}
While you already have an answer, there are a few additional points you need to consider before you have a solution that doesn't have the potential to invoke Undefined behavior.
First, always, always validate all user input. For all you know a cat could have gone to sleep on the 'L' key (with millions being entered), or a more likely case, the user just decides to type a 100-char sentence (or more) which leaves 'words' as an array of chars that is NOT nul-terminated and thus not a valid string in C. Your loop to get the length now invokes Undefined Behavior by reading beyond the end of words off into the stack until the first random '0' is encounter or a SegFault occurs.
To prevent this behavior (you should really just use fgets) but with scanf you can provide a field-width modifier to prevent reading more than length - 1 chars. This insures space for the nul-terminating character.
Further, the "%s" conversion-specifier stops conversion on the first whitespace character encountered -- making your "Enter a .../sentence" an impossibility because scanf ("%s", words) will stop reading after the first word (at the first whitespace.
To correct this problem (you should really just use fgets) or with scanf you can use a character class (stuff between [...]) as the conversion specifier that will read until a '\n' is encountered., e.g. scanf ("%[^\n]", words). However, recall, that is still not good enough because more than 99-chars can be entered leaving the string un-terminated at 100 and invoking Undefined Behavior at character 101 (off the end of the array).
To prevent this problem (ditto on fgets), or include the field-width modifier, e.g. scanf ("%99[^\n]", words). Now no more than 99-chars will be read regardless of the cat sleeping on the 'L' key.
Putting that altogether, you could do something like:
#include <stdio.h>
#define MAXC 100 /* if you need a constant, define one */
int main(void) {
char words[MAXC] = "";
int i = 0, rtn = 0; /* rtn - capture the return of scanf */
printf ("Enter a word/sentence : ");
if ((rtn = scanf ("%99[^\n]", words)) != 1) { /* validate ! */
if (rtn == EOF) /* user cancel? [ctrl+d] or [ctrl+z]? */
fprintf (stderr, "user input canceled.\n");
else /* did an input failure occur ? */
fprintf (stderr, "error: invalid input - input failure.\n");
return 1; /* either way, bail */
}
for (; words[i]; i++) {} /* get the length */
printf ("Reversed word/sentence: ");
while (i--)
putchar (words[i]); /* no need for printf to output 1-char */
putchar ('\n');
return 0;
}
Example Use/Output
$ ./bin/strrevloops
Enter a word/sentence : My dog has fleas.
Reversed word/sentence: .saelf sah god yM
Look things over and let me know if you have any further questions.
There are few mistakes in your program.
After you have reached the end of the string.You should do i-- as your array index of i will be pointing to '\0'.
Your while loop checks for <= but it should be >=.
Use %c for printing chararcters. %s is used to print strings and not char.
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
i--;
printf("\nThe Reverse is: ");
while (i>=0) {
printf("%c",words[i]);
i--;
}
return 0;
}
This question already has answers here:
scanf() leaves the newline character in the buffer
(7 answers)
Closed 6 years ago.
I have written a small code to take multiple strings as input from user. Prior to that, I'm asking the user to input the number of strings to take as input. While taking strings as input, a new line character automatically occupies the the place of first string. I don't know what is happening :( Here is my code:
#include<stdio.h>
#include<string.h>
void main()
{
char *arr[20],str[40];
int n;
printf("enter the number of strings\n");
scanf("%d",&n);
int i;
printf("Enter the strings\n");[It isn't taking 3 strings as input,as a newline character is already occupying first place][1]
for(i=0;i<n;i++)
{
gets(str);
arr[i]=(char *)malloc(sizeof str);
strcpy(arr[i],str);
}
printf("The Strings are:\n");
for(i=0;i<n;i++)
{
printf(arr[i]);
printf("\n");
}
}
Following on from the comments, there are a number of issues you have in your code. First gets, don't use it, it is a 'hanging' offense -- enough said.
Next, validate all user input. For all you know, a cat could be stepping on the keyboard. Make sure you test what you receive as input, and that it matches what you expect.
Mixing scanf with line-oriented input (e.g. fgets or getline can cause problems for new users. Why? The scanf family of functions do not remove the '\n' and instead leaves it in the input buffer (e.g. stdin). When you then attempt to read with fgets the first character it sees in stdin is what? A '\n', which it reads and considers a whole line. If you are going to use scanf to read the number of strings, it is up to you to remove the '\n' that scanf left in stdin.
It's not hard, you can actually just use the assignment suppression operator provided by scanf in your format string. E.g.:
scanf ("%d%*c", &n);
The * is the assignment suppression operator, which when used with %*c simply tells scanf to read and discard the next character without adding to the match count (e.g. what scanf returns).
You will want to use a while loop instead of a for loop, (or use an independent index) when filling your array. Why? What if the users cancels input with a ctrl + d (or ctrl + z on windoze). If you are iterating up to n regardless of what the user does, you can easily attempt to index an array element that you haven't allocated, or allocate for a string that was not entered.
Putting it altogether, you can do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXA = 20, MAXS = 40 }; /* max ptrs and str */
int main (void) {
char *arr[MAXA] = {NULL}, str[MAXS] = "";
int i = 0, n = 0, ndx = 0; /* initialize all variables */
printf ("enter the number of strings: ");
/* use assignment suppression %*c to discard the \n */
if (scanf ("%d%*c", &n) != 1) { /* always validate input */
fprintf (stderr, "error: invalid integer input.\n");
return 1;
}
if (n > MAXA) { /* validate the value of n */
fprintf (stderr, "warning: n > %d, using %d as limit.\n",
MAXA, MAXA);
n = MAXA;
}
printf("Enter the strings\n");
while (ndx < n && fgets (str, sizeof str, stdin)) { /* validate input */
size_t len = strlen (str); /* get the length */
if (str[len - 1] == '\n') /* check for '\n' */
str[--len] = 0; /* overwrite with nul-terminator */
if (!(arr[ndx] = malloc (len + 1))) { /* validate allocation */
fprintf (stderr, "error: virtual memory exhausted.\n");
break;
}
strcpy (arr[ndx], str); /* copy to array */
ndx++; /* increment index */
}
printf("\nThe Strings are:\n");
for (i = 0; i < ndx; i++) { /* you have ndx strings not n */
printf (" arr[%2d] : %s\n", i, arr[i]);
free (arr[i]); /* free memory when no longer needed */
}
return 0;
}
Example Use/Output
$ ./bin/nogets
enter the number of strings: 3
Enter the strings
My dog
has a lot
of fleas.
The Strings are:
arr[ 0] : My dog
arr[ 1] : has a lot
arr[ 2] : of fleas.
Now try the same thing and press ctrl + d instead of entering "of fleas." (you are covered).
Lastly, in any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. Get in the habit of tracking the memory you allocate, and free it rather than relying on it being done on exit. That will serve you well as your programs grow more complex.
Look over the code, and make sure you understand what is going on. Let me know if you have any questions.
When you read the number of digits using scanf, it only processed the digit typed in, not the enter you pressed after entering the digit.
Might be worth while to flush the rest of the for input such as
How to clear input buffer in C?
The gets() function shall read bytes from the standard input stream, stdin, into the array pointed to by s, until a <newline> is read or an end-of-file condition is encountered.
So whenever you enter after
enter the number of strings
5
So if you press enter then gets is getting called it's taking new line as a first input.
And if you didn't input enter and press like 5abc then enter then it's going to consider abc as a first string.
You need to clear buffer before gets.
Your correct program is
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void main()
{
char *arr[20],str[40];
int n;
int ch;
printf("enter the number of strings\n");
scanf("%d",&n);
int i;
printf("Enter the strings\n");
//flush the input stream
while ((ch = getchar()) != '\n' && ch != EOF);
for(i=0;i<n;i++)
{
gets(str);
arr[i]=(char *)malloc(sizeof str);
strcpy(arr[i],str);
}
printf("The Strings are:\n");
for(i=0;i<n;i++)
{
printf("%s",arr[i]);
printf("\n");
}
}
What i am trying to do ?
I am trying to allocate memory to a char type two dimensional array which is declared as a member of a structure.
What is the problem?
I am not sure how to do the same.
My code:
# include <stdio.h>
# include <stdlib.h>
# include <conio.h>
typedef struct information {
int id ;
char **name ;
} info ;
void main () {
info s1 ;
int i , size ;
printf ("\n Enter max size of students name : ") ;
scanf ("%d",&size) ;
s1.name = ( char** ) malloc ( size * sizeof ( char* ) ) ;
for ( i = 0 ; i < size ; i++ )
s1.name [ i ] = ( char** ) malloc ( size * sizeof ( char* ) ) ;
printf ("\n Enter Id : ") ;
scanf ("%d",&s1.id) ;
printf ("\n Enter Name : ") ;
scanf ("%s",s1.name) ;
getche () ;
}
Is this right? Am I allocating it correctly?
I also want to know why we don't use & when getting input in the form of a string. For example, in my code at the third from bottom line, why can't I write this: scanf ("%s",&s1.name); ?
As explained in the comment, you will allocate/free exactly the same as you would if the pointer were not a member to a struct. While it appears you do not need a char **name (pointer-to-pointer-to-char) unless one id is associated with many names. Simply declare a pointer to name (e.g. char *name;) would be sufficient to provide a one-to-one relationship with id.
Putting those pieces together, and adjusting the format-specifiers for scanf to read/discard the trailing newlines after the read of size, you could do something like:
# include <stdio.h>
# include <stdlib.h>
// # include <conio.h>
typedef struct information {
int id ;
char *name ;
} info ;
int main (void) {
info s1;
int size;
printf ("\n Enter max size of students name : ");
scanf ("%d",&size);
s1.name = malloc (size * sizeof *s1.name);
printf ("\n Enter Id : ");
scanf ("%d%*c", &s1.id);
printf ("\n Enter Name : ");
scanf ("%[^\n]%*c", s1.name);
printf ("\n You entered, id: %d, name: %s\n\n", s1.id, s1.name);
free (s1.name);
// getche () ;
return 0;
}
Example Use/Output
$ ./bin/struct_stdnm
Enter max size of students name : 29
Enter Id : 231
Enter Name : Alfred Q. Myres
You entered, id: 231, name: Alfred Q. Myres
Let me know if you have further questions or further needs.
Note: if you need an array of id and names, then you need an array of struct information, not a double-pointer to name -- if I interpret what makes sense for what you are doing correctly.
Protect Against Input Beyond Allocation
As noted in the comments below, scanf is not optimal for reading s1.name because it does not allow a maximum field width by variable. To properly protect against against writing beyond the end of your allocation, you should use fgets and limit your read to size:
printf ("\n Enter Name : ");
fgets (s1.name, size, stdin);
Example
$ ./bin/struct_stdnm
Enter max size of students name : 5
Enter Id : 123
Enter Name : Johnny Jones
You entered, id: 123, name: John
Validation Required on All Allocation & Input
Regardless of whether you stick with scanf for input or use fgets, you must validate all allocations and input to insure you actually allocate the memory you intend to use, and you actually read what you think you have read. Think of this is just another layer of completeness for your code. While learning it may be convenient to use examples without complete validation, you should never consider your code complete until you provide sufficient checks for all allocations and input. Here is an update showing minimal validation checks for your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// # include <conio.h>
typedef struct information {
int id ;
char *name ;
} info ;
int main (void) {
info s1;
int size;
size_t len;
printf ("\n Enter max size of students name : ");
if (scanf ("%d",&size) != 1) { /* validate converison */
fprintf (stderr, "error: conversion of size failed.\n");
return 1;
}
if (size < 1) { /* validate size */
fprintf (stderr, "error: invalid size entered.\n");
return 1;
}
/* always validate allocations */
if (!(s1.name = malloc (size * sizeof *s1.name))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
printf ("\n Enter Id : ");
if (scanf ("%d%*c", &s1.id) != 1) { /* validate converison */
fprintf (stderr, "error: conversion of size failed.\n");
return 1;
}
printf ("\n Enter Name : ");
if (fgets (s1.name, size, stdin)) {
len = strlen (s1.name);
/* validate all characters fit within 'size' allocation */
if (len + 1 == (size_t)size && (s1.name)[len-1] != '\n')
printf (" warning: name exceeded allocation.\n");
/* remove trailing newline from name */
if (len && (s1.name)[len-1] == '\n')
(s1.name)[--len] = 0;
}
else {
fprintf (stderr, "error: name entry failed.\n");
return 1;
}
printf ("\n You entered, id: %d, name: %s\n\n", s1.id, s1.name);
free (s1.name);
// getche () ;
return 0;
}
This also flags any time you have not allocated enough space to hold the entire name entered:
Short Allocation Example
$ ./bin/struct_stdnm
Enter max size of students name : 5
Enter Id : 123
Enter Name : Johnny Jones
warning: name exceeded allocation.
You entered, id: 123, name: John
Let me know if you have further questions.