Consecutive scanf is adding up strings [duplicate] - c

This question already has answers here:
How to prevent scanf causing a buffer overflow in C?
(6 answers)
Closed 6 years ago.
I've been having a problem with a very simple program and I really don't know why. There is a struct for a person:
typedef struct {
char name[50];
char p_id[11];
char cel[11];
int by;
int id;
} Person;
Now, there is another struct which stands for the list of contacts:
typedef struct {
Person * people;
} lContacts;
I've been trying to include the person's data to it, and add that person to the contact list. The person is being added normally, so I won't post the code here, but there is something wrong happening when I read the string:
void include(lContacts * myContacts)
{
Person p;
scanf("%s", p.name);
scanf("%d", &p.by); //birth year
scanf("%s", p.p_id);
printf("TEST P_ID: %s\n\n", p.p_id);
scanf("%s", p.cel);
printf("TEST P_ID AGAIN: %s\n\n", p.p_id);
myContacts->people[index]=p; //don't worry about the index, there is a piece of code I'm omitting to make it easier to read, just assume it is right.
}
}
Notice that I have a print test there, because when I listed my contacts, the contact p_id had itself concatenated with the cel, so I printed the whole code until I found the mistake was there.
Here is a input example:
Name
1991
11111111111
<console prints| TEST P_ID: 11111111111>
22222222222
<console prints| TEST P_ID AGAIN: 1111111111122222222222>
however, if I print p.cel, it is correctly printed
<console prints 22222222222>
Any ideas? Maybe I should use '&' when scanning strings? (I read about it and the way I understood, there is no need.. is that right?)

This is actually a very minute mistake here. Basically, you are experiencing a Buffer Overflow in the string p.p_id.
When you input the value for p.p_id as 11111111111 (11 times 1), you must realise that the actual capacity for the p_id string is declared as just 11 which must include the NULL character at the end.
If you assign 11111111111 to p_id there will be no space left for the NULL character in that string and so there will NOT be any at its end.
When you input the value for next member string of the structure, the same happens there, too.
Now, when you are trying to print the value of p.p_id the value will print until a '\0' (NULL character) is found in the string. But, there is none here, so the next string will start printing! (The buffer or string p_id overflows)
This is happening due to the member alignment done in structures in C. The next string will be stored in a consecutive memory cell, so the printing will continue. (If there was a third consecutive string member, then due to the buffer overflow in the second string, it will print too!)
But, the printing stops after second string because either there is no data in the next consecutive memory cell due to certain struct alignment or there may be an actual 0 value in the next cell which is interpreted as NULL character.
To avoid, either use a larger size for the character array or use dynamically allocated strings.

As #skrtbhtngr pointed out, you have a buffer overflow. In order to prevent this in the future, you should use fgets instead of scanf on unknown string inputs. This example will exit(1) if the input is corrupted.
int getsafestring(char* s, int maxlen)
{
if(fgets(s,maxlen,stdin) == NULL) return 1; // read error
if(strlen(s) == 0) return 2; // other read error
if(s[strlen(s)-1] !='\n') return 3; // buffer overflow error
s[strlen(s)-1]=0; // replace newline with null
return 0;
}
if(Getsafestring(p.name,sizeof(p.name))) exit(1);
if(scanf("%d", &p.by) != 1) exit(1);
if(getsafestring(p.p_id,sizeof(p.p_id))) exit(1);
if(getsafestring(p.cel,sizeof(p.cel))) exit(1);

Related

What is the point of assigning the size of a string?

