Using an ampersand in scanf() - c

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.

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.

Why does a certain part of the code output Segmentation Fault?

I'm making this console application where the user has to enter inputs. However, when the code gets to the part where the user inputs a file name which then gets added into a file path using strcat, it outputs Segmentation Fault..
Here is the full code:
int main(int argc, char *argv[])
{
char theFilePath[512];
char theIP[20];
char theFile[100];
char password[1];
char username[10];
printf("Username: ");
scanf("%s" , &username);
printf("Enter password: ");
scanf("%s", &password);
printf("Enter IP: ");
scanf("%d" , &theIP);
printf("Please specify the file: ");
scanf("%s" , &theFile);
strcat(theFilePath, "./passfiles/");
strcat(theFilePath, theFile);
strcat(theFilePath,".pf");
sprintf(theFilePath,"%s",theFilePath);
if (!(file_exist (theFilePath)))
{
printf("The file cannot be found in the path %s", theFilePath);
exit(EXIT_FAILURE);
} else
{
printf("The file exists!");
}
}
Any ideas why it does that?
Thanks in advance!
You have (at least) a couple of issues.
The first is the fact that you generally shouldn't be using an unbounded %s is scanf since it does not protect against buffer overflows (very similar to gets, which was deprecated in C99 and removed from C11).
This is doubly important when your buffers are pitifully small such as char password[1] which means, with the null terminator, all your passwords will have to be zero characters long. Now I'm no world-famous security researcher but I'm reasonably confident there's a security hole there in that scheme somewhere :-)
There are far safer options when needing user input, such as the function that can be found here, one that protects against buffer overflow and provides information on (and recovers from) the user trying to enter too much data.
The second is relying on uninitialised memory. The theFilePath variable is not initialised so may contain arbitrary data in it, yet your use of strcat expects it to contain a num-terminated string. That's a bad assumption to make and can be fixed by simply making the first strcat into a strcpy or, since it's always being set to the same initial value, doing that as part of the variable declaration itself:
char theFilePath[512] = "./passfiles/";
And, as an aside, assuming you're using a modern C compiler, you would be better declaring the variables at the point where you need them, rather than all at the top of the function, meaning this declaration should go where the current (errant) strcat currently is. Localising your declaration and use greatly aids readability.
You may also want to think about the usefulness of the statement:
sprintf(theFilePath,"%s",theFilePath);
Even if it works, it's effectively a no-op since all it does is copy some data to exactly the same place as it currently exists. In any case, it's not guaranteed to work since the standard quite clearly states in C11 7.21.6.6./2:
If copying takes place between objects that overlap, the behavior is undefined.
The character array
char theFilePath[512];
was not initialized by a string. So you may not use the standard C function strcat that concatenates strings. As result the program has undefined behavior.
You could initialize it for example either like
char theFilePath[512] = "";
or like
char theFilePath[512] = { 0 };
or like
char theFilePath[512];
theFilePath[0] = '\0';
Take into account that the scanf function is not safe. It is better to use function fgets.
Also you have to write
printf("Username: ");
scanf("%s" , username);
^^^^^^^
instead of
printf("Username: ");
scanf("%s" , &username);
^^^^^^^^^
Arrays are implicitly converted (with very rare exceptions) to pointers to their first elements in expressions.
While the above answers are all helpful and correct, I would like to offer a slightly different perspective.
scanf is safe if used safely. For example,
char username[10];
scanf("%10s" , username);
is safe. Unfortunately it's not possible to pass sizeof username to scanf.
For filenames, Posix defines PATH_MAX in limits.h as the maximum valid length of a filename. If your system doesn't supply that definition, er, I recommend switching to one that does. In a crunch, MAX_PATH is available on Windows.
I also suggest looking at the man page for getopt(3). While it's useful to learn how to use scanf, it's good practice to accept command-line options for user-provided information that is needed only at startup time.
Finally, see what you can do to crank up your warning level. My recent version of clang, for example, flags your incorrect use,
$ cc -std=c11 addr.c -o addr
addr.c:9:16: warning: format specifies type 'char *' but the argument
has type 'char (*)[10]' [-Wformat]
scanf("%s" , &username);
~~ ^~~~~~~~~
1 warning generated.
Such warnings provide feedback quicker even than questions on SO. ;-)

