Structures in C - not printing the whole string - c

Working on some self study in understanding structures in C.
I've made a small program that gets info from the user and then prints it back via functions.
I used two different methods of handing off the data using a couple examples in the C Primer Plus book.
What happens is that I can input the data but when it prints it back out the numeric data is ok but only the first character in each string is printed with garbage after it.
My code is below for review. I can't figure out what the issue is.
Any help would be great. Thanks!
#include <stdio.h>
#include <stdlib.h>
struct stats {
char name;
char city;
int wins;
int losses;
int draws;
};
void screen_print(char name,char city,int wins,int losses,int draws);
void team_input (struct stats * ptr);
int main()
{
struct stats team;
team_input(&team);
screen_print(team.name,team.city,team.wins,team.losses,team.draws);
return 0;
}
void screen_print(char name,char city,int wins,int losses,int draws)
{
// system("cls");
printf("==================================================\n");
printf("Name:\t\t\t%s\n",&name);
printf("City:\t\t\t%s\n",&city);
printf("Number of Wins:\t\t%d\n",wins);
printf("Number of Losses:\t%d\n",losses);
printf("Number of Draws:\t%d\n",draws);
printf("==================================================");
}
void team_input (struct stats * ptr)
{
system("cls");
printf("Enter Team name: ");
scanf("%s",&(ptr->name));
printf("\nEnter City:");
scanf("%s",&(ptr->city));
printf("\nEnter Wins:");
scanf("%d",&(ptr->wins));
printf("\nEnter Losses:");
scanf("%d",&(ptr->losses));
printf("\nEnter Draws:");
scanf("%d",&(ptr->draws));
}

name and city are single characters only: they are not strings.
scanf("%s",&(ptr->name)); is invalid and will be overwriting memory as an attempt is being made to read a string into a single char.
printf("%s", &name); expects name to be a null terminated string, so it will print the name char then random characters until a null is found somewhere in memory.
Change to:
struct stats {
char name[20]; /* Or greater than 20 if required */
char city[20];
int wins;
int losses;
int draws;
};
or dynamically allocate memory before populating if maximum possible length of name and city are unknown beforehand.
Change the printf() statements to:
printf("Name:\t\t\t%s\n", name);
printf("City:\t\t\t%s\n", city);
and scanf() statements to:
scanf("%s",ptr->name);
scanf("%s",ptr->city);
and screen_print() signature to:
void screen_print(char* name,char* city,int wins,int losses,int draws)

You're using single chars instead of arrays of chars. Both when inputting and outputting, the system is getting the memory address of a char, thinking that it's a string, and writing to/reading from it without any bounds check. So the adjacent memory is accessed as well, even though you didn't intend to.
You're lucky that your numeric data is input after the textual ones, or else even those would be wrong. Since a struct will usually occupy contiguous positions in memory, it's likely that they are being overwritten when you input your strings. That "garbage" you're seeing is in fact the data in the rest of your struct, plus anything that's "close" to it in memory, until an empty value is found (\0, interpreted as the null char).

In your struct, only one char is allocated for name and city. To hold a string, you need to specify the length at declaration.
struct stat {
char city[20];
char name[20];
...
}
String in C is quite tricky. It is an array of characters using an invisible '\0' for the ending. A string "hello" is actually 'h', 'e', 'l', 'l', 'o', '\0' in the memory.

Related

Using getchar() to store address of string literal in char*pointer

