I have found myself unable to explain why the following piece of code works. Needless to say, I am quite new to C++...
#include <cstdio>
void foo(char* p)
{
p[0] = 'y';
}
int main()
{
char a[1];
a[0] = 'x';
printf("a[0] = %c\n", a[0]);
foo(a);
printf("a[0] = %c\n", a[0]);
return 0;
}
This program outputs
a[0] = x
a[0] = y
What intrigues me us that I am not passing a pointer, but an array, to foo. So how can foo change the value of the array a? Does this apply only to arrays of char?
The answer to Difference between char and char[1], confirms my observation, but it does not go into detail about why this is the case.
Thanks!
When you're passing an array to a function it decays to a pointer to the first element.
The following are completely equivalent:
void foo(char* p);
void foo(char p[]);
void foo(char p[42]); /* Or any other number. */
Does this apply only to arrays of char?
It applies to any array. I recommend the aryptr section of the C FAQ.
In C, arrays, in most contexts (*), decay into a pointer to their first element.
In your case, the array a decays into a pointer to a[0].
The array int arr[12][23], when used just by its identifier, decays to a pointer to its first element, namely arr[0], which is of type int (*)[23] (pointer to array of 23 ints).
(*) Arrays do not decay when used as the argument to the sizeof operator, or when used as argument to the & operator or when used as initializer (a string literal) for a character array.
When You say
char a[5];
the compiler allocates 5 sequential blocks of memory, and the address of the first block is assigned to a. So by definition name of the array (a in our case) is just a pointer to a memory location.
Now when we say a[0], compiler manipulates it as *(a+0*sizeof(array datatype i.e. char, int etc.)), symbolically a[i] is represented as *(a+i*sizeof(array datatype i.e. char, int etc.))
As far as function parameter passing is concerned When we pass an array to a function basically it is just the address of the first element which is passed. For more details regarding this plz follow this link or read the #cnicutar's answer (as he has already posted a correct answer there is no point repeating that again)...
You can try this to convince yourself how this works.
int a[8], *p;
p = a;
printf("%p, %p, %p\n", a, &a[0], p);
An array is nothing more than a pointer (to the first element), at least for argument passing sake.
The three prototypes here are equivalent:
void foo(char *p);
void foo(char p[]);
void foo(char p[42]);
C says a declaration of parameter of type array of T is adjusted to a declaration of type pointer to T.
Array name points to the address of the first element of the array. Please refer: Is an array name a pointer?
Related
This question already has answers here:
Array changed in a void function, is still changed outside! why? (scope)
(4 answers)
Passing an array as a parameter in C
(3 answers)
Passing Arrays to Function in C++
(5 answers)
Passing an array as an argument to a function in C
(11 answers)
Closed 11 months ago.
I don't get it why you can alter the values inside the array, without using a reference or a pointer (&, *), I'm a freshmen student, and I don't know the reason behind, I hope someone can provide a logical answer, please refer to the code below, Thank You in Advance.
#include <iostream>
using namespace std;
void a(int x[]){
for(int i = 0; i < 5; i++){
x[i] += 2;
}
}
int main(){
int x[5] = {1,2,3,4,5};
a(x);
for(auto b : x){
cout << b << " ";
}
return 0;
}
A function parameter is never an array in C++. When you declare a function parameter as an array, it is automatically adjusted to be a pointer to element of such array. These declarations are effectively identical:
void a(int x[]); // looks like an array of int of unknown bound
void a(int* x); // declaration above is adjusted to this
void a(int x[1234]); // the size is ignored completely
An array implicitly converts to a pointer to the first element of the array (such conversion is called decay). Hence, you can call the function that doesn't accept an array parameter by passing an array argument:
int* ptr1 = x; // implicit conversion
int* ptr2 = &x[0]; // same conversion explicitly
a(x); // same conversion happens here
These two rules (function parameter adjustment and array to pointer decay) make it so that what syntactically looks like passing arrays by value, is actually done using indirection. Within the function, you modify the elements by indirecting through the pointer parameter that points to the array that exists outside of the function.
Important note: The adjustment of array to pointer in function parameter does not apply in other contexts. Arrays and pointers are distinct types with different properties.
Another note: The adjustment does not apply to parts of compound types. For example, a function parameter of type "pointer to array" will not be adjusted to be "pointer to pointer" and "reference to array" will not be adjusted to be "reference to pointer".
The parameter having the array type in this function declaration
void a(int x[]){
is adjusted by the compiler to pointer type to array elements type. That is the above declaration is equivalent to
void a(int *x){
In this call of the function
a(x);
the array designator is implicitly converted to pointer to its first element. That is the call is equivalent to
a( &x[0]);
So within the function you have a pointer to the first element of the array declared in main.
Using the pointer arithmetic you can access elements of the array. That is the elements of the array are passed to the function by reference in the C meaning indirectly through a pointer to them.
Within the function the variable x has the type int *. And this expression statement
x[i] += 2;
is equivalent to
*( x + i ) += 2;
Beacuse
void a(int x[]){
is the same as
void a(int *x){
and so you are using a pointer
Why?
Because an array like
int x[10];
'decays' to a pointer when passed to a function (and in other places). This can be very confusing but at the same time is very flexible
It mens that I can have a function like strlen that can accpet a 'real' array, or a pointer. These 'strings'
char *s1 = malloc(10);
strcpy(s1, "hello");
char s2[] = "hello";
char *s3 = "hello";
store their data in different ways but all can be handled by
size_t strlen(const char *s);
note that this is exactly the same as
size_t strlen(const char s[]);
they are 2 different ways of writing the same thing. Personal preference is for the second type if its really is an 'array' vs a pointer to maybe an array.
One issue with this 'decay' is that inside strlen (or any pointer/array accepting function) it is impossible to 'know' the length just from the parameter. The compiler knows that the size of s2 is 6 but this information is not carried forward to the function.
Regularly SO sees this
void my_func(int *arr){
int len = sizeof(arr)/sizeof(arr[0]);
....
}
int a[10];
my_func(a);
This will give len = 1 or 2 (depending on 32 or 64 bit machine), never 10
The flexibility costs a litle power
Could somebody explain the difference between these two function declarations below please? As far I know, aDecay() takes a pointer as an argument and if you have an integer int a[5] you can call aDecay(a) or aDecay(&a[0] because an array decays to a pointer.
Now if I want to call pDecay() I have to use pDecay(&a).
How does pDecay force you to use the &.
void aDecay(int *p)
void pDecay(int (*p)[7])
With plain a (or its equal &a[0]) you have a pointer to a single element in the array. A single element is of type int, so a pointer to it must be int*.
If you have a pointer to the actual array, like you get with &a, you no longer have a pointer to a single int element but to the whole array. The type of the array is int[5], and a pointer to it is int(*)[5].
Also note that in neither case you pass the array by value. You pass a pointer by value.
This is not a pointer to function syntax, it's a pointer to array syntax.
In the first example p is a pointer to an array of integers. p[i] is a single int. It cannot be indexed any further.
In the second example p is a pointer to an array of seven integer arrays. p[i] is an array which can be indexed further, i.e. p[i][0] is valid.
Here is an example of using the second approach:
void fill_data(int (*p)[7], size_t n) {
for (size_t i = 0 ; i != n ; i++) {
for (size_t j = 0 ; j != 7 ; j++) {
p[i][j] = (int)(7*i+j);
}
}
}
int main() {
int data[10][7];
fill_data(data, 10);
}
Demo
aDecay takes a pointer to an int.
An array of int can decay to a pointer to the array's first element.
pDecay takes a pointer to an array of seven ints.
An array does not implicitly convert into a pointer to itself.
Passing p will convert the array p to a pointer instead of keeping it as an array (this is called array decay, because the array decays to a pointer).
Passing &p will not convert p to a pointer, because it will convert the pointer to p to a generic pointer, which is not a big deal because it is actually a pointer.
Why we don't want to convert p to a pointer is because arrays are not just pointers. If you think that arrays are just pointers, try to compare sizeof(myArray) with sizeof(myPointer). An array embeds also its size, not only the pointer to the first element.
First of all, you have a misconception in the question: if a is an array, a is always a pointer to that array, i.e. you do pDecay(a) - NOT pDecay(&a). Now, [ ] is a dereferencing operation, so if you say a[5] - you dereference a pointer to a + 5 * bytes in memory occupied by the array's unit. Accordingly, a and &a[0] is exactly the same. a[5] and &(a + 5) is the same.
Answering your question, aDecay(int *p) takes a pointer to integer - nothing asks for an array here. int (*p)[7] is an array of 7 integers and compiler will check that's the case.
How does pDecay force you to use the &.
Because an array doesn't decay to just any pointer. It specifically decays to pointer to first element. The type of pointer to first element of an array of int (i.e. int[n]) is: pointer to int (i.e. int*).
The argument of pDecay is int (*p)[7] which is a pointer to an array of 7 int. An int* is not implicitly convertible to (int (*p)[7]; they are separate, incompatible types. Note that pDecay(&a) won't work either because the type of &a is (int (*p)[5] which is also a different type than (int (*p)[7].
pDecay doesn't force you to use & in general. You could pass an array of arrays, which would then decay to a pointer to first array, which is what pDecay accepts:
int arr[n][7];
pDecay(arr);
Could somebody explain the difference between these two function declarations below please?
int* is a pointer to an int object. int (*p)[7] is a pointer to an int[7] object i.e. an array of 7 int.
I am learning c pointer and I follow c4learn as tutorial. In pointer to array of string section, which has following code:
char *arr[4] = {"C","C++","Java","VBA"};
char *(*ptr)[4] = &arr;
I didn't get what is
*(*ptr)[4]
? Wasn't it possible to use it like
**ptr
instead?
Update1:
Currently I am in the next section, function pointer and I saw again similar code:
void *(*ptr)();
char *(*ptr)[4]
is a pointer to a length 4 array of pointers to char (char*). Since arr is a length 4 array of char*, ptr can be made to point to arr by assigning it the address of arr, &arr.
void *(*ptr)();
Is a pointer to a parameterless function returning void*. For example
void* fun(); // function
void *(*ptr)(); // function pointer
p = fun; // function pointer points to function
C syntax can be quite confusing, so it may be easier to illustrate this with some examples. Note that whitespaces between T and ; make no difference.
T name[N]; // size N array of T
T * name[N]; // size N array of pointer to T
T (*name)[N]; // pointer to size N array of T
T ** name[N]; // size N array of pointer to pointer to T
T *(*name)[N]; // pointer to size N array of pointer to T
char *ar[4];
Declares ar as an array of four pointers to chars.
To declare a pointer, you take the declaration of something it can point to, and replace the variable name with (*some_other_name).
So char *(*ptr)[4]; declares ptr as a pointer to an array of four pointers to chars. Normally you can drop the brackets, but in this case, char **ptr[4]; would declare ptr as an array of four pointers to pointers to chars which is not what we want.
Similarly for a function pointer. void *fn() declares a function. void *(*ptr)() declares a pointer that could point to fn. void **ptr() would declare a function with a different return type.
Wasn't it possible to use it like **ptr instead?
Yes, assuming you mean like ptr2 below:
const char* arr[4] = {"C","C++","Java","VBA"};
const char* (*ptr)[4] = &arr;
const char** ptr2 = arr;
There is a difference though... with ptr the type still encodes the array length, so you can pass ptr but not ptr2 to functions like the one below:
template <size_t N>
void f(const char* (&arr)[N]) { ...can use N in implementation... }
Currently I am in the next section, function pointer and I saw again similar code: void *(*ptr)();
That creates a pointer to a function - taking no arguments - returning a void* (i.e. the address of an unspecified type of data, or nullptr).
char *(*ptr)[4] is an array pointer to an array of pointers.
With less obfuscated syntax: if you have a plain array int arr[4]; then you can have an array pointer to such an array by declaring int (*arr_ptr)[4].
So there are arrays, regular pointers and array pointers. Things get confusing because when you use the array name by itself, arr, it decays into a regular pointer to the first element. Similarly, if you have a regular pointer and let it point at the array, ptr = arr; it actually just points at the first element of the array.
Array pointers on the other hand, points at the "whole array". If you take sizeof(*arr_ptr) from the example above, you would get 4*sizeof(int), 4*4=16 bytes on a 32-bit machine.
It should be noted that an array pointer a mildly useful thing to have. If you are a beginner, you don't really need to waste your time trying to understand what this is. Array pointers are mainly there for language consistency reasons. The only real practical use for array pointers is pointer arithmetic on arrays-of-arrays, and dynamic allocation of multi-dimensional arrays.
Can I pass an array to printf directly:
char text[1024] = "text";
printf("%s", text);
Or should I explicitly cast it to a char pointer:
char text[1024] = "text";
printf("%s", (char*) text);
I'm asking because I thought maybe it copies the array elements directly into the va_list instead of putting just a pointer to the first element.
Yes, you can pass an array directly. Exactly, name of array represents address of the array which makes no difference with char *.
(char*)text and text hardly make any difference in this example! Base address of an array decays into a pointer when passed as a function argument.
In fact, even if text was not a char array, still it would hardly make any difference to printf because it is a variable argument function
int printf(const char *format, ...);
and all it cares is about the first argument. Logic might go wrong but printf doesn't care!
Short answer, yes, arrays in C _are_a pointer to their first element. (Edit: Longer answer, well, actually, arrays in C are identifiers or labels which are just names, but when used as part of an expression that doesn't preface them with & or sizeof they get converted into or otherwise evaluated as pointers).
Even longer answer: You see, functions can't really receive an array as an argument, but the compiler has to send them something, so it sends them a pointer. Thus the first sentence should probably start with "As far as functions like printf() are concerned..."
Some functions purport to take an array, but that's just an illusion. These two functions are the same.
func(char *a);
func(char a[]);
The C FAQ entry below goes into more detail about the subtle differences between arrays and pointers:
char a[] = "this";
char *b = "that";
a[0] generates completely different code than b[0] yet they are roughly equivalent in that the subscripting operator [] gives us the expected result.
Interestingly, the format specifier "%s\n" is also passed to printf() as a pointer.
http://www.lysator.liu.se/c/c-faq/c-2.html
Isn't an array/arrayname always a pointer to the first element in C?
Except when it is the operand of the sizeof or unary & operator, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be replaced by an expression of type "pointer to T" whose value is the address of the first element of the array.
When you write
printf("%s", text);
the expression text is replaced by a new expression of type char * whose value is &text[0], and this pointer value is what gets passed to printf. This means you don't have to cast the argument. It also means that you cannot pass an array expression as a function parameter and have the called function receive it as an array type. The conversion happens before the function is called.
names of pointers and arrays are exchangeable. (Besides the memory allocation)
When you have
char *a = "abcdef";
char b[5];
You could write
char z = a[2]; // 'c'
char x = *b;
char y = *(b+2);
But you can't do
b ++;
You may consider array b as char * const b as a unmodifyable pointer
I tried some code to check the behavior of array and pointers. Its as follows.
#include <stdio.h>
main(){
int s[]={1,2};
int *b=s;
printf("%d, %d, %d\n", s, &s, *s);
printf("%d, %d, %d\n", b ,&b, *b);
}
Initially I thought of pointers and array to be same BUT...
To my surprise the value of 's' and '&s' are SAME unlike 'b'. Does that mean an Array variable "points to itself?"
I am also now confused with what actually is a variable "name"? How its binding takes place with a location in memory? I am just unable to visualize things that go on there!
And where in memory (RAM) is all the information stored at run time?
OK, let say the following is how the memory looks when you execute
int s[]={1,2};
int *b=s;
s[]
+-----+-----+
| 1 | 2 |
+-----+-----+
100 104
^
|
| int *b = &s
+-----+
| 100 |
+-----+
200
s is an array. What that means is, it is a contiguous memory location which is associated with a variable s and each element is accessed by offsetting the array variable name.
So when you use s it actually boils down to the address of the array (which is 100 in this case). And when you do *s, it boils down to *(s+0) which is equivalent of s[0] and so *s represents the contents stored in the zeroth location (in this case s[0] is 1). When do do an &s, this will print the address of the s (which is100` in this case).
Note that, here s and &s represents an address; *s and s[x] represents an integer.
The same applies to the pointer. So, b prints the content it has, which is the address of s (which is 100 in this case). &b prints the address of b, which is 200 in this case. And, *b prints the content of the first element of the array s which is 1.
I have modified you program to make it print the address.
#include <stdio.h>
int main(void)
{
int s[]={1,2};
int *b=s;
printf("%p, %p, %d\n", (void *)s, (void *)&s, *s);
printf("%p, %p, %d\n", (void *)b, (void *)&b, *b);
return 0;
}
Output:
0xbfc4f3e4, 0xbfc4f3e4, 1
0xbfc4f3e4, 0xbfc4f3ec, 1
EDIT: %p expects void *. Added the same!
An array, when used as an argument to a function, decays into a pointer to its first element. Similarly, taking the address of an array results in a pointer to the location of the first element (but with a different type).
As for your second question, a variable name only exists at compile-time. It typically has no representation in memory at runtime.
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be replaced with an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.
Assume the following code:
int arr[10];
foo(arr);
In the call to the function foo, the expression arr is converted from type "10-element array of int" to "pointer to int", and the address of the first element of arr is what actually gets passed to foo.
We would define foo as either
void foo(int a[]) {}
or
void foo(int *a) {}
In the context of a function parameter declaration, T a[] and T a[N] are identical to T *a; the parameter is a pointer type, not an array type. Note that this is only true for function parameter declarations.
As mentioned above, one exception to this rule is when the array expression is the operand of the unary & operator. If we change the call to foo to read
foo(&arr);
then the type of the expression &arr is "pointer to 10-element array of int", or int (*)[10], and the value of the expression is the address of a. For this, the definition of foo would be
void foo(int (*a)[10]) {}
In C, the address of the array and the address of the first element of the array are the same - thus both of the expressions arr and &arr have the same value, but their types are different. This matters for operations involving pointer arithmetic. For example, assume our code had been written
int arr[10];
foo(arr);
...
void foo(int *a)
{
...
a++;
...
}
On entry, a points to arr[0]. The expression a++ would advance the pointer to point to the next integer in the array (arr[1]).
Now assume the code had been written as
int arr[10];
foo(&arr);
...
void foo(int (*a)[10])
{
...
a++;
...
}
On entry, a still points to arr[0] (remember, the address of the array is the same as the address of the first element of the array), but this time the expression a++ will advance the pointer to point to the next 10-element array of integers; instead of advancing the pointer sizeof (int) bytes, we advance it sizeof (int[10]) bytes.
So this is why in your printf statement you see the same values for both s and &s. You should use the %p conversion specifier to print pointer values, and it expects the corresponding argument to be type void *, so change those printf statements to
printf("%p %p %d\n", (void *) s, (void *) &s, *s);
printf("%p %p %d\n", (void *) b, (void *) &b, *b);
A simple way to think about this is that an array as a pointer can't be changed by assignment, it is effectively a constant pointer to a known amount of memory.
To try that, use:
myptr = myarray;
Which is perfectly ok, and then try:
myarray = myptr;
Which is not.