C strings keep overwriting each other

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.

For "char list[3][10];" why does all of these work as scanf() %s arguments---&list[i],list[i],&list[i][0]?

Isn't char* the only valid argument type for the %s format specifier used in the format specifier string of scanf()?If so,in my program why each one of these work exactly the same for both scanf()'s and printf()'s %s format specifier:
scanf("%s",&list[i]);
scanf("%s",list[i]);
scanf("%s",&list[i][0]);
I'll appreciate if you clear the following confusions that arise from this premise:
1) Why is &name[i] working given it is of type char (*)[].Isn't &name[i][0] is the only valid argument as it is of type char* ?
2) Does name[i] decompose/translate into char* when we pass it as an argument for %s?Is it same as passing &name[i][0],which is the address of the first character of each name we enter?Is it why it works?
#include<stdio.h>
#include<string.h>
int main(void)
{
char list[3][10];
int i;
printf("Enter the three names \n");
for(i=0;i<=2;i++)
scanf("%s",&list[i]); //Why this works?
//scanf("%s",list[i]); //Does it decompose into char* type?
//scanf("%s",&list[i][0]);
for(i=0;i<=2;i++)
printf("%s\n",list+i); //All of these printf() work as well
//printf("%s\n",list[i]);
//printf("%s\n",&list[i][0]);
}
list[i] is a char[10], and undergoes array-to-pointer conversion when passed as an argument, so in that place, it is completely equivalent to &list[i][0].
&list[i] is a char(*)[10] and as such an invalid argument for the %s conversion specifier in printf or scanf, invoking undefined behaviour.
However, since the first byte in list[i] has the same address as the array list[i], usually it works(1), since printf and scanf don't know about the types of their arguments and interpret them according to the format string.
(1) It will fail if the representation of a char* is different from that of a char(*)[10]. That would be uncommon, but is possible.
Variable argument lists -- like used for printf() or scanf() -- cannot check their variable arguments for type-correctness. To the contrary, these functions rely on the format string stating the correct type, because the format string defines the number and type of arguments pulled from the stack.
(Check the documentation for <stdargs.h> for details on how variable argument lists work.)
If you state "%s" as format specifier, scanf() will pull a char * from the stack and write the standard input to that address, no matter what you actually gave as a parameter, or whether you gave a parameter at all.
Needless to say, if your format specifiers don't match your arguments in number, type and order, you might be lucky and actually get a result, but you're definitely invoking undefined behaviour.
(This is talking about the general "why doesn't this give me an error" question, assuming that you don't have a modern compiler and the appropriate warning enabled, like -Wformat for GCC. As for the type conversions in your specific case, see Daniel's answer.)
PS: While we are at it, using scanf() in combination with %s is pretty lethal in any case, because a too-long input would clobber your program. At least limit the input, e.g. via %10s. Better yet, read the input via fgets() and do proper input parsing in memory, since scanf()'s abilities to recover gracefully from malformed input are severely limited.
As long as the argument provides the correct address, it will "do the right thing". However, if you make the example more complex, or use them for arguments to functions that take specific arguments, then you will find out that some of them are not "correct".
The argument to scanf when using a %s format should be the address of a char array, with enough space to store the string being read. In your example &list[i] does not actually provide that, it is the address of a pointer to an array of char. It just so happens that that address is the same as the address to the char array representing the array. &list[i][0] is a pointer to a single character, but it is followed by another 9, so it "works", but it's again technically not correct. list[i] is the only absolutely correct variant. But all alternatives have the same address, so will technically work in this case.
When it comes to printf, again we want the address of a char array.
In this case, both list+i and list[i] mean exactly the same thing - when an array is used in this way, list makes it a pointer to the first element, and +n will give you the nth element, just like the bracket notation will. &list[i][0] is giving the address of a single char, but it's the same address as the first element of the whole array, so will produce the same result.
By the way, I don't think gcc, which does have an understanding of the format string and CAN check and produce warnings for mixing up the format string and the corresponding arguments, will complain about most of those forms. But not all forms are technically correct.

warning: format ‘%d’ expects type ‘int *’, but argument 2 has type ‘int’