The goal is storing the adress of a string litteral in a char*, which is a member of struct
id. I thought about using an array.
The problem with array is that, if I set the maximum number of character to 7, the user
might enter less than 7, so it will be a waste of memory.
The advantage using getchar() is that I can set max of char to 7, but if user enter less, that's ok too.
typedef struct id
{
int age;
char* name;
}id;
id Mary;
char L;
int c =0;
printf("Enter your age: ");
scanf("%d",&Mary.age);
printf(" Enter your name: );
if( (L=getchar() != '\n' )
{
// stroring string litteral in char*
}
printf("%s", Mary.name);
This is a common problem: "How do I read an input string of unknown length?" Daniel Kleinstein has mentioned several general solutions in his answer. I'll give a more implementation-based answer here
Firstly, your program does not try to store a string literal, but a string read from an input stream (e.g. stdin).
Secondly, it is not possible to store a string "in a char*". The string is stored in memory pointed to by a char*. This memory needs to be allocated first.
The following code comes closest to what you want to do. It reads one character at a time and increases the size of the memory copied to by 1 byte every time.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int age;
char *name;
} Id;
int main(void)
{
Id mary;
printf(" Enter your name: ");
size_t nameSize = 0U;
mary.name = NULL;
while (true)
{
mary.name = (char*) realloc(mary.name, ++nameSize); // cast is optional
if (mary.name == NULL)
{
printf("Memory allocation error\n");
exit(EXIT_FAILURE);
}
int ch = getchar(); // Note the `int` type, necessary to detect EOF
if (ch == '\n' || ch == EOF)
{
mary.name[nameSize - 1] = '\0';
break;
}
mary.name[nameSize - 1] = (char) ch;
}
printf("%s\n", mary.name);
free(mary.name);
}
This does not waste a single byte of memory, however, the frequent memory reallocations will make this code slow. A good compromise is to read one fixed length string at a time, instead of one character at a time.
To do this in practice: create a buffer on the stack of fixed length (e.g. 64 characters), read into that buffer using fgets, and copy that contents to mary.name. If the string didn't fit the buffer, repeatedly call fgets again, realloc mary.name and append the contents of the buffer to mary.name until you find a newline character.
Another, simpler solution is to set a maximum length for the string, allocate memory for that length, read a string of maximally that length, and finally reallocate the memory to the (possibly smaller) actual size of the string.
There isn't a magic bullet solution for this sort of problem.
Your options are:
Use an array with a maximum length - but as you mentioned this can be wasteful if the user inputs a shorter length. Nevertheless this is usually the solution you'll find in real code - in practice, if memory isn't a huge concern, this is faster and simpler than trying to deal with other dynamic solutions that involve memory allocations.
Ask the user for the length of their name before they input it - then you can dynamically allocate an appropriately sized buffer using either char* name = malloc(input_length);, or char name[input_length]; in C99+. You could also do something like a flexible array member:
struct name {
size_t length;
char buffer[];
};
struct name* username = malloc(sizeof(*username) + username_length);
If you don't want to ask the user for the length of the username, you can do a chain of realloc calls after each new getchar, which will resize a dynamically allocated array - but this a terrible idea which you shouldn't even consider unless you're stressed over every byte of memory consumed in your program.

simple c scanf is not reading and parsing my input

I have created Date struct to store date format as date, month, year which is inputed by the user as a single line string seperated by space.
I want to split the input and store it accordingly. I have used simple C sscanf() to split and store my input in struct Date. But when I try to print my data it's showing some garbage value, I don't know what is missing.
Help me to clear this issue.
My code:
Input: 24 Jan 1980
output: 24 ? -978635323
here is my C code.
struct Date
{
int date;
char* month;
int year;
};
int main()
{
struct Date date[1];
char* string;
scanf("%s",string);
sscanf(string,"%d %s %d",&date[0].date,date[0].month,&date[0].year);
printf("%d %s %d",date[0].date,date[0].month,date[0].year);
return 0;
}
Thanks in advance.
it works for me. You can check it.
struct Date
{
int dat;
char month[20];
int year;
};
int main()
{
struct Date date[1];
char str[20];
scanf("%[^\n]%*c", str);
sscanf(str,"%d %s %d",&date[0].dat,&date[0].month,&date[0].year);
printf("%d %s %d",date[0].dat,date[0].month,date[0].year);
return 0;
}
EXPLANATION
In your code, you have declared two char * pointers char *month and char *string, and trying to store string values in them. You have to understand that these two are mere pointers. All they can store are memory addresses to char arrays. It is true that an array variable is interchangeable with a pointer in C. But an array declaration also involves a size attribute.
When you declare a character array as:
char str[10];
It actually reserves a block of size 10 in memory, implicitly creates char *str, and stores the base address of the newly created block in str. But when you just declare char *str, all it does is create a pointer. This doesn't have any associated memory block for the array.
That's why you have to declare a character array of some size before storing strings in it with scanf(). Or you have to dynamically allocate memory with calloc or malloc after declaring the char * pointer. Only then can you store a string input in it.
I have modified your code to work properly. It produces the desired output now. Hope this helps.
WORKING CODE
#include <stdio.h>
#include <stdlib.h>
struct Date
{
int date;
char month[10];
int year;
};
int main()
{
struct Date date[1];
char string[20];
scanf("%[^\n]s",string);
sscanf(string,"%d %s %d",&date[0].date,date[0].month,&date[0].year);
printf("%d %s %d",date[0].date,date[0].month,date[0].year);
return 0;
}
EDIT
They themselves are integers actually (as are all pointers).
As mentioned by anastaciu, pointers are not equivalent to integers per se. You can follow the links given in his comments for more knowledge. I had mentioned that statement to assert the difference between pointers and array declarations.
char* string is an uninitialized pointer, it cannot hold a string, you need to allocate memory, initialize it as an array of chars or point it to a variable with memory already allocated or to an existing array of chars.
Your structure member month suffers from the same problem.
A -Wall flag in your compiler should give you a warning:
'string is used uninitialized in this function [-Wuninitialized]'
Another problem is that %s only gets a string, when it finds a blank space, it stops reading, so it can only read 24 in your case.
Though "%[^\n]s" can work, a better solution would be to use fgets, it's a safer function since you can limit the size of the input to the size of receiving container, unlike scanf.
#include <stdio.h>
#define SIZE 100
struct Date
{
int date;
char month[SIZE];
int year;
};
int main()
{
struct Date date[1];
char string[SIZE];
fgets(string, sizeof(string), stdin);
sscanf(string,"%d %s %d",&date[0].date, date[0].month, &date[0].year);
}

Asking for Name and Displaying input

I relatively new to the C language and StackOverflow. I'm trying to write a simple C code that will prompt user for their name and then display it.
#include <stdio.h>
int main (void)
{
char name;
printf("Let's do this, please enter your name:");
scanf("%s", &name);
printf("Your name is %s", name);
return 0;
}
The code complies but after inputting the name, it displays Segmentation fault (core dumped).
Thank you for any help.
A very small mistake #Jackie_Legs. In the declaration of the variable name, You have declared it as a char. so it holds just one character.
The solution: choose an arbitrary size for your name, say 10 or 15 characters. and declare it as an array of the size.
char name[15];
No changes in any other part of the program. Also, you should omit the & symbol in the scanf for strings.
So just one change and your code should work.
Here is the updated code which would work:
#include <stdio.h>
int main (void)
{
char name[15];
printf("Let's do this, please enter your name:");
scanf("%s", name);
printf("Your name is %s\n", name);
return 0;
}
It is because your name variable can only store single character and you are trying to store more than one character which will lead to unpredictable behaviour of program (e.g. segmentation fault).
If you know max length of name, declare variable name as array of character such as
char name[20];
Here you can store name with max length of 19 character. You can decide length of array as per your requirement.
You need to declare a string as because a single letter is stored in char and multiple characters along with a null character at the end forms an String which can be anything like names, etc.
In your code you have taken char which stores only a single character but in order to store your name (which is a string) you will have to take char array along with the size of the array.
Replace char with char[size] where size is the size for the string that you need.
Here are the changes I made to your code:
#include <stdio.h>
int main (void)
{
char name[30];
printf("Let's do this, please enter your name:");
scanf("%s", name);
printf("Your name is %s", name);
return 0;
}
name is declared as char, which means it can contain only one character. It is not sufficient to contain a string of characters (which a name usually consists of). You need to declare a char array (an array of characters).
The size of the array should be at least 1 more than the largest name you want to read in.
The extra byte is to contain the null terminating character '\0'.
char name[SizeOfLargestName + 1];
And when you use scanf, you need not use & because now name points to the first byte of the array.
scanf("%s", name);
It is throwing error because of char name; char data type in C contain 1 character only in a variable. But your input is a string.
You need to change the declaration of the variable from char to char array.
char name[50];
printf("Let's do this, please enter your name:");
scanf("%s", name);
printf("Your name is %s", name);

Argument of type "char" is incompatible with parameter of type char*

I keep receiving this error and unsure how to fix it. I'm new to C programming and tried searching through the book/internet and couldn't find much help. I'm trying to create a program that will print a grade report using a loop and a sctructure
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Grades
{
char Name[20];
char Hrs;
int ID;
char ClassName[20];
char Grade;
char ClassID[6];
};
int main ()
{
struct Grades Transcript[6];
int classCnt = 0;
int vHrs=0;
char vGrade[2];
char vName[20], vCID[6], vClassName[20];
printf("Enter Students Name: ");
fgets(vName, 20, stdin);
do
{ printf("Enter Class ID; ");
fgets(vCID, 6, stdin);
strcpy_s(Transcript[classCnt].ClassID, vCID);
printf("Enter Class Name: ");
fgets(vClassName, 20, stdin);
strcpy_s(Transcript[classCnt].ClassName, vClassName);
printf("Enter Class Hours: ");
scanf("%d", &vHrs);
strcpy(Transcript[classCnt].Hrs, vHrs); //Problem occurs here
printf("Enter Class Grade: ");
scanf("%c", vGrade);
strcpy(Transcript[classCnt].Grade, vGrade); //Problem occurs here
classCnt++;
}while(classCnt<=6);
}
You actually have a number of problems here:
First, strcpy() is used to copy a string, if you have a character and you want it to assign it, you can simply assign it with the = operator. The strcpy() function is used when you have a character array you want to assign.
So your first problem
strcpy(Transcript[classCnt].Hrs, vHrs);
Hrs from your struct is just a char type, and vHrs is an int type. You can simply assign it like:
Transcript[classCnt].Hrs = vHrs;
However, an int can hold a lot more data than a char can, this is going to give you a warning about overflow and you should listen to it (depending on the implementation char holds -128 to 127, where as int holds −2,147,483,648 to 2,147,483,647). Decide what data type you really wanted here and either make Hrs an int or vHrs a char then just do the assignment.
Second problem:
scanf("%c", vGrade);
vGrade as a character array (it is made up of more than one character) that means you should assign it with the string format operator "%s", but when you do a string you should make the array long enough for the number of characters you want + 1 (for the NULL terminator).
Third problem:
strcpy(Transcript[classCnt].Grade, vGrade);
Grade is a char whereas vGrade is an array. Again, you have to make a decision of type, if you wanted a "string" of characters then you need to make them both arrays, if you wanted just a single character then change the type of vGrade and do a simple assignment with the = operator.
The thing you're running into here is a data typing error.
The structure member .Hrs is of type "char". The argument to strcpy is a "char *". Think of it like this: A char is a single 8 byte number representing a value in ASCII. A "char *" is a pointer to an array of characters (or a string in C language).
The Above is traditionally true,but , today it might be a unicode or multi-byte character string, as well. In either case, what you need is the assignment operator (=), not a strcpy.
So you're telling strcpy to look at a single value (char), not a string of values (char *) which is it expecting as it's argument. In this case, you can just copy the value of the integer you scanned in with scanf directly.
Transcript[classCnt].Hrs = vHrs;
and
Transcript[classCnt].Grade = vGrade;
If you're looking for the best book ever written, well in my opinion anyway :-), on C Language, check out:
C Programming Language (2nd Edition) - Looks like it's available on Amazon (or at you're favorite used book seller) for about $21 (USD).

