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

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.

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.

Difference between char array and pointer to char array

While reviewing my code I realized I had placed an extra & while passing a char array to strcpy and missed the resulting warning; regardless, everything worked as expected. I then reproduced the behavior in this example:
#include <string.h>
#include <stdio.h>
void main() {
char test1[32] = {0};
char test2[32] = {0};
strcpy(test1, "Test 1\n");
strcpy(&test2, "Test 2\n");
printf(test1);
printf(test2);
printf("%i %i\n", test2, &test2);
}
Here I copy a string to the address of test2 and the compiler complains accordingly:
main.c: In function ‘main’:
main.c:9:12: warning: passing argument 1 of ‘strcpy’ from incompatible pointer type [-Wincompatible-pointer-types]
9 | strcpy(&test2, "Test 2\n");
| ^~~~~~
| |
| char (*)[32]
In file included from main.c:1:
/usr/include/string.h:125:39: note: expected ‘char * restrict’ but argument is of type ‘char (*)[32]’
125 | extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
| ~~~~~~~~~~~~~~~~~^~~~~~
However the code is still compiled and the result seems to ignore the second level of indirection. Even when printing the address &test2 it is the same as simply test2.
./a.out
Test 1
Test 2
-1990876288 -1990876288
I must admit this is a part of the C language that completely escapes me. Why is the & operand seemingly ignored when targeting an array?
The first byte of the first element in the array is at the same place as the first byte of the array, because they are the same byte.
Most C implementations use the memory address, or some representation of it, of the first byte of an object as a pointer to the object. The array contains its elements, and there is no padding: The first element of the array starts where the array starts. So the first byte in the first element is the first byte in the array. So they have the same memory address.
There is a rule in C that converting a pointer to an object to a char * produces a pointer to the first byte of an object (C 2018 6.3.2.3 7). So, given an array a, (char *) &a[0] is a pointer to the first byte of the first element, and (char *) &a is a pointer to the first byte of the array. These are the same byte, so (char *) &a[0] == (char *) &a.
However, &a[0] and &a have different types. If you attempt to compare them directly with &a[0] == &a, the compiler should issue a warning that the types do not match.
If you pass &a as an argument to a routine that expects &a[0], it will often work in most modern C implementations because they use plain memory addresses as pointers, so &a is represented with the same bits (a memory address) as &a[0], so the receiving routine gets the value it expected even though you passed a pointer of the wrong type. However, the behavior of your program will not be defined by the C standard, since you have violated the rules. This was more of a problem in older C implementations when memory models were not simple flat address spaces, and different types of pointers may have had different representations.
Your code works in this case because, once the pointer argument (with or without the extra &) gets to the strcpy function, it is interpreted as a simple char* value. Thus, any pointer arithmetic (such as the likely increments) performed in that function will be correct.
However, there are cases where using a pointer-to-char and pointer-to-array-of-char will yield significantly different results. Pointer arithmetic is such a case: if p is a char* variable, then ++p will add the size of a char (i.e. 1) to the address stored in p; however, if q is an array of char* pointers, then ++q will add the size of a pointer to the address in q. And, if r is the address of an array of character strings, then ++r will add the size of the entire array to the address stored in r.
So, it's good that the compiler warns you about that extra &. Be very careful about addressing (no pun intended) such issues, if ever your compiler warns you about them.
Why is the & operand seemingly ignored when targeting an array?
The conversion of char (*)[32] to char * is UB.
Is is not ignored by the compiler, hence the warning.
The compiler emitted code did convert the pointer from one type to the other in a common fashion resulting in acceptable behavior. Still remains UB.
Best if the programmer does not ignore the warning.

Can we pass pointers in printf?