So I'm new to C and am having trouble with whats happening with this warning. What does the warning mean and how can i fix it.
The code i wrote is here:
void main(void)
{
char* name = "";
int age = 0;
printf("input your name\n");
scanf("%s\n", name);
printf("input your age\n");
scanf("%d\n", age);
printf("%s %d\n", name, age);
}
The scanf function takes the address of a variable to put the result into.
Writing scanf("%d", &someVar) will pass the address of the someVar variable (using the & unary operator).
The scanf function will drop a number into the piece of memory at that address. (which contains your variable)
When you write scanf("%d", age), you pass the value of the age variable to scanf. It will try to drop a number into the piece of memory at address 0 (since age is 0), and get horribly messed up.
You need to pass &age to scanf.
You also need to allocate memory for scanf to read a string into name:
char name[100];
scanf("%99s\n", name);
The problem is this line:
scanf("%d\n", age);
scanf expects pointer arguments - this is the only way functions can modify parameters in C. In order to fix this one, you need to:
scanf("%d\n", &age);
Which passes the addressof age, which is now a pointer (a pointer is a variable containing an address to another area of memory).
As for this:
char* name = "";
Ouch-ow-please-don't! Ok, I need to explain. You've asked for a pointer to a character type, but as far as everyone's concerned, all you've got is a character, not a whole string. That's right, not enough space. Why does C let you do this? Well, C is basically portable assembler, so you can write wherever you like in memory (or rather, you can try and the compiler won't disagree, but the operating system can and probably will).
What you need to do is read up on memory allocation using malloc in order to allocate some space, otherwise I can input a massive string and it gets put at the address of name onwards.
This can, although by no means will, lead to stack-based vulnerabilities. What, stacks? Yes. A stack is a FILO structure and every time you call a function, you add space onto the stack for your return address and function arguments, as well as frequently function-scope variables.
Where this becomes a problem is if you don't check input sizes. Then, I can write massive values to your stack, including executable code... see Buffer Overflow.
So how do I mitigate this, I hear you say, just yearning not to implement software vulnerabilities? You use functions that allow you to specify your buffer input size and read into a buffer of a specific size, and go from there. That's it. That simple.
See this question: string reading in C.
You should write scanf("%d", &age), as the scanf function needs to modify the value of age, hence the need to pass it "by address" and not "by value".
It mean that it expect a "int *" (That is: A pointer to an integer) but you give it "int" which is an integer. To fix it add & to age in the scanf line so it become.
scanf("%d\n", age);
I'm assuming its complaining about the line scanf("%d\n", age); The issue is that scan f expects a pointer to your variable not the variable. You need to get an address to variable by perpending a '&` and you should be fine.
The warning means exactly what it says: the compiler expects a pointer to int rather than an int in the scanf call.
Quick fix: replace age with &age, and everything will work.
C passes all arguments by value, meaning that if you pass a variable only the value of the variable is passed. The receiving function can modify that value all it wants, but the original value isn't changed. To change the variable, you need to pass a value that points to the variable somehow, which in C is a pointer.
To get a pointer to a variable, prefix it with &. To use a variable you've got a pointer to, prefix the pointer value with *.
In this case, you want scanf to change the value of age, so you need to pass it a pointer.
char* name = "";
name points at a bit or memory large enough to hold a single null character.
scanf("%s\n", name);
You ask for a unknown number of characters to be read and stored at this address. Anything could happen. You must ensure name hass the address of a chunk of memory large enough to hold anything scanf() might read.
char twenty_bytes[20] = "";
scanf("%s\n", twenty_bytes);
hey friend recently means today I have also gone through this stuff.don't worry it's simple.
problem in the below lines of code :
scanf("%s\n", name);
and
scanf("%d\n", age);
you simply written these lines like printf() function. if you are using the scanf() function then always use ampersand(&) before the variable in the scanf() function because it's necessary. & is the 'Address of ' operator, it gives the location of the number(address) used by the variable in memory.
your corrected lines of code:
scanf("%s\n", &name);
and
scanf("%d\n", &age);
Next time when you are going to writting this type of code or using scanf() function always try to keep in mind this stuff due to that you will not get the problem in your code.
Thank you for asking the question due that I also will not make such a mistake in the future while writing such type of code.

Resources