Copy a string into a char array

Hello i want to copy the input of a user into an array of char which is defined in a struct. Sincerly i have no idea how to do this.
#include <stdio.h>
#include <stdlib.h>
int main ()
{
struct employee
{
char firstname[11];
char lastname[11];
char number[11];
int salary;
}
int i;
int nemps;
const int maxemps = 5;
struct employee* emps[maxemps];
printf("How many employees do you want to store?(max:5): ");
scanf("%d", nemps);
for (i = 0; i < nemps; i++)
{
printf("Employee 1./nLast name:");
scanf("....")// Read the string and copy it into char lastname[]
}
}
First off struct employee* emps[maxemps]; creates an array of pointers to struct employee with array size maxemps. You haven't actually set aside any space in memory for the actual structs, just the pointers that will point to them. To dynamically allocate space on the heap for your structs so you can use them in a meaningful way, you'll need to loop over a call to malloc() like so:
for (i = 0; i < maxemps; i++) {
emps[i] = malloc(sizeof(struct employee));
}
You'll also want a similar loop at the end of your program which will free() each pointer.
Next, when you are getting input from a user you really want to be using fgets() over scanf() because fgets() allows you to specify the number of characters to read in so you can prevent overflows on your destination buffer.
Update
If you want to work with a single struct employee without the use of pointers this is accomplished by declaring one or more struct employee on the stack and then using the . member access operator as follows:
struct employee emp;
fgets(emp.lastname, sizeof(emp.lastname), stdin);
Update2
I found a number of errors in your code. Please see this link for a working sample with comments.
You only need:
scanf("%10s", (emps[i])->last_name);
Here "%10s" denotes a string with a maximum length of 10, and it will load the string to last_name.
In C the string is represented as a char array, with '\0' as the end.
Using scanf here is vulnerable to buffer attacks if the user-input is longer than 10: http://en.wikipedia.org/wiki/Scanf#Security, so you need to assign a maximum length to the format.

Resources