C programming: '&' with arrays in scanf [duplicate] - c

This question already has answers here:
format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[64]
(3 answers)
Closed 9 years ago.
I got a warning in my program, and it says:
format '%c' expects argument of type 'char *' , but argument 2 has type 'char (*)[10]' [-Wformat]
Here's my program:
#include<stdio.h>
int main()
{
char array[10];
scanf("%10c", &array);
printf("%.10s", array);
return 0;
}
The warning disappears when I remove '&' from scanf.
I know, it's an array and doesn't require &. But don't they have same effect?
I mean both '&array' and 'array' give address of its first element, right?
If so, what's the difference here?
I read some related questions here, and googled a lot.
It has been said that '&array' is a pointer to an array of characters if 'array' is an array while 'array' itself is a pointer to char.
According to what it says, since I'm using %c, a pointer to an array of characters should be passed, I think.
Idk, I would very greatful if someone explains how %[width]c works.
I also verified that all 'array', '&array' and '&array[0]' give address of its first element.
Here's what I did:
int main()
{
char array[10];
puts("ADDRESS:");
printf(" %p \n %p \n %p", array, &array, &array[0]);
return 0;
}
If they all give same address, why is it giving such warnings?
It also works for %s.
They all work fine in most of windows compiler, without any warnings.
Since I'm a windows user, I never used gcc compiler before. And what I was thinking was it's just not mandatory to write & as with function pointers.
You don't necessarily have to write & with function pointers, I read.
I'm getting more and more confused, please help me get it.
Thank you.

array and &array both yield a pointer to the same address, but with different types. The former is equivalent in most situations to &array[0], a char * in your case. &array, however, is the address of the array itself, which has type char (*)[10] in your example.

array and &array are not the same... even if they have same address location in it.
array here being char array, it points to a single char, and if you increment it increases by 1 char size.
but &array points to the entire array and if increments it increases by the array size.
scanf function expects for the array.. not &array

Related

C Pointer Question 'int', but argument 2 has type 'int **'

I am currently doing pointers. I have been programming for a long time, but not in C/C++. With that being said, my pointer knowledge is abysmal.
Currently, I am following a guide on YouTube and he prints the code below.
int main() {
int a = 5;
int *p;
p = &a;
printf("%d\n", p);
}
This prints successfully for him, and he sees a memory location. For me, I see the error
warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *'
From this, I expect I need to put an & in front of the p to make it print the value. But then I receive this error,
int main() {
int a = 5;
int *p;
p = &a;
printf("%d\n", &p);
}
'int', but argument 2 has type 'int **'
Where is the hole in my knowledge? Any key tips or strategies when working with this, I don't know why I find this so abstract.
Thanks,
I was expecting the value to print as expected, but instead am greeted with the error.
%d is the wrong format specifier for pointers. That may work on a more lenient or noncompliant implementation, but you should use %p to print pointer values.
Warnings are not errors. You're receiving a warning, which is not stopping your program from working, because the print specifier %d (ie printf("%d")) is for displaying integers, and you're giving it a non-integer argument of type int*.
The problem here is not with the argument, it's with the print specifier. Your attempt at a fix just changes the int* to an int**, which still does not match the format specifier %d. Use %p instead, which is the specifier for pointers, and will fix the warning, and print the address in hexadecimal notation.
You could also suppress the warning with a series of explicit casts from int* to int, but integer representations of memory addresses are generally much less used than hexadecimal representations in the first place.
Note that using wrong format specifier in printf() lead to undefined behaviour1).
The correct format specifier to print a pointer is %p format specifier.
Remember, format specifier %p expect that the argument shall be a pointer to void, so you should type cast pointer argument to void *. The correct statement to print pointer p would be:
printf("%p\n", (void *)p);
C11#7.21.6.1p9 [emphasis added]
9 If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

Trying to scan and print a string in a structure [duplicate]

I am trying to run a simple C program but I am getting this error:
warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
Running Mac OSX Mountain Lion, compiling in terminal using gcc 4.2.1
#include <stdio.h>
int main() {
char me[20];
printf("What is your name?");
scanf("%s", &me);
printf("Darn glad to meet you, %s!\n", me);
return (0);
}
scanf("%s", &me);
should be
scanf("%s", me);
Explanation:
"%s" means that scanf is expecting a pointer to the first element of a char array.
me is an object array and could be evaluated as pointer. That's why you can use me directly without adding &. Adding & to me will be evaluated to 'char (*)[20]' and your scanf is waiting char *
Code critic:
Using "%s" could cause a buffer overflow if the user input string's length is bigger than 20, so change it to "%19s":
scanf("%19s", me);
Except when it is the operand of the sizeof, _Alignof, or unary & operators, or is a string literal being used to initialize an array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and it will evaluate to the address of the first element in the array.
The array me is declared as a 20-element array of char; normally, when the expression me appears in your code, it will be treated as an expression of type "pointer to char". If you had written
scanf("%s", me);
then you wouldn't have gotten the error; the expression me would have been converted to an expression of the correct type.
By using the & operator, however, you've bypassed that rule; instead of a pointer to char, you're passing a pointer to an array of char (char (*)[20]), which is not what scanf expects for the %s conversion specifier, hence the diagnostic.
Another way you could fix this issue is by doing this:
scanf("%s",&me[0]);
You actually have to give the array's starting point (which in most languages is 0).