For an instance if I store ABCDE from scanf function, the later printf function gives me ABCDE as output. So what is the point of assigning the size of the string(Here 4).
#include <stdio.h>
int main() {
int c[4];
printf("Enter your name:");
scanf("%s",c);
printf("Your Name is:%s",c);
return 0;
}
I'll start with, don't use int array to store strings!
int c[4] allocates an array of 4 integers. An int is typically 4 bytes, so usually this would be 16 bytes (but might be 8 or 32 or something else on some platforms).
Then, you use this allocation first to read characters with scanf. If you enter ABCDE, it uses up 6 characters (there is an extra 0 byte at the end of the string marking the end, which needs space too), which happens to fit into the memory reserved for array of 4 integers. Now you could be really unlucky and have a platform where int has a so called "trap representation", which would cause your program to crash. But, if you are not writing the code for some very exotic device, there won't be. Now it just so happens, that this code is going to work, for the same reason memcpy is going to work: char type is special in C, and allows copying bytes to and from different types.
Same special treatment happens, when you print the int[4] array with printf using %s format. It works, because char is special.
This also demonstrates how very unsafe scanf and printf are. They happily accept c you give them, and assume it is a char array with valid size and data.
But, don't do this. If you want to store a string, use char array. Correct code for this would be:
#include <stdio.h>
int main() {
char c[16]; // fits 15 characters plus terminating 0
printf("Enter your name:");
int items = scanf("%15s",c); // note: added maximum characters
// scanf returns number of items read successfully, *always* check that!
if (items != 1) {
return 1; // exit with error, maybe add printing error message
}
printf("Your Name is: %s\n",c); // note added newline, just as an example
return 0;
}
The size of an array must be defined while declaring a C String variable because it is used to calculate how many characters are going to be stored inside the string variable and thus how much memory will be reserved for your string. If you exceed that amount the result is undefined behavior.
You have used int c , not char c . In C, a char is only 1 byte long, while a int is 4 bytes. That's why you didn't face any issues.
(Simplifying a fair amount)
When you initialize that array of length 4, C goes and finds a free spot in memory that has enough consecutive space to store 4 integers. But if you try to set c[4] to something, C will write that thing in the memory just after your array. Who knows what’s there? That might not be free, so you might be overwriting something important (generally bad). Also, if you do some stuff, and then come back, something else might’ve used that memory slot (properly) and overwritten your data, replacing it with bizarre, unrelated, and useless (to you) data.
In C language the last of the string is '\0'.
If you print with the below function, you can see the last character of the string.
scanf("%s", c); add the last character, '\0'.
So, if you use another function, getc, getch .., you should consider adding the laster character by yourself.
#include<stdio.h>
#include<string.h>
int main(){
char c[4+1]; // You should add +1 for the '\0' character.
char *p;
int len;
printf("Enter your name:");
scanf("%s", c);
len = strlen(c);
printf("Your Name is:%s (%d)\n", c, len);
p = c;
do {
printf("%x\n", *(p++));
} while((len--)+1);
return 0;
}
Enter your name:1234
Your Name is:1234 (4)
31
32
33
34
0 --> last character added by scanf("%s);
ffffffae --> garbage

Is it possible to use a strcpy inside a scanf or a sscanf? (in C language)

I read somewhere that when we use structs we can't just write something like «example1.name = "Jim";», instead of it we should write «strcpy(example1.name, "Jim");»
The thing is that I need to use a struct and I need to scanf (and right after that sscanf) some information that corresponds to a string and I don't know how I should do it.
Could somebody help me, please?
Edit:
My code isn't complete and I know it's wrong, but I'll post it here so that you know what I am talking about:
int main(){
struct Cards{
int value;
char type[4];
};
for(i=1, 0 < i && i < 11, i++){
struct Cards cardi;
}
scanf("%d %s", &cardi.value, cardi.type);
/*at this point I just know it's wrong but I am really bugged.
I thought about something like «scanf("%d %s", &cardi.value, strcpy(cardi.type, "%s"));»
but I just know it's very wrong */
return 0;
}
This isn't true only about structs, but for all strings. You can use = for strings, only when you initialize them. You can scanf a string and place it in a string. Example:
scanf("%s", my_struct.str);
If you already have a string and you want to pass it in a struct, or in an other string variable you then need strcpy:
char str1[] = "abc", str2[4];
strcpy(str2, str1);
or
strcpy(my_struct.str, str1);
Edit:
for(i=1, 0 < i && i < 11, i++) {
struct Cards cardi;
}
In your code cardi is not card0, card1 etc, it is a struct Cards variable with the name cardi.
If you want to store 10 cards, you sould make an array of struct Cards with capacity of 10 like:
struct Cards array[10];
for (i = 0; i < 10; i++) {
scanf("%d %s", &array[i].value, array[i].type);
}
Anyway i suggest that you focus on learning the basics on arrays, strings and pointers before you use structs.
Edit 2: You don't want to define structs inside your main, because in that way, if you write a function it will not "see" your struct. Structs usually are written in the top of the code.
Your use of scanf() is correct. However scanning strings using %s is dangerous and discouraged. This is because the type member of your structure has space for only 4 characters, including the terminating '\0' (NUL) character. Entering a string longer than 3 characters will result in the extra characters being written into an unknown area of memory likely corrupting other variables and leading to incorrect program execution or a program crash. You can correct this by adding a string length limit, simply replace %s with %3s, where 3 is the maximum numbers of characters that will be accepted (scanf will automatically add a '\0' (NUL) character following the last scanned character).
Below are some additional comments:
If you want to declare an array for 10 Cards, you could do it this way without any loops:
struct Cards cardi[10];
To scan values into the first card (C arrays are 0-based):
// this will scan into the first card; type field is restricted to at most 3 characters
scanf("%d %3s", &cardi[0].value, cardi[0].type);
At the top of the file you'll want to add:
#include <stdio.h>
This header file provides a prototype (declaration) for the scanf function (among many others).

String concatenation in C?

I am trying to understand string's behavior in C and it is bothering me since my following two code snippets result into different output:
(For the sake of this question, Let us assume user enters 12)
int main(void)
{
char L_Red[2];
char temp[] = "I";
printf("Enter pin connected to red: ");
scanf("%s", L_Red);
strcat(temp,L_Red);
printf("%s \n", temp);
return 0;
}
this yields: 12 as output (and not I12) Why ?
int main(void)
{
char L_Red[2];
printf("Enter pin connected to red: ");
scanf("%s", L_Red);
char temp[] = "I";
strcat(temp,L_Red);
printf("%s \n", temp);
return 0;
}
This yields: I12I (and not, I12) Why ?
I have read about string in C and as per my understanding, neither am I allocating temp any fixed size and changing it later to get these vague outputs nor am I using strings like the way they are not supposed to. Is there any other concept at play here ?
The array temp is an array of two characters (the 'I' and the string terminator '\0'). That's it. Attempting to append more characters to that array will write out of bounds and lead to undefined behavior.
You need to make sure that the destination array temp have enough space to fit its original content plus the string you want to append (plus the terminator).
Also, if you want to input more than one character for the "string" L_Red you need to increase its size as well.
I also recommend you use a limit in the format specifier so you can't write out of bounds:
char L_Red[3]; // Space for two characters, plus terminator
scanf("%2s", L_Red); // Read at most two characters of input
You are getting strange answers because your destination string (ie the first argument to strcat) is not long enough to handle both strings plus a null termination character. Also the length of L_Red is too short as it does not have enough space for the null termination character either.

C - Using fgets until newline/-1 [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
So I'm trying to make it so that you can write text into a file until you make a newline or type -1. My problem is that when you write, it just keeps going until it crashes and gives the error "Stack around the variable "inputChoice" was corrupted".
I believe the problem is that the program doesn't stop accepting stdin when you want to stop typing (-1, newline) and that causes the error. I've tried with a simple scanf and it works, but you can only write a word. No spaces and it doesn't support multiple lines either. That's why I have to use fgets
Judging from your comments, I assume that there are some basic concepts in C
that you haven't fully understood, yet.
C-Strings
A C-String is a sequence of bytes. This sequence must end with the value 0.
Every value in the sequence represents a character based on the
ASCII encoding, for example the
character 'a' is 97, 'b' is 98, etc. The character '\0' has
the value 0 and it's the character that determines the end of the string.
That's why you hear a lot that C-Strings are '\0'-terminated.
In C you use an array of chars (char string[], char string[SOME VALUE]) to
save a string. For a string of length n, you need an array of dimension n+1, because
you also need one space for the terminating '\0' character.
When dealing with strings, you always have to think about the proper type,
whether your are using an array or a pointer. A pointer
to char doesn't necessarily mean that you are dealing with a C-String!
Why am I telling you this? Because of:
char inputChoice = 0;
printf("Do you wish to save the Input? (Y/N)\n");
scanf("%s", &inputChoice);
I haven't changed much, got very demotivated after trying for a while.
I changed the %s to an %c at scanf(" %c, &inputChoice) and that
seems to have stopped the program from crashing.
which shows that haven't understood the difference between %s and %c.
The %c conversion specifier character tells scanf that it must match a single character and it expects a pointer to char.
man scanf
c
Matches a sequence of characters whose length is specified by the maximum field
width (default 1); the next pointer must be a
pointer to char, and there must be enough room for all the characters
(no terminating null byte is added). The usual skip of
leading white space is suppressed. To skip white space first, use an explicit space in the format.
Forget the bit about the length, it's not important right now.
The important part is in bold. For the format scanf("%c", the function
expects a pointer to char and its not going to write the terminating '\0'
character, it won't be a C-String. If you want to read one letter and one
letter only:
char c;
scanf("%c", &c);
// also possible, but only the first char
// will have a defined value
char c[10];
scanf("%c", c);
The first one is easy to understand. The second one is more interesting: Here
you have an array of char of dimension 10 (i.e it holds 10 chars). scanf
will match a single letter and write it on c[0]. However the result won't be
a C-String, you cannot pass it to puts nor to other functions that expect
C-Strings (like strcpy).
The %s conversion specifier character tells scanf that it must match a sequence of non-white-space characters
man scanf
s
Matches a sequence of non-white-space characters; the next pointer must be a
pointer to the initial element of a character array that is long enough to
hold the input sequence and the terminating null byte ('\0'), which is added
automatically.
Here the result will be that a C-String is saved. You also have to have enough
space to save the string:
char string[10];
scanf("%s", string);
If the strings matches 9 or less characters, everything will be fine, because
for a string of length 9 requires 10 spaces (never forget the terminating
'\0'). If the string matches more than 9 characters, you won't have enough
space in the buffer and a buffer overflow (accessing beyond the size) occurs.
This is an undefined behaviour and anything can happen: your program might
crash, your program might not crash but overwrites another variable and thus
scrwes the flow of your program, it could even kill a kitten somewhere, do
you really want to kill kittens?
So, do you see why your code is wrong?
char inputChoice = 0;
scanf("%s", &inputChoice);
inputChoice is a char variable, it can only hold 1 value.
&inputChoice gives you the address of the inputChoice variable, but the
char after that is out of bound, if you read/write it, you will have an
overflow, thus you kill a kitten. Even if you enter only 1 character, it will
write at least 2 bytes and because you it only has space for one character, a kitten will die.
So, let's talk about your code.
From the perspective of an user: Why would I want to enter lines of text, possibly a lot of lines of text
and then answer "No, I don't want to save the lines". It doesn't make sense to
me.
In my opinion you should first ask the user whether he/she wants to save the
input first, and then ask for the input. If the user doesn't want to save
anything, then there is no point in asking the user to enter anything at
all. But that's just my opinion.
If you really want to stick to your plan, then you have to save every line and
when the user ends entering data, you ask and you save the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFERLEN 1024
void printFile () {
int i;
char openFile[BUFFERLEN];
FILE *file;
printf("What file do you wish to write in?\n");
scanf("%s", openFile);
getchar();
file = fopen(openFile, "w");
if (file == NULL) {
printf("Could not open file.\n");
return;
}
// we save here all lines to be saved
char **lines = NULL;
int num_of_lines = 0;
char buffer[BUFFERLEN];
printf("Enter an empty line of -1 to end input\n");
// for simplicity, we assume that no line will be
// larger than BUFFERLEN - 1 chars
while(fgets(buffer, sizeof buffer, stdin))
{
// we should check if the last character is \n,
// if not, buffer was not large enough for the line
// or the stream closed. For simplicity, I will ignore
// these cases
int len = strlen(buffer);
if(buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if(strcmp(buffer, "") == 0 || strcmp(buffer, "-1") == 0)
break; // either an empty line or user entered "-1"
char *line = strdup(buffer);
if(line == NULL)
break; // if no more memory
// process all lines that already have been entered
char **tmp = realloc(lines, (num_of_lines+1) * sizeof *tmp);
if(tmp == NULL)
{
free(line);
break; // same reason as for strdup failing
}
lines = tmp;
lines[num_of_lines++] = line; // save the line and increase num_of_lines
}
char inputChoice = 0;
printf("Do you wish to save the Input? (Y/N)\n");
scanf("%c", &inputChoice);
getchar();
if (inputChoice == 'Y' || inputChoice == 'y') {
for(i = 0; i < num_of_lines; ++i)
fprintf(file, "%s\n", lines[i]); // writing every line
printf("Your file has been saved\n");
printf("Please press any key to continue");
getchar();
}
// closing FILE buffer
fclose(file);
// free memory
if(num_of_lines)
{
for(i = 0; i < num_of_lines; ++i)
free(lines[i]);
free(lines);
}
}
int main(void)
{
printFile();
return 0;
}
Remarks on the code
I used the same code as yours as the base for mine, so that you can spot the
differences much quicker.
I use the macro BUFFERLEN for declaring the length of the buffers. That's
my style.
Look at the fgets line:
fgets(buffer, sizeof buffer, stdin)
I use here sizeof buffer instead of 1024 or BUFFERLEN. Again, that's my
style, but I think doing this is better, because even if you change the size
of the buffer by changing the macro, or by using another explicit size, sizeof buffer
will always return the correct size. Be aware that this only works when
buffer is an array.
The function strdup returns a pointer a pointer to a new string that
duplicates the argument. It's used to create a new copy of a string. When
using this function, don't forget that you have to free the memory using
free(). strdup is not part of the standard library, it conforms
to SVr4, 4.3BSD, POSIX.1-2001. If you use Windows (I don't use Windows,
I'm not familiar with the Windows ecosystem), this function might not be
present. In that case you can write your own:
char *strdup(const char *s)
{
char *str = malloc(strlen(s) + 1);
if(str == NULL)
return NULL;
strcpy(str, s);
return str;
}

I received the error message: Run-Time Check Failure #2. Stack around the variable was corrupted. However, I was not out of bounds of any array

I've been doing some exercise with structures to prepare for my upcoming exams, and I've run into a bit of trouble with this code.
// Creates a structure of type 'person' with a first name (nameF), last name (nameL), and the age (age)
typedef struct {
char nameF[20];
int age;
char nameL[40];
}person;
// Main function
int main() {
person ppl[2]; // We are creating 3 "people", whose information shall be printed into the file
// This loop takes user input to create the names and ages of 3 people
int i;
for (i = 0; i <= 2; i++) {
printf("\nEnter first name %d: ", i+1);
scanf("%s", &ppl[i].nameF);
printf("%s\n", ppl[i].nameF);
}
printf("It worked\n");
system("pause");
return 0;
}
It runs fine, but when the program exits, I keep receiving an error message from the Debugger that states: "Run-Time Check Failure #2. Stack around the variable 'ppl' was corrupted."
So I looked it up on Stack Overflow, this error appears when you go outside the bounds of a string. I don't understand where in my code I'm going out of bounds.
Here's an example of the output:
Enter first name 1: 'Adolfo'
Adolfo
Enter first name 2: 'Cecilia'
Cecilia
Enter first name 3: 'Tim'
Tim
Press any key to continue...
And then the error message pops up.
As you can see none of my inputs exceed the maximum amount of characters for the variable 'nameF', so there should be no reason for me to receive the error.
I saw someone on Stack Overflow mention that instead of making a character array:
char nameF[20];
One should instead write use dynamic memory allocation:
char * nameF = malloc(<enough bites to support any input);
Unfortunately, I don;t quite have a grasp of memory allocation yet, and when I attempted this code, I received all sorts of other errors, and my program wouldn't even run.
What is the mistake?
Also, I am not sure if this information is important, but I wrote this code using Visual Studio in C.
Edit: "char * nameF[20]" to "char nameF[20]" that asterisk was a leftover from my previous attempts at fixing the code, sorry.
Why are you allocating two structures then filling it with three responses?
Also, scanf is very dangerous as it can easily be abused to overwrite past a buffer. Look for routines (or write one) that limits the input to the length of the string.
hint: scanf man page might lead you to a better version.
Not meant as an answer, just made some adaptions allowing you to continue your work:
typedef struct {
char nameF[20]; // may take on 19 characters (+ string terminating char)
int age;
char nameL[40];
}person;
int main() {
person ppl[3]; // 3, not 2.
int i;
for (i = 0; i <= 2; i++) {
printf("\nEnter first name %d: ", i+1);
scanf("%s", ppl[i].nameF); // removed the &
printf("%s\n", ppl[i].nameF);
}
printf("It worked\n");
system("pause");
return 0;
}

Resources