I am trying to figure out array and pointer relations in C and wrote a program as below:
#include <stdio.h>
void printd(char *v[]);
int main()
{
char *lineptr[] = {"hello", "c", "world"};
printd(lineptr);
return 0;
}
void printd(char *lineptr[])
{
printf("%s\n", *lineptr++);
printf("%s\n", lineptr[0]);
}
After run this program, I got "hello" printed first and then "c". By the defintion in main(), lineptr is an array of pointers.
After passing this array name to the function, I take this array name as a pointer and operate this pointer with *lineptr++ to dereference it in the first printf. In the second printf, I use the lineptr[0], I got the string "c".
Intuitively, I think this should be an array and lineptr[0] will give us the first element (pointer) in this array. But the result is the second pointer. It seems my understanding is wrong somehow.
Based on the result, as a pointer, lineptr was increased by lineptr++ in the first printf, it will move to the second element. In the second printf, lineptr[0] is actually equal to *(lineptr + 0). So it will print out "c" but not "hello". Am I understand it correctly this way? Why can we not take lineptr as an array anymore as the definition?
You write:
... by the defintion in main(), lineptr is an array of pointer, after passed this array name to function, I take this array name as a pointer and <......> as a pointer, lineptr was increased by lineptr++ in the first printf, it will move to the second element, and in the second printf, lineptr[0] is actually equal to *(lineptr + 0), so it will print out "c" but not "hello", is that right if I understand it in this way?
Yes. We can always discuss the exact wording but... Yes, it seems you got it pretty correct.
Arrays in C have a kind of special feature. In most cases when you use the identifier for an array (i.e. name of array), it will be converted to a pointer to the first element of the array. An example where this doesn't apply is sizeof but the rule applies in most cases. It happens implicit, you can't see it from the code, you just need to know that that's how arrays are handled in C.
For instance:
int arr[7];
arr[1] = 5;
can be seen as:
int arr[7];
int* p_arr = &arr[0]; // Create pointer to first element of array
*(p_arr + 1) = 5; // access array element using that pointer
// BTW: it is the same as p_arr[1] = 5
The end result is the same. But for the first code block you don't "see" that "pointer to first element of array", it has no name, it all happens behind the scene.
When you pass an array to a function that "pointer to first element of array" becomes visible because it's simply the name of a function parameter.
Example:
void foo(int* p) // Same as void foo(int p[])
{
// Now p is a pointer to the first element of
// the array used as argument when calling foo
....
}
int arr[7];
foo(arr);
When entering foo the pointer p will get the value equal to a pointer to the first element of the array. Again we can make that more "visible" by rewriting the code to:
void foo(int* p)
{
....
}
int arr[7];
int* p_arr = &arr[0];
foo(p_arr);
Now the code explicit show that we really pass a pointer to the first element of the array to the function. The prior code example do the same, it's just done implicit. You can't see it as it is done behind the scene.
Further, you ask:
why we cannot take lineptr as an array anymore as the definition?
The answer is the same... due to the implicit conversion of array name/identifier to a pointer to first element. It's just how the inventors of C decided it to be...
I can't tell you for sure why they decided to do so but a guess could be performance. If passing an array to a function would mean "pass every array element" there would be a lot of data copying going on and that would hurt performance.
One could argue that the C syntax could/should allow both ways but in my experience it's seldom (very seldom) that you really need to pass an array by passing the value of all elements so I don't see a need for such language feature.
BTW: Should you for some reason really need to pass a whole array (i.e. all elements), you can put the array into a struct and then pass the struct. But be careful... large arrays will hurt performance and - even worse - may lead to stack overflows
Array indexing is relative. When you use it on a pointer pointing somewhere in the array, the index is calculated from the index where that pointer is pointing at - which may not necessarily be the first item in the array.
In case of *lineptr++, operator precedence applies ++ before *. Meaning that you change the local variable lineptr to point 1 item ahead in the array. But as the postfix ++ works, that change only happens after the end of the expression, meaning that *lineptr gives the item before ++ gets applied.
It's also important to know the rule of "array decay" - whenever we use an array inside an expression or pass it to a function as parameter, it "decays" into a pointer to its first element. The first element in thisarray of pointers is a char*. So the char *lineptr[] parameter decays into a pointer to a char*, making it equivalent to char** when used in this function.
Just as an example, an alternative way of implementing this would be to use a loop. We could add a placeholder to mark the end of the array in the form of a null pointer: {"hello", "c", "world", NULL};. This is sometimes called a sentinel, as it guards from accessing out of bounds.
Full example:
#include <stdio.h>
// use const correctness since we shouldn't modify the contents
// also always name the prototype parameter the same as in the function definition
void printd (const char *lineptr[]);
int main (void)
{
const char *lineptr[] = {"hello", "c", "world", NULL}; // sentinel value at the end
printd(lineptr);
/* Note that the original lineptr in main() remains intact here, the function
only applied ++ to a local copy. */
}
void printd (const char *lineptr[])
{
while(*lineptr != NULL)
{
printf("%s\n", lineptr[0]); // this is the same as *lineptr
lineptr++; // change where the local variable lineptr points at
}
}
The expression *lineptr++ returns the first pointer (to "hello"), then advance to the 2nd pointer ("c). Perhaps it is easier to see this refactoring:
void printd(char *lineptr[]) {
printf("%s\n", *lineptr);
lineptr++;
printf("%s\n", *lineptr);
}
I would write it like this:
void printd(char *lineptr[]) {
printf("%s\n", lineptr[0]);
printf("%s\n", lineptr[1]);
}
Output is hello then c
because of *lineptr++ and lineptr[0] are not same then the control move in second element of array
Related
//reference from Herbert Schildt//
This is the first code
void change(char *);
int main(void)
{
char target[80]="hello";
change(target);
printf("%s",target);//printing aaaaa
return 0;
}
void change(char *tar)
{
int i;
for(i=0;i<strlen(tar);i++)
{
tar[i]='a';
}
}
this is the second code
void change(char *);
int main(void)
{
char target[80]="hello";
change(target);
printf("%s",target);/printing hello
return 0;
}
void change(char *tar)
{
int i;
tar="aaaaa";
}
despite of not passing address of
target
why the string in
target
is getting altered in first code but not in second
why first code is printing
aaaaa
and second code print
hello
The first piece of code changes the contents of the characters pointed to by tar. The second piece changes the local variable tar Itself to point to a different memory location, not the contents of the original memory pointed to. Thus, the original content in main() is preserved in the second case.
Quick answer: In both cases, what's being passed to the change function is a pointer value (not a pointer object). That pointer value is the address of the first element of the target array. In the first example, the change function modifies the array that that pointer points to. In the second example, the change function modifies the pointer. Specifically, it modifies the function's local copy of the pointer, which has no effect on the caller.
//reference from Herbert Schildt//
There's your first problem. Herbert Schildt has written several books on C, but he really doesn't know the language very well. Read some reviews of his books written by actual C experts: The Annotated Annotated C Standard, written by Clive D.W. Feather, and C: The Complete Nonsense, written by Peter "Seebs" Seebach.
I don't know which book your examples came from. His use of int main(void), which is correct, probably indicates that it's one of his later books; in his earlier books he uses void main(void), which is blatantly incorrect (or at least gratuitously non-portable).
Find a better book.
In your first example:
void change(char *tar);
...
char target[80]="hello";
change(target);
the parameter tar is a pointer. That pointer is not changed; the change function changes what the pointer points to. target is an array object, but there's a special-case rule in C that says that an expression of array type is implicitly converted to a pointer to the array's first element in most contexts. (The exceptions are when the array is the operand of a unary & or sizeof operator, or when it's a string literal in an initializer used to initialize an array object; none of those apply here.)
So in the call change(target), the argument passed to the change function is a pointer to (equivalently, the address of) the array element target[0]; the call change(&target[0]) would be exactly equivalent. And via pointer arithmetic, the function can change not just the char object that the pointer points to, but other char objects that are elements of the same array. (The [] indexing operator is defined in terms of pointer arithmetic.)
Incidentally, this loop:
for(i=0;i<strlen(tar);i++)
/* ... */
is horribly inefficient. It recomputes strlen on each iteration of the loop, and each recomputation traverses the entire string.
The change function in the second example is quite different:
void change(char *tar)
{
int i;
tar="aaaaa";
}
The assignment copies a pointer value, not an array value. (There are no array assignment in C; that's why we have functions like strcpy and memcpy.) tar, like in the first example, is a parameter of pointer type. A parameter is a local variable that take its initial value from the corresponding argument passed by the caller. The assignment modifies that local variable (so it points to the array associated with the string literal), but it has no effect on the value passed in by the caller.
Finally, in both examples, the main program does this:
printf("%s",target);
(Incidentally, it should be "%s\n", not just '%s".) The result of evaluating the expression target does not change; it's the base address of the array, and that's fixed as long as the array exists. So why does the output change? Because the %s format specifier, unlike any other printf format specifier, prints not the value of the corresponding argument, but the value of what it points to.
Suggested reading: section 6 of the comp.lang.c FAQ, titled "Arrays and Pointers". It's the best resource I know of for explaining the often confusing relationship between arrays and pointers in C.
char *string = "something between quotes"
when you use double quotes in C and put a string inside it, C automatically creates an array for us and puts the stuff between the double quotes to that new array, and the pointer to that new array is now put in the string variable. in your case "aaaa" will make a new array somewhere in memory(probably in the data section) and then the pointer to this new array is given to tar, expressions are evaluated from right to left, so first "aaaaa" is evaluated and then the result of this is given to tar. so basically always use double quotes "sometinh here" when you initialise an array.
#Filipe Goncalves answer is the correct answer, I couldn't write this in a comment, so had to make a new post.
This question already has answers here:
Create a pointer to two-dimensional array
(10 answers)
Closed 9 years ago.
First of all I'm not confident with C, but I have a 2D array of int and I want a function to write all the values of a single line of this array.
For example:
int main(int argc, char *argv[])
{
int a[2][2];
a[0][0] = 1;
a[0][1] = 2;
a[1][0] = 3;
a[1][1] = 4;
change_array(&a[0]);
}
void change_array(int* array[])
{
(*array)[0] = -1;
(*array)[1] = -1;
}
The program crash immediately. I tried to change the change_array function to array[0] = -1 and... it works! Values are changed correctly (and I don't know why because it should be totally wrong), but if I use this function in other part of the program the values of array remain unchanged.
How it could be possible? Any suggestion to successfully change the values of my array?
Thank you very much!
You can try to do it like this:
#include <stdio.h>
void change_array(int array[2][2])
{
array[0][0] = -1;
array[0][1] = -1;
}
int main(int argc, char *argv[])
{
int a[2][2];
a[0][0] = 1;
a[0][1] = 2;
a[1][0] = 3;
a[1][1] = 4;
printf("%d %d\n%d %d\n\n", a[0][0], a[0][1], a[1][0], a[1][1]);
change_array(a);
printf("%d %d\n%d %d\n\n", a[0][0], a[0][1], a[1][0], a[1][1]);
}
It depends on your needs, but in some cases I have found that it is better to use a single-diemensional array and build a getter/setter for 2 diemensions. Such solution can be found in this answer: Correct way to allocate and free arrays of pointers to arrays
Your code passes something to change_array that is different from the parameter declared for change_array.
In the code change_array(&a[0]), a is an array of two arrays of two int. So a[0] is the first array of two int. So &a[0] is the address of the first array of two int. Compare this with the declaration of change_array. In void change_array(int* array[]), array is an array of pointers to int. So that is a different type.
Instead, you could declare change_array with void change_array(int (*array)[]). Then array is a pointer to an array of int, and your code would work (using (*array)[0] = -1).
Note: You should compile with warnings enabled, and preferably with strict or pedantic language semantics. Then the compiler should have warned you that change_array is used in main without a prior declaration. You should have a prior declaration, so that the compiler knows the full type of change_array before it is used. With that, the compiler would have seen that the wrong type was passed, and it would warn you.
Although the above would correct your code, most people would use a different solution. They would declare change_array with void change_array(int *array), and they would call it with change_array(a[0]).
In change_array(a[0]), a[0] is the first array of two int. However, there is a rule in the C language that an array expression is converted to a pointer to its first element. So a[0] automatically becomes &a[0][0]. (This conversion occurs whenever the array is not the operand of &, sizeof, or _Alignof and is not a string literal used to initialize an array.) So the call is passing a pointer to int, which matches the parameter that is a pointer to int. Then the parameter array can be used as array[i] to access array elements, so you can use the simpler syntax array[0] = -1 instead of (*array)[0] = -1.
In C, an array variable decays into a pointer to the memory address that contains the first element of the array, when it is passed as a parameter to a function. That is joined to the fact that all it's elements are placed in contigous memory. So C only needs to save the first position of the array (no matter how many dimensions it has) and will then be able to calculate, based on the indexes, what offset of the pointed memory it should access.
So, on your particular piece of code variable a points to the first element, which is a[0][0]. So, basically doing this:
change_array(&a[0]);
Is roughly (but not exactly) the same as doing:
change_array(a);
Now, knowing how arrays are passed in C, you should be able to deduce that, indeed, two dimensional arrays, when passed as parameters, actually contain on the access to their first coordinate a pointer to where the first element of the second coordinate is. So, when you do array[0] you're passing a pointer to the first element of the array under the first coordinate. and then when you do *(array[0]) you're actually accessing the first element of the second coordinate.
Here it is also important to add then that; since array variables decay into pointers, then all arrays in C are passed by reference, because you are passing a pointer. So all function calls that modify an array passed to them will do actual modifications.
Knowing this, then your function should be:
void change_array(int *array)
{
array[0] = -1;
array[1] = -1;
}
And then you can perform a call such as:
change_array(a[0]);
In order to modify the elements of the first array of a.
Now, as good practice in C, and in order to avoid segmentation faults, one would pass to the function along with the pointer, an integer saying the size of the array:
void change_array(int *array, int size);
And then always perform the access checking this bound.
In the C program below, I don't understand why buf[0] = 'A' after I call foo. Isn't foo doing pass-by-value?
#include <stdio.h>
#include <stdlib.h>
void foo(char buf[])
{
buf[0] = 'A';
}
int main(int argc, char *argv[])
{
char buf[10];
buf[0] = 'B';
printf("before foo | buf[0] = %c\n", buf[0]);
foo(buf);
printf("after foo | buf[0] = %c\n", buf[0]);
system("PAUSE");
return 0;
}
output:
before foo | buf[0] = 'B'
after foo | buf[0] = 'A'
void foo(char buf[])
is the same as
void foo(char* buf)
When you call it, foo(buf), you pass a pointer by value, so a copy of the pointer is made.
The copy of the pointer points to the same object as the original pointer (or, in this case, to the initial element of the array).
C does not have pass by reference semantics in the sense that C++ has pass by reference semantics. Everything in C is passed by value. Pointers are used to get pass by reference semantics.
an array is just a fancy way to use a pointer. When you pass buf to the function, you're passing a pointer by value, but when you dereference the pointer, you're still referencing the string it points to.
Array as function parameter is equivalent to a pointer, so the declaration
void foo( char buf[] );
is the same as
void foo( char* buf );
The array argument is then decayed to the pointer to its first element.
Arrays are treated differently than other types; you cannot pass an array "by value" in C.
Online C99 standard (draft n1256), section 6.3.2.1, "Lvalues, arrays, and function designators", paragraph 3:
Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and is not an lvalue. If the array object has register storage class, the
behavior is undefined.
In the call
foo(buf);
the array expression buf is not the operand of sizeof or &, nor is it a string literal being used to initialize an array, so it is implicitly converted ("decays") from type "10-element array of char" to "pointer to char", and the address of the first element is passed to foo. Therefore, anything you do to buf in foo() will be reflected in the buf array in main(). Because of how array subscripting is defined, you can use a subscript operator on a pointer type so it looks like you're working with an array type, but you're not.
In the context of a function parameter declaration, T a[] and T a[N] are synonymous with T *a, but this is only case where that is true.
*char buf[] actually means char ** so you are passing by pointer/reference.
That gives you that buf is a pointer, both in the main() and foo() function.
Because you are passing a pointer to buf (by value). So the content being pointed by buf is changed.
With pointers it's different; you are passing by value, but what you are passing is the value of the pointer, which is not the same as the value of the array.
So, the value of the pointer doesn't change, but you're modifying what it's pointing to.
arrays and pointers are (almost) the same thing.
int* foo = malloc(...)
foo[2] is the same as *(foo+2*sizeof(int))
anecdote: you wrote
int main(int argc, char *argv[])
it is also legal (will compile and work the same) to write
int main(int argc, char **argv)
and also
int main(int argc, char argv[][])
they are effectively the same. its slightly more complicated than that, because an array knows how many elements it has, and a pointer doesn't. but they are used the same.
in order to pass that by value, the function would need to know the size of the argument. In this case you are just passing a pointer.
You are passing by reference here. In this example, you can solve the problem by passing a single char at the index of the array desired.
If you want to preserve the contents of the original array, you could copy the string to temporary storage in the function.
edit: What would happen if you wrapped your char array in a structure and passed the struct? I believe that might work too, although I don't know what kind of overhead that might create at the compiler level.
please note one thing,
declaration
void foo(char buf[])
says, that will be using [ ] notation. Not which element of array you will use.
if you would like to point that, you want to get some specific value, then you should declare this function as
void foo(char buf[X]); //where X would be a constant.
Of course it is not possible, because it would be useless (function for operating at n-th element of array?). You don't have to write down information which element of array you want to get. Everything what you need is simple declaration:
voi foo(char value);
so...
void foo(char buf[])
is a declaration which says which notation you want to use ( [ ] - part ), and it also contains pointer to some data.
Moreover... what would you expect... you sent to function foo a name of array
foo(buf);
which is equivalent to &buf[0]. So... this is a pointer.
Arrays in C are not passed by value. They are not even legitimate function parameters. Instead, the compiler sees that you're trying to pass an array and demotes it to pointer. It does this silently because it's evil. It also likes to kick puppies.
Using arrays in function parameters is a nice way to signal to your API users that this thing should be a block of memory segmented into n-byte sized chunks, but don't expect compilers to care if you spell char *foo char foo[] or char foo[12] in function parameters. They won't.
Consider the following statements:
int *pFarr, *pVarr;
int farr[3] = {11,22,33};
int varr[3] = {7,8,9};
pFarr = &(farr[0]);
pVarr = varr;
At this stage, both pointers are pointing at the start of each respective array address. For *pFarr, we are presently looking at 11 and for *pVarr, 7.
Equally, if I request the contents of each array through *farr and *varr, i also get 11 and 7.
So far so good.
Now, let's try pFarr++ and pVarr++. Great. We're now looking at 22 and 8, as expected.
But now...
Trying to move up farr++ and varr++ ... and we get "wrong type of argument to increment".
Now, I recognize the difference between an array pointer and a regular pointer, but since their behaviour is similar, why this limitation?
This is further confusing to me when I also consider that in the same program I can call the following function in an ostensibly correct way and in another incorrect way, and I get the same behaviour, though in contrast to what happened in the code posted above!?
working_on_pointers ( pFarr, farr ); // calling with expected parameters
working_on_pointers ( farr, pFarr ); // calling with inverted parameters
.
void working_on_pointers ( int *pExpect, int aExpect[] ) {
printf("%i", *pExpect); // displays the contents of pExpect ok
printf("%i", *aExpect); // displays the contents of aExpect ok
pExpect++; // no warnings or errors
aExpect++; // no warnings or errors
printf("%i", *pExpect); // displays the next element or an overflow element (with no errors)
printf("%i", *aExpect); // displays the next element or an overflow element (with no errors)
}
Could someone help me to understand why array pointers and pointers behave in similar ways in some contexts, but different in others?
So many thanks.
EDIT: Noobs like myself could further benefit from this resource: http://www.panix.com/~elflord/cpp/gotchas/index.shtml
The difference is because for farr++ to have any effect, somewhere the compiler would need to store that farr will evaluate to the address of the second element of the array. But there is no place for that information. The compiler only allocates place for 3 integers.
Now when you declare that a function parameter is an array, the function parameter won't be an array. The function parameter will be a pointer. There are no array parameters in C. So the following two declarations are equivalent
void f(int *a);
void f(int a[]);
It doesn't even matter what number you put between the brackets - since the parameter really will be a pointer, the "size" is just ignored.
This is the same for functions - the following two are equivalent and have a function pointer as parameter:
void f(void (*p)());
void f(void p());
While you can call both a function pointer and a function (so they are used similar), you also won't be able to write to a function, because it's not a pointer - it merely converts to a pointer:
f = NULL; // error!
Much the same way you can't modify an array.
In C, you cannot assign to arrays. So, given:
T data[N];
where T is a type and N is a number, you cannot say:
data = ...;
Given the above, and that data++; is trying to assign to data, you get the error.
There is one simple rule in C about arrays and pointers. It is that, in value contexts, the name of an array is equivalent to a pointer to its first element, and in object contexts, the name of an array is equivalent to an array.
Object context is when you take the size of an array using sizeof, or when you take its address (&data), or at the time of initialization of an array. In all other contexts, you are in value context. This includes passing an array to a function.
So, your function:
void working_on_pointers ( int *pExpect, int aExpect[] ) {
is equivalent to
void working_on_pointers ( int *pExpect, int *aExpect ) {
The function can't tell if it was passed an array or a pointer, since all it sees is a pointer.
There are more details in the answers to the following questions:
type of an array,
sizeof behaving unexpectedly,
Also see this part of C for smarties website, which is very well-written.
Trying to increment farr or varr fails because neither one is a pointer. Each is an array. The name of an array, when evaluated by itself (except as the operand of the sizeof or address-of operator) evaluates to a value (an rvalue) that's of the correct type to be assigned to a pointer. Trying to increment it is a bit like trying to increment 17. You can increment an int that contains the value 17, but incrementing 17 itself won't work. The name of an array is pretty much like that.
As for your second part, it's pretty simple: if you attempt to declare a function parameter of array type, the compiler silently "adjusts" it to a pointer type. As such, in your working_on_pointers, aExpect and pExpect have exactly the same type. Despite the array-style notation, you've defined aExpect as having type 'pointer to int'. Since the two are the same, it's entirely expected that they'll act the same.
Have a look at this answer I posted in relation to differences between pointers and arrays here on SO.
Hope this helps.
okay, i may be wrong. but arrays and pointers can be used alternately.
int * ptr = (int *)malloc(2* sizeof(int));
ptr[0]=1;
ptr[1]=2;
printf ("%d\n", ptr[0]);
printf ("%d\n", ptr[1]);
here i declared a pointer and now i am treating it as array.
moreover:
As a consequence of this definition,
there is no apparent difference in the
behavior of the "array subscripting"
operator [] as it applies to arrays
and pointers. In an expression of the
form a[i], the array reference "a"
decays into a pointer, following the
rule above, and is then subscripted
just as would be a pointer variable in
the expression p[i] (although the
eventual memory accesses will be
different, as explained in question
2.2). In either case, the expression x[i] (where x is an array or a
pointer) is, by definition, identical
to *((x)+(i)).
reference: http://www.lysator.liu.se/c/c-faq/c-2.html
you need to understand the basic concept of arrays.
when you declare an array i.e
int farr[]
you are actually declaring a pointer with this declaration
const int * farr
i.e; a "constant" pointer to integer. so when you do farr++ you are actually trying to add up to a pointer which is constant, hence compilers gives you an error.
if you need to understand, try to declare a pointer with the above declaration and you would not be able to do the arithmetic which are legal on normal pointers.
P.S:
its been quiet a while i have coded in C so i am not sure about exact syntax. but bottom line is the difference between a pointer and a constant pointer.
Confused with the problem here. New to C, as made obvious by the below example:
#include <stdlib.h>
#include <stdio.h>
void pass_char_ref(unsigned char*);
int main()
{
unsigned char bar[6];
pass_char_ref(&bar);
printf("str: %s", bar);
return 0;
}
void pass_char_ref(unsigned char *foo)
{
foo = "hello";
}
To my understanding, bar is an unsigned character array with an element size of 6 set away in static storage. I simply want to pass bar by reference to pass_char_ref() and set the character array in that function, then print it back in main().
You need to copy the string into the array:
void pass_char_ref(unsigned char *foo)
{
strcpy( foo, "hello" );
}
Then when you call the function, simply use the array's name:
pass_char_ref( bar );
Also, the array is not in "static storage"; it is an automatic object, created on the stack, with a lifetime of the containing function's call.
Two things:
You don't need to pass &bar; just pass bar.
When you pass an array like this, the address of its first (0th) element is passed to the function as a pointer. So, call pass_char_ref like this:
pass_char_ref(bar);
When you call pass_char_ref like this, the array name "decays" into a pointer to the array's first element. There's more on this in this tutorial, but the short story is that you can use an array's name in expressions as a synonym for &array_name[0].
Pointers are passed by value. You have:
void pass_char_ref(unsigned char *foo)
{
foo = "hello";
}
In some other languages, arguments are passed by reference, so formal parameters are essentially aliases for the arguments. In such a language, you could assign "hello" to foo and it would change the contents of bar.
Since this is C, foo is a copy of the pointer that's passed in. So, foo = "hello"; doesn't actually affect bar; it sets the local value (foo) to point to the const string "hello".
To get something like pass by reference in C, you have to pass pointers by value, then modify what they point to. e.g.:
#include <string.h>
void pass_char_ref(unsigned char *foo)
{
strcpy(foo, "hello");
}
This will copy the string "hello" to the memory location pointed to by foo. Since you passed in the address of bar, the strcpy will write to bar.
For more info on strcpy, you can look at its man page.
In C, arrays are accessed using similar mechanics to pointers, but they're very different in how the definitions work - an array definition actually causes the space for the array to be allocated. A pointer definition will cause enough storage to be allocated to refer (or "point") to some other part of memory.
unsigned char bar[6];
creates storage for 6 unsigned characters. The C array semantics say that, when you pass an array to another function, instead of creating a copy of the array on the stack, a pointer to the first element in the array is given as the parameter to the function instead. This means that
void pass_char_ref(unsigned char *foo)
is not taking an array as an argument, but a pointer to the array. Updating the pointer value (as in foo = "hello";, which overwrites the pointer's value with the address of the compiled-in string "hello") does not affect the original array. You modify the original array by dereferencing the pointer, and overwriting the memory location it points to. This is something that the strcpy routine does internally, and this is why people are suggesting you use
void pass_char_ref(unsigned char *foo)
{
strcpy(foo, "hello");
}
instead. You could also say (for sake of exposition):
void pass_char_ref(unsigned char *foo)
{
foo[0] = 'h';
foo[1] = 'e';
foo[2] = 'l';
foo[3] = 'l';
foo[4] = 'o';
foo[5] = 0;
}
and it would behave correctly, too. (this is similar to how strcpy will behave internally.)
HTH
Please see here to an explanation of pointers and pass by reference to a question by another SO poster. Also, here is another thorough explanation of the differences between character pointers and character arrays.
Your code is incorrect as in ANSI C standard, you cannot pass an array to a function and pass it by reference - other data-types other than char are capable of doing that. Furthermore, the code is incorrect,
void pass_char_ref(unsigned char *foo)
{
foo = "hello";
}
You cannot assign a pointer in this fashion to a string literal as pointers use the lvalue and rvalue assignment semantics (left value and right value respectively). A string literal is not an rvalue hence it will fail. Incidentally, in the second link that I have given which explains the differences between pointers and arrays, I mentioned an excellent book which will explain a lot about pointers on that second link.
This code will probably make more sense in what you are trying to achieve
void pass_char_ref(unsigned char *foo)
{
strcpy(foo, "hello");
}
In your main() it would be like this
int main()
{
unsigned char bar[6];
pass_char_ref(bar);
printf("str: %s", bar);
return 0;
}
Don't forget to add another line to the top of your code #include <string.h>.
Hope this helps,
Best regards,
Tom.
Since bar[] is an array, when you write bar, then you are using a pointer to the first element of this array. So, instead of:
pass_char_ref(&bar);
you should write:
pass_char_ref(bar);
Time again for the usual spiel --
When an expression of array type appears in most contexts, its type is implicitly converted from "N-element array of T" to "pointer to T" and its value is set to point to the first element of the array. The exceptions to this rule are when the array expression is the operand of either the sizeof or & operators, or when the array is a string litereal being used as an initializer in a declaration.
So what does all that mean in the context of your code?
The type of the expression bar is "6-element array of unsigned char" (unsigned char [6]); in most cases, the type would be implicitly converted to "pointer to unsigned char" (unsigned char *). However, when you call pass_char_ref, you call it as
pass_char_ref(&bar);
The & operator prevents the implicit conversion from taking place, and the type of the expression &bar is "pointer to 6-element array of unsigned char" (unsigned char (*)[6]), which obviously doesn't match the prototype
void pass_char_ref(unsigned char *foo) {...}
In this particular case, the right answer is to ditch the & in the function call and call it as
pass_char_ref(bar);
Now for the second issue. In C, you cannot assign string values using the = operator the way you can in C++ and other languages. In C, a string is an array of char with a terminating 0, and you cannot use = to assign the contents of one array to another. You must use a library function like strcpy, which expects parameters of type char *:
void pass_char_ref(unsigned char *foo)
{
strcpy((char *)foo, "hello");
}
Here's a table of array expressions, their corresponding types, and any implicit conversions, assuming a 1-d array of type T (T a[N]):
Expression Type Implicitly converted to
---------- ---- -----------------------
a T [N] T *
&a T (*)[N]
a[0] T
&a[0] T *
Note that the expressions a, &a, and &a[0] all give the same value (the address of the first element in the array), but the types are all different.
The use of the address of operator (&) on arrays is no longer allowed. I agree that it makes more sense to do &bar rather than bar, but since arrays are ALWAYS passed by reference, the use of & is redundant, and with the advent of C++ the standards committee made it illegal.
so just resist the urge to put & before bar and you will be fine.
Edit: after a conversation with Roger, I retract the word illegal. It's legal, just not useful.