why the actual parameter is altered in the first code? - c

//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.

Related

What will happen when passing an array name to a function?

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

Why is setting an array of characters to NULL illegal? Passing to function changes behavior

The name of an array is a synonym for the address of the first element of the array, so why can't this address be set to NULL? Is it a language rule to prevent a memory leak?
Also, when we pass an array to a function, it's behavior changes and it becomes possible to set it to NULL.
I don't understand why this occurs. I know it has something to do with pointers, but I just can't wrap my mind around it.
Example:
void some_function(char string[]);
int main()
{
char string[] = "Some string!";
some_function(string);
printf("%s\n", string);
return 0 ;
}
void some_function(char string[])
{
string = NULL;
}
Output: Some string!
I read that when an array is passed into a function, what's actually passed are pointers to each element, but wouldn't the name of the array itself still be a synonym for the address of the first element? Why is setting it to NULL here even allowed, but not in the main function?
Is it at all possible to set an array to NULL?
An array is not a pointer - the symbol string in your case has attributes of address and size whereas a pointer has only an address attribute. Because an array has an address it can be converted to or interpreted as a pointer, and the language supports this implicitly in a number of cases.
When interpreted as a pointer you should consider its type to be char* const - i.e. a constant pointer to variable data, so the address cannot be changed.
In the case of passing the array to a function, you have to understand that arrays are not first class data types in C, and that they are passed by reference (i.e. a pointer) - loosing the size information. The pointer passed to the function is not the array, but a pointer to the array - it is variable independent of the original array.
You can illustrate what is effectively happening without the added confusion of function call semantics by declaring:
char string[] = "Some string!";
char* pstring = string ;
then doing:
pstring = NULL ;
Critically, the original array data cannot just "disappear" while it is in scope (or at all if it were static), the content of the array is the variable, whereas a pointer is a variable that refers to data. A pointer implements indirection, and array does not. When an array is passed to a function, indirection occurs and a pointer to the array is passed rather than a copy of the array.
Incidentally, to pass an array (which is not a first class data type) by copy to a function, you must wrap int within a struct (structs in C are first class data types). This is largely down to the original design of C under constraints of systems with limited memory resources and the need to to maintain compatibility with early implementations and large bodies of legacy code.
So the fact that you cannot assign a pointer to an array is hardly the surprising part - because to do so makes little sense. What is surprising perhaps is the semantics of "passing an array" and the fact that an array is not a first class data type; leading perhaps to your confusion on the matter.
You can't rebind an array variable. An array is not a pointer. True, at a low level they are approximately similar, except pointers have no associated dimension / rank information.
You cant assign NULL to the actual array (same scope), but you can assign to a parameter since C treats it like a pointer.
The standard says:
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted
to ‘‘qualified pointer to type’’,
So in the function the NULL assignment is legal.

Cannot understand why that behavior

I have this code:
char *name[] = { "a1", "b2", "c3", "d4" };
printf("%s\n", *name); //the critical line
Related to critical line:
In this form, the output is simple: a1.
If I replace the critical line with:
printf("%s\n", ++*name);
then the output is 1. I think until now everything is good.
Taking in account that name is a pointer to the first string of characters, respectively "a1", I replace the critical line with:
printf("%s\n", ++name);
in the hope that I'll get "b2" result as output. But I get this error:
../src/test.c:32: error: lvalue required as increment operand.
Question: I can't understand why ++*name is legal - name is a pointer to first string of characters - and ++name isn't. In my opinion, the ++name should move the name to the next string of characters. Can anybody explain me where is the lack in my understing?
When you write ++name, the array name is converted to a pointer to the first element of the array. The result of this conversion is not an lvalue, and it can't be modified with ++ or otherwise. You could instead write name+1, which would print the right thing. When name is an array, there is no way to modify it to refer to anything other than that array[*].
Consider also:
char **p = name; // OK, `name' converts to pointer
++p; // OK, `p' is an lvalue
++(p+1); // not OK, `p+1' is not an lvalue
++((char**)p); // not OK, `(char**)p' is not an lvalue
++*name; // OK, `*name' is an lvalue
Roughly speaking, an "lvalue" is an expression that refers to an object, whereas a "not an lvalue" is an expression that has a value. The difference between an object and a value, is that an object is a place for storing values (well, one value at a time). Values can never be modified, objects sometimes can.
Whenever you have a subexpression which is an lvalue but whose current value is needed, the object is read/loaded/whatever you want to call it. In C++ this is called an "lvalue to rvalue conversion", I can't remember whether it's called anything in C other than "evaluating the subexpression".
[*] you can shadow it with another variable name in an inner scope, that refers to something else. But that's still not modifying the outer name, just temporarily hiding it.
name is an array, so, except when you use it as operand of the sizeof or & operators, it is evaluated as a pointer to the initial member of the array objet and is not an lvalue.
Accordingly, you can't modify name directly, with an operator such as ++ (remember that the postfix increment operator need a modifiable lvalue as operand). Otherwise, you can use a temporary pointer (p in the following example).
#include <stdio.h>
const char *name[] = { "a1", "b2", "c3", "d4" };
const char **p = name;
printf("%s\n", *p); /* Output: a1 */
*++p; /* or *p++ */
printf("%s\n", *p); /* Output: b2 */
While name points to the address of the first element, name is not of type char *, but char *[4]. therefore, sizof(name) == sizeof(char *)*4
Incrementing a pointer always means to add the size of the data it points to. So, after incrementing, it points behind the whole array. Just like if you have
int i, a;
int *p = &i;
p++;
p will now point behind i. It will point to a, if the compiler decided to put a behind i.
Also note that your array only contains 4 pointers, not 4 strings. Like above, it is the compilers choice where those strings actually are. So, the end of the first string is not necessarily next to the beginning of the second string. Especially if you assign other values (string adresses) to name[1] later. Therefore, you may cast name to char **, but should not cast to char *. Incrementing the former will point to the second element (second char* pointer).
First off, make sure you're totally happy and confident with the following fact: An arrays is not a pointer.
Second, what's in a name? The array decays into a pointer to the first element. After decay, the expression name has type char ** (pointer to first element of an array of char*. However, the decayed expression is an rvalue. You cannot modify it! And naturally so, since it makes no sense to modify a pointer that's a pointer to a fixed array.
Therefore, you cannot directly increment the pointer which is the result of decaying name, no more than you can say ++5 or ++foo() (where foo returns a primitive type by value)[whoops, that was a concession meant for C++].
What you can say is this:
char ** np = name;
++np;
printf("%s", *np);
This has the same effect as printing name[1], but now you also have a variable that holds a pointer to the second array element.

Pointer arithmetic and arrays: what's really legal?

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.

Pointers, arrays and passing pointers to methods

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.

Resources