In the following code, we pass pointer to a string and it works fine but it doesn't works for pointer to an integer. I want to know why ?
#include <stdio.h>
int main(){
char *astring = "afdv";
printf("%s",astring);
int a;
a = 1000;
int *ptr = &a;
printf("\n%d", ptr);
}
In the first printf you did NOT send a pointer. You de-referenced the pointer and hence, you are actually sending a character. Likewise if you want to print an integer, send *ptr, the dereferenced value of the integer pointer.
I'm guessing the source of your confusion to be this-
printf("%s", string_ptr);
perfectly prints the string value instead of the pointer value but
printf("%d", integer_ptr);
prints the pointer value instead of the integer value.
This is because the way printf is implemented. When it sees a %s in the format string, it considers the corresponding parameter as a pointer to a NULL-terminated string. And goes looking for the value of that string at the address contained in the pointer.
But when it sees a %d, it considers the corresponding parameter as an integer value and prints it out directly.
The change in behavior is because types like integer, floats etc are smaller and finite in size and can be passed to printf as values directly. But a string can be arbitrarily large in size. So it makes sense to pass it as a pointer and let printf go find the actual value using the pointer.
Because the language specification says so.
The %s conversion specifier expects its corresponding argument to have type char *; printf will then print out the sequence of characters starting at that address until it sees the string terminator.
The %d conversion specifier expects its corresponding argument to have type int; printf will then print out the text representation of that value as a signed decimal integer.
Check your handy C reference manual for the complete list of conversion specifiers and the types of arguments they expect. If you don't have a handy C reference manual, my preferred one has been C: A Reference Manual since the late 1980s, although the current edition only covers up to C99.
If you use %d and pass something that isn't an integer, then the behavior is undefined. The compiler isn't required to yell at you for passing an argument of the wrong type. If you run the code, anything may happen. In practice, you'll usually get garbled output. If you pass something that isn't a pointer for %s, you may get a runtime error.

Can using an array as the last named argument to a variadic function result in a buffer underrun?

I found this paragraph in the man page for stdarg.h:
Because the address of this parameter is used in the va_start() macro, it should not be declared as a register variable, or as a function or an array type.
So, register variable I understand, since a register can't be addressed with a pointer. Function I understand, since you would get the return value, which would use immediate addressing rather than address register indirect addressing.
I'm curious about what would happen if you used an array as the parameter. Say you use an array of three int types. Would this result in the first element of the array being used as the last named parameter, while the next two elements would end up being used as the values for the variable arguments? This would be a buffer underrun.
I'm also wondering if this would result in a security vulnerability, e.g. someone could input elements of the array and have the function do something it wasn't supposed to do because it thinks the extra array elements are variable parameters.
Also, what about the printf family of functions? Those use character arrays as their last named arguments. How do they not run into problems?
This looks like an error in the man page.
A function cannot have a parameter of array type or function type, since any array declaration as a function parameter is converted to a pointer, as is a parameter of function type.
Section 6.7.6.3 of the C standard detailing Function Declarators states the following:
7 A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation. If
the keyword static also appears within the [ and ] of the array type
derivation, then for each call to the function, the value of the
corresponding actual argument shall provide access to the first
element of an array with at least as many elements as specified by the
size expression.
8 A declaration of a parameter as "function returning type" shall be adjusted to "pointer to function returning type", as in 6.3.2.1
So what this means as far as va_start is that the last named parameter can't use the register specifier. Arrays and functions don't matter because they are adjusted to pointers.
You are imagining call by value, which does not happen.
Arrays and functions are passed as addresses.
I suspect that in some compilers, sizeof() returns (or once returned) the size of an array, and not the size of a pointer to the first element. I'm not sure about functions.
This code:
#include <stdio.h>
void test(void (*f)(), int a[3]) {
printf("sizeof(f): %lu\n", sizeof(f));
printf("sizeof(a): %lu\n", sizeof(a));
printf(" f: %p\n", f);
printf("&f: %p\n", &f);
printf(" a: %p\n", a);
printf("&a: %p\n", &a);
}
void foo() {}
int main() {
int ints[3] = { 1, 2, 3 };
test(foo, ints);
}
Gives me this warning when I compile it with gcc:
address.c: In function ‘test’:
address.c:6:38: warning: ‘sizeof’ on array function parameter ‘a’ will return size of ‘int *’ [-Wsizeof-array-argument]
printf("sizeof(a): %lu\n", sizeof(a));
^
address.c:4:28: note: declared here
void test(void (*f)(), int a[3]) {
^
This would seem to imply that the compiler authors suspect programmers may be confused about what this should be, so there may be (or have once been) disagreement among compiler writers too.
If sizeof() were to return the size of the array, and not the size of the pointer passed on the stack, that could cause va_start/va_arg to skip over the wrong number of bytes when looking for the next argument.

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

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

Resources