Why do I have an incompatible pointer types waning but code still works? [duplicate]

This question already has answers here:
Is this a pointer to a pointer of the start of an array?
(2 answers)
Closed 5 years ago.
I got this warning: incompatible pointer types passing 'char (*)[1024]' to parameter of type 'char *'
My code looks like this:
myfunc(char* sString, size_t s_tString)
{
memset(sString, '\0', s_tString);
...
fPipe = (FILE *)popen (sString, "r");
...
}
int main()
{
...
char sString[1024];
myfunc(&sString, sizeof(sString));
...
}
After calling myfunc sString has the value I was expecting but the compiler throws that warning... After asking some coworkers I get why I'm getting that warning but we don't know why the code is still working.
I'm looking for the explanation of why this happens, the fix is pretty simple, rmove the & from the call.
If you use sString is will decay to a pointer to its first element, i.e. &sString[0] which is of type char *.
When you use &sString you get a pointer to the array, which is of type char (*)[1024].
Those two pointers are semantically very different, but fortunately for you they both point to the same location so the function you call works as expected.
To solve the warning, drop the address-of operator & in the call, so only do
myfunc(sString, sizeof(string));
// ^
// Note: No & here
It works since the actual value of &sString and sString end up being the same. The types differ, which triggers the warning, but the address the called function sees is still sane, i.e. it can be treated as a character buffer.
This is because there is no run-time memory representing sString itself, so all the compiler can do when you take its address is return the address of the first element.

Why doesn't scanf() treat array and &array the same?

