C strings keep overwriting each other - c

Right, so I was screwing around with C with the idea of learning the language by doing a small text based RPG game. I followed the steps of how I did the game similarly in Java but with some different workarounds until I got to the issue of getting output of having the first string being partially overwritten by the second string I was inputting. Here's the bit where the issue occurs (I presume).
struct character{
char firstname[100];
char lastname[100];
};
int main(){
struct character charname;
char first=charname.firstname[100];
char last=charname.lastname[100];
printf("And what do they call you?(First name)\n");
scanf("%s", &first);
printf("Any other names?(Last name)\n");
scanf("%s", &last);
printf("So you are called ");
printf("%s ", &first);
printf("%s ", &last);
return 0;
}
The Output I got was:
And what do they call you?(First name)
Bob
Any other names?(Last name)
Dole
So you are called BDole Dole
I am not BDole Dole but Bob Dole!!!
Any ideas?
(Just realised how horrible the code input thing here is)

When you do
char first=charname.firstname[100];
you get character 101 from an array containing 100 characters (remember that array indexes are zero-based). This alone is undefined behavior.
You then make it worse by using &first getting a pointer not into the array, not even beyond the array, but a pointer to where the local variable first is in memory, writing a string to this location will overwrite memory on the stack, and lead to very serious problems.
What you probably want is e.g.
char *first=charname.firstname;
But that isn't even needed, as arrays naturally decays to pointers to their first element, meaning this is perfectly valid:
scanf("%99s", charname.firstname);
Note the "%99s" format, which tells scanf to not read more than 99 characters, so it won't write beyond the end of the provided buffer.

This doesn't do what you think it does:
char first=charname.firstname[100];
char last=charname.lastname[100];
Probably what you meant to do was create an array[100] of char, like you would in Java with the "new" operator. But C doesn't work that way. The struct definition tells the compiler that a "struct character" has two 100-char strings in it, so when you write
struct character charname;
the space for both of those strings is reserved. In advance of needing it, on the stack. It's not initialized, but it's there and it belongs to the struct.
So you can populate those arrays now by calling scanf. Notice that no & or [] are required here; the array is said to "decay" into a pointer when used for its value.
scanf("%s", charname.first);
/* later... */
scanf("%s", charname.last);
However, it is usually smarter to use fgets() -- see this C FAQ entry.
Aside, it looks like you may be trying to learn C by taking Java-like code and modifying it haphazardly until it compiles. This does not work, especially in C. In some languages, you can get a pretty good idea of what is legal by listening to what the compiler tells you; but C has a comparatively weak type system and so it can't tell when you do something dumb.

Related

I have problem in string, in using the gets function, in c language