I wrote this simple program
#include <stdio.h>
int main(void)
{
int array[10];
printf("sizeof(array): %lu\n", sizeof(array));
printf("sizeof(&array): %lu\n", sizeof(&array));
printf("array: %p\n", array);
printf("&array: %p\n", &array);
printf("value: ");
scanf("%d", array); // 1
//scanf("%d, &array"); // 2
}
Output:
sizeof(array): 40
sizeof(&array): 8
array: 0x7fffaab6f480
&array: 0x7fffaab6f480
value: 10
It compiles when I use 1. However, it doesn't when I use 2!
I get this compilation warning
warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type
‘int (*)[10]’ [-Wformat=]
scanf("%d", &array);
^
although array and &array have the same value, they have different sizes (40 and 8 respectively) on my system.
On the other hand, this code
int array[10];
fread(&array, x, y, z);
compiles and works perfectly as
int array[10];
fread(array, x, y, z);
I noticed in the warning message that &array has the type int (*)[10]. Now what does that mean?
I also believe I have no troubles with fread() because it accepts a void *, but what about scanf()? How does it differentiate between pointer types?
And why doesn't it treat array as &array even though they're practically have the same value?
Arrays decays to pointers, so using the array as argument is the same as passing a pointer to the first element.
When you're doing &array, you get a pointer to the array (the int (*)[10] thing), and not a pointer to an integer.
With the scanf and printf family, you must pass the exact type that they expect (after the default argument promotions are applied - but that is not relevant in this example).
This is because the arguments correspond to a ... in the prototype, meaning that the compiler does not attempt to convert your argument to the expected type. If you use the wrong type you just get undefined behaviour.
This happens in the second case, scanf("%d", &array). As your examples show &array does not have type int *, therefore the behaviour is undefined.
In practice, it is likely to work anyway if your compiler uses the same representation for int * as it does for int (*)[10], which all modern compilers do AFAIK. But , of course, you should not rely on undefined behaviour, especially when there is an easy fix available.
In the fread example, it matches the prototype parameter void *. Therefore the compiler converts your argument to void *. Since the first element of an array is at the same memory address as the array itself, (void *)&array == (void *)array, even though array and &array have different types (and possibly even different representations), they both point to the same memory address.
First, your program was compiled very well - warning is not an error.
The warning message tells you that there's mismatch between format and type: "%d" format requires int *, but you sent &array, whose type is int (*)[10].
array is 10-length array of int. Its type is int [10]. However, array can be converted to pointer, that is, &array[0].
When you try to pass array to function, you actually pass a pointer, &array[0]. So both this code
printf("%d", array);
scanf("%d", array);
and this another code
printf("%d", &array[0]);
scanf("%d", &array[0]);
are the same.
Now let's look at another thing, &array. Its type is int (*)[10], that is, pointer to array.
Of course, the actual values of both &array[0] and &array are the same, but they have different types. So they're diferent things, as 'A' and 65 are different.
Altough it looks okay since the values are the same, I don't think it is such a good practice - Use array or &array[0].
Both are syntactically correct C, and do exactly the same thing, as you suggest. However, one is more correct than the other. That said, neither is perfect.
To be properly pedantic, you should have this:
scanf("%d", &array[0]);
.. because that's a pointer to an integer, array[0], and what you really want.
In fact, you can pass anything to scanf (if you ignore the documentation), and the compiler is expected to accept it (misuse of the standard library does not trigger an error, traditionally). If you pass something invalid then your program is expected to fail at runtime (or not, if you're lucky -- undefined behaviour is like that).
However, your compiler is trying to be helpful and interpret the scanf format string for you to make sure you did the right thing, but if you look carefully, it's not an error, its a warning. You're free to ignore it, if you choose.
The reason you don't get an error with fread is because that function does not take an integer, it takes void *, and both forms can be converted to that type just as easily.

Data type warning when initializing pointers

I'm currently trying to wrap my head around pointers in C, coming from front-end developing this not an entirely easy endeavour. I'm following this tutorial, and everything is running smoothly until I try to compile the following example:
#include <stdio.h>
int main(int argc, char *argv[]) {
int **ramon;
int *paul;
int melissa = 5;
paul = &melissa;
ramon = &paul;
printf("ramon = %d\n", ramon); // <- warning: format '%d' expects type 'int'...
printf("&paul = %d\n", &paul); // <- warning: format '%d' expects type 'int'...
printf("*ramon = %d\n", *ramon); // <- warning: format '%d' expects type 'int'...
printf("&melissa = %d\n", &melissa); <- warning: format '%d' expects type 'int'...
printf("**ramon = %d\n", **ramon);p1); <- warning: format '%d' expects type 'int'...
return(0);
}
From the first printf line I get this error: "warning: format '%d' expects type 'int', but argument 2 has type 'int **'"
I reckon this has to do with the way I initialize my pointers, but standing at the bottom of a steep learning curve I don't know how to progress. What is wrong, how do I initialize pointers to avoid the warnings?
Your pointer initialization looks correct. The problem is that you are trying to print a variable of type int** (i.e. a "pointer to a pointer to an int") using the %d format specifier which is for int values.
Print int value
If you want to print the value of melissa (i.e. 5) which is essentially what ramon is indirectly pointing to, you need to de-reference the pointer value the correct number of times.
De-referencing pointers is done via the * operator, and it essentially means "the value at which this pointer is pointing to".
De-referencing it once (i.e. *ramon) will get the int* value which ramon is pointing to.
De-referencing it a second time will get the int value which the de-referenced int* value is pointing to.
You can chain together de-reference operators like so to get what you want:
printf("ramon = %d\n", **ramon);
Print pointer value
If you want to print a pointer (i.e. memory location) value, you can use the %p format specifier. This will print the memory address out in hex:
printf("ramon = %p\n", ramon); // Print the "pointer to the pointer to melissa"
printf("ramon = %p\n", *ramon); // Print the "pointer to melissa"
ramon isn't an int, it's a pointer to a pointer to an int. **ramon would be an int. You're trying to print a bunch of ints with %d, but you're mostly trying to print pointers which uses the format %p.
read man printf. It is not clear whether you want to print the value pointed to by ramon or the actual value of ramon which is an address of an pointer to an int. Same for other warnings as well.
The type of the expression ramon is an int **, and the %d specifier expects an int. *ramon will be of type int *, and **ramon will be the desired int.
If you want to print the pointer itself, use the %p specifier.
ramon and paul are pointers and the printf format specifier for printing values of pointers themselves (not what they're pointing to) is %p. Try
printf("ramon = %p\n", ramon);
The warning has nothing to do with how you are initializing your pointers, it has to do with the mismatch in types between printf's %d specifier and the types you are passing to it.
If you want to print a pointer, use %p.
You declared ramon of type "pointer to pointer to type int". i.e. it holds an address to a "pointer to type int". %d on the other hand is the format specifier for int which is why you are getting the errors. Instead try the following:
printf("&paul = %p\n", ramon); //print the address that ramon points to
printf("&melissa = %p\n", *ramon); //print the address that paul points to
printf("&melissa = %p\n", paul); //same as above
printf("melissa = %d\n", **ramon); //print the contents of melissa
printf("melissa = %d\n", *paul); //same as above
printf("melissa = %d\n", melissa); //same as above
Output
&paul = 0xbf8072f4
&melissa = 0xbf8072f0
&melissa = 0xbf8072f0
melissa = 5
melissa = 5
melissa = 5
I reckon this has to do with the way I initialize my pointers
Why would you reckon that when the error message says nothing about initialization? Read it again:
"warning: format '%d' expects type 'int', but argument 2 has type 'int **'"
You have %d in the format string for your printf() call, as a placeholder for the value that you want to print. %d is used as a placeholder for values that are int s. The value you want to print is ramon. That variable has the type int**. The type int and the type int** are not the same.
If you want to print a pointer, then you should write the format string so as to expect a pointer. We format pointers with %p.
but standing at the bottom of a steep learning curve I don't know how to progress.
The way to progress is to read the error and/or warning messages and attempt to understand them. The error messages are talking about types. You should not be trying to do anything with pointers if you haven't first got your head around the concept that values have a type (i.e., what kind of thing they are).

Resources