I have a problem with string in the c language
we know, the string is known as a null-terminated character** array, in the string, we declare how many characters this string is store. and the memory of the character is 1byte. when we declare char name[5]; it means the string store at least 4 characters value and null value.
but there is a problem withstrong text the gets function it.
when we have entered a name that contains more than 5 characters it is accepted and prints all characters. In this case, the compiler does not throw the warning.
Please tell me the solution to it.......
the code is below.
#include<stdio.h>
int main()
{
char name[4]="smti prajapati";
printf("Print the name througth the initilizing : ");
puts(name);
printf("Enter the name : ");
gets(name);
printf("Print the name througth the user : ");
puts(name);
return 0;
}
In the terminal program trow the look like this error
rough.c: In function 'main':
rough.c:5:18: ***warning: initializer-string for array of chars is too long***
char name[1]="smti prajapati";
^~~~~~~~~~~~~~~~
Print the name througth the initilizing : s3
Enter the name : smit prajapati
Print the name througth the user : smit prajapati
You’ve declared the name array to have a single element; it’s not large enough to hold the contents of the string "smti prajapati". You’d need to declare name to be at least 15 elements wide - 14 characters in the string plus the zero terminator.
You can omit the size in the declaration and write
char name[] = "smti prajapati";
and the array size will be taken from the size of the initializer.
Note that the size of name is fixed after it is defined, and will not able to store strings longer than the initializer.
Do not use gets; if you enter a string that’s too long for the array, it will write those extra characters to whatever follows the array. This is known as a buffer overflow, and can lead to anything from corrupted data, a runtime error, or branching to a random location in your program. Buffer overflows are a common malware exploit. Use fgets instead, as it allows you to limit how many characters get written to the target.
The declaration and assignment:
char name[1] = "smti prajapati";
Can never work because you assign a string with 14 characters plus the null byte to a char array that only has space for 1 character.
The reason why you don't get errors or warnings with some ill-formed code can be explained by leeway given by C to the programmer, compilers are not mandated to issue warnings due to some types of ill-formed code like your own, this falls in the category of undefined behavior, and it's up to the programmer to avoid it.
You could use:
char name[] = "smti prajapati";
This way the size of the array will be deduced when initialized.
Or if you want to explicitly use a size:
char name[15] = "smti prajapati";
Note that I added an extra space for the null byte, it is mandatory if you want to handle name as a propper string. Also note that once the char array is given a size, it will not change, so when you store other strings in name keep that in mind.
The other problem is gets, you should never use it, it's a very dangerous function, it does not check the bounds of the destination buffer and therefore can easily cause a buffer overflow and that can cause all kinds of trouble. Recent compilers don't even support it anymore as it was deprecated and later removed from the C standard.
Even those which do still support it often issue warnings similar to:
warning: the `gets' function is dangerous and should not be used.
Here are two examples of the output of two modern compilers when you use it.
Summarizing and concluding, use fgets instead:
fgets(name, sizeof name, stdin);
When you use arrays in C you are using a fixed memory space. If I want to save "Hello World!" in memory, I have to reserve an array of length 13. In C it looks as:
char aString[13] = "Hello World!"
It may go as high as 20, but no lower than 13. Why? Strings have characters that we care and the last one is the null character (its value is 0). So, you need to reserve thirteen characters.
In your code you are reserving only 1 byte (1 character). At least, you have to reserve 15 bytes.
Try this code:
#include<stdio.h>
int main()
{
char name[15]="smti prajapati";
printf("Print the name througth the initilizing : ");
puts(name);
printf("Enter the name : ");
gets(name);
printf("Print the name througth the user : ");
puts(name);
return 0;
}
I compiled the code without problems. Also, you are able to not specify the length and It is going to work right. The best solution is dynamic memory. Avoid gets(). It has security issues. Instead, use fgets().
You have a number of misunderstandings. Let's look at them in order.
in the c language we know, the string is known as a null-terminated character** array
In C, a string is a null-terminated array of char. It is very common to refer to a string using a character pointer, char *. It may have been a typo when you said "character**", but type char ** is a pointer-to-pointer, which is something else.
when we declare char name[5]; it means the string store at least 4 characters value and null value.
Correct.
char name[1]="smti prajapati";
This is incorrect. Your compiler correctly warned you: warning: initializer-string for array of chars is too long
when we have entered a name that contains more than 5 characters it is accepted and prints all characters.
Right. Sometimes, when you break the rules, you get away with it.
In this case, the compiler does not throw the warning.
Right. C does not always detect or complain about buffer overflow. In general, it is your responsibility to make sure your arrays are allocated large enough for the strings you try to store in them.

How do you assign a string in C

Printing the initials (first character) of the string held in the variable 'fn' and the variable 'ln'
#include <stdio.h>
#include <cs50.h>
int main(void)
{
string fn, ln, initials;
fn = get_string("\nFirst Name: ");
ln = get_string("Last Name: ");
initials = 'fn[0]', 'ln[0]';
printf("%s", initials)
}
Read more about C. In particular, read some good C programming book, and some C reference site and read the C11 standard n1570. Notice that cs50.h is not a standard C header (and I never encountered it).
The string type does not exist. So your example don't compile and is not valid C code.
An important (and difficult) notion in C is : undefined behavior (UB). I won't explain what is it here, but see this, read much more about UB, and be really afraid of UB.
Even if you (wrongly) add something like
typedef char* string;
(and your cs50.h might do that) you need to understand that:
not every pointer is valid, and some pointers may contain an invalid address (such as NULL, or most random addresses; in particular an uninitialized pointer variable often has an invalid pointer). Be aware that in your virtual address space most addresses are invalid. Dereferencing an invalid pointer is UB (often, but not always, giving a segmentation fault).
even when a pointer to char is valid, it could point to something which is not a string (e.g. some sequence of bytes which is not NUL terminated). Passing such a pointer (to a non-string data) to string related functions -e.g. strlen or printf with %s is UB.
A string is a sequence of bytes, with additional conventions: at the very least it should be NUL terminated and you generally want it to be a valid string for your system. For example, my Linux is using UTF-8 (in 2017 UTF-8 is used everywhere) so in practice only valid UTF-8 strings can be correctly displayed in my terminals.
Arrays are decayed into pointers (read more to understand what that means, it is tricky). So in several occasions you might declare an array variable (a buffer)
char buf[50];
then fill it, perhaps using strcpy like
strcpy(buf, "abc");
or using snprintf like
int xx = something();
snprintf(buf, sizeof(buf), "x%d", xx);
and latter you can use as a "string", e.g.
printf("buf is: %s\n", buf);
In some cases (but not always!), you might even do some array accesses like
char c=buf[4];
printf("c is %c\n", c);
or pointer arithmetic like
printf("buf+8 is %s\n", buf+8);
BTW, since stdio is buffered, I recommend ending your printf control format strings with \n or using fflush.
Beware and be very careful about buffer overflows. It is another common cause of UB.
You might want to declare
char initials[8];
and fill that memory zone to become a proper string:
initials[0] = fn[0];
initials[1] = ln[0];
initials[2] = (char)0;
the last assignment (to initials[2]) is putting the NUL terminating byte and makes that initials buffer a proper string. Then you could output it using printf or fputs
fputs(initials, stdout);
and you'll better output a newline with
putchar('\n');
(or you might just do puts(initials); ....)
Please compile with all warnings and debug info, so gcc -Wall -Wextra -g with GCC. Improve your code to get no warnings. Learn how to use your compiler and your debugger gdb. Use gdb to run your program step by step and query its state. Take time to read the documentation of every standard function that you are using (e.g. strcpy, printf, scanf, fgets) even if at first you don't understand all of it.
char initials[]={ fn[0], ln[0], '\0'};
This will form the char array and you can print it with
printf("%s", initials) //This is a string - null terminated character array.
There is no concept of string datatype in c . We simulate it using null terminated character array.
If you don't put the \0 in the end, it won't be a null terminated char array and if you want to print it you will have to use indexing in the array to determine the individual characters. (You can't use printf or other standard functions).
int s[]={'h','i'} // not null terminated
//But you can work with this, iterating over the elements.
for(size_t i=0; i< sizeof s; i++)
printf("%c",s[i]);
To explain further there is no string datatype in C. So what you can do is you simulate it using char [] and that is sufficient for that work.
For example you have to do this to get a string
char fn[MAXLEN}, ln[MAXLEN];
Reading an input can be like :
if(!fgets(fn, MAXLEN,stdin) ){
fprintf(stderr,"Error in input");
}
Do similarly for the second char array.
And then you do form the initializationg of array initials.
char initials[]={fn[0],ln[0],'\0'}
The benefit of the null terminated char array is that you can pass it to the fucntions which works over char* and get a correct result. Like strcmp() or strcpy().
Also there are lots of ways to get input from stdin and it is better always to check the return type of the standard functions that you use.
Standard don't restrict us that all the char arrays must be null terminated. But if we dont do that way then it's hardly useful in common cases. Like my example above. That array i shown earlier (without the null terminator) can't be passed to strlen() or strcpy() etc.
Also knowingly or unknowingly you have used somnething interesting The comma operator
Suppose you write a statememnt like this
char initialChar = fn[0] , ln[0]; //This is error
char initialChar = (fn[0] , ln[0]); // This is correct and the result will be `ln[0]`
, operator works that first it tries to evaluate the first expression fn[0] and then moves to the second ln[0] and that value is returned as a value of the whole expression that is assigned to initialChar.
You can check these helpful links to get you started
Beginner's Guide Away from scanf()
How to debug small programs

How can I use gets() in a function to assign string in char *ch?

How can I write gets(???);
Thank you.
void getStr(**temp){
gets(???);
}
void main(){
char *ch;
printf("Enter a string: \n");
getStr(&ch);
printf("main: %s\n", ch);
}
------ Output ------
Enter a string:
abc
main: abc
Never use the gets function. It is inherently unsafe, since there's no way to guard against overruns (the user entering more data than you're prepared to accept). In fact, it was removed from the language by the 2011 ISO C standard.
You should probably use fgets() instead. It's a bit more complicated to use (for one thing, it leaves the '\n' line terminator in the string), but it lets you specify the maximum number of characters to be read.
Your getStr function probably doesn't need to take a char** argument; a char* would do, since it's not going to be modifying the pointer, just reading data into an array to which the pointer points.
You'll need to allocate a char array to read the data into. You can either declare an array object:
char line[200]; // for example
or use malloc to allocate the space.
One more thing: void main() is incorrect. (Compilers are allowed to accept it, but there is no good reason to use it.) The correct definition is int main(void). If you have a book that's telling you to use void main(), it was written by someone who doesn't know the C language very well, and it's likely to have more serious errors.
Take a look at the comp.lang.c FAQ.

scanf() does not read input string when first string of earlier defined array of strings in null

I defined an array for strings. It works fine if I define it in such a way the first element is not an empty string. When its an empty string, the next scanf() for the other string stops reading the input string and program stops execution.
Now I don't understand how can defining the array of strings affect reading of input by scanf().
char *str_arr[] = {"","abc","","","b","c","","",""}; // if first element is "abc" instead of "" then works fine
int size = sizeof(str_arr)/sizeof(str_arr[0]);
int i;
printf("give string to be found %d\n",size);
char *str;
scanf("%s",str);
printf("OK\n");
Actually, you are getting it wrong my brother. The initialization of str_arr doesn't affect the working of scanf() , it may however seem to you like that but it ain't actually. As described in other answers too this is called undefined behavior. An undefined behavior in C itself is very vaguely defined .
The C FAQ defines “undefined behavior” like this:
Anything at all can happen; the Standard imposes no requirements. The
program may fail to compile, or it may execute incorrectly (either
crashing or silently generating incorrect results), or it may
fortuitously do exactly what the programmer intended.
It basically means anything can happen. When you do it like this :
char *str;
scanf("%s",str);
Its an UB. Sometimes you get results which you are not supposed to and you think its working.That's where debuggers come in handy.Use them almost every time, especially in the beginning. Other recommendation w.r.t your program:
Instead of scanf() use fgets() to read strings. If you want to use scanf then use it like scanf("%ws",name); where name is character array and w is the field width.
Compile using -Wall option to get all the warnings, if you would have used it, you might have got the warning that you are using str uninitialized.
Go on reading THIS ARTICLE, it has sufficient information to clear your doubts.
Declaring a pointer does not allocate a buffer for it in memory and does not initialize it, so you are trying to dereference an uninitialized pointer (str) which results in an undefined behavior.
Note that scanf will cause a potential buffer overflow if not used carefully when reading strings. I recommend you read this page for some ideas on how to avoid it.
You are passing to scanf a pointer that is not initialized to anything particular, so scanf will try to write the characters provided by the user in some random memory location; whether this results in a crash or something else depends mostly by luck (and by how the compiler decides to set up the stack, that we may also see as "luck"). Technically, that's called "undefined behavior" - i.e. as far as the C standard is concerned, anything can happen.
To fix your problem, you have to pass to scanf a buffer big enough for the string you plan to receive:
char str[101];
scanf("%100s",str); /* the "100" bit tells to scanf to avoid reading more than 100 chars, which would result in a buffer overflow */
printf("OK\n");
And remember that char * in C is not the equivalent of string in other languages - char * is just a pointer to char, that knows nothing about allocation.

Using an ampersand in scanf()

When I compile scanf("%s", &var);, gcc sends back a warning:
warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
however when I compile scanf("%s", var);, no warning is applied. Both pieces of code work and the book I am reading specifically says to use the ampersand, but even it doesn't in some of the examples.
My question is, should I continue to use the ampersand, even when the book doesn't specify?
From what you've posted var is a char array. In that case, you don't need the ampersand, just the name var will evaluate to a (char *) as needed.
Details:
scanf needs a pointer to the variable that will store input. In the case of a string, you need a pointer to an array of characters in memory big enough to store whatever string is read in. When you declare something like char var[100], you make space for 100 chars with var[0] referring to the first char and var[99] referring to the 100th char. The array name by itself evaluates to exactly the same thing as &var[0], which is a pointer to the first character of the sequence, exactly what is needed by scanf. So all you need to do is scanf("%s", var);, but be aware that scanf does not enforce size constraints on input strings, so if the user inputs a 101 length string your will have a buffer overrun, which will result in bugs or, even worse, security problems. The better choice is generally fgets which does allow size constraints for input strings.
I invite any other answerers to give a good summary of pointers and references here. I don't think I can do that without making some mistakes here and there, and I have no intent to deal with the nerd rage that will follow.
So I'll just point you to a good resource to learn about pointers and references which lies at the heart of your problem. http://www.cplusplus.com/doc/tutorial/pointers/
Remove the & from the scanf("%s", &var); so it is scanf("%s", var);.
When you use an array like this one:
char name[20];
you must consider that a memory area composed by 20 chars is associated with it. To obtain the address of the beginning of that memory area you must use the name of the array, while to obtain the address of a single char of that array, you have to use a sintax like this: &name[i].
You are using scanf to read a string, which is an array of char in C. scanf require the address of the memory area associated to the type of variable to read. In this case, you are reading an array so you have only to use the name.
If you want to read a single variable and not an array you have to use the ampersand. For example:
char myChar;
scanf("%c", &myChar);
I hope this can be helpful.

Resources