When are values implicitly converted to pointers? - c

I have many functions like:
void write(char* param, int len)
{
//
}
And I notice that I almost never use the & operator for arguments. When I pass an array:
char in[20];
write(in, 20);
I dont need it, but when I pass a single value:
write(5, 1);
I don't seem to need it, and when I pass a pointer:
char* in = malloc(20);
write(in, 20);
I also dont need it. So in which circumstances do I actually need to call:
write(&in, 1);
Because I'm confused :D

Are you sure about your second case? It doesn't compile for me and should be
char in = 5;
write(&in, 1);

When are values implicitly converted to pointers?
In practice, integer types may be converted to pointers implicitly by the compiler. In theory, this is illegal and compilers that accept it will usually issue a warning:
example.c:2:6: warning: incompatible integer to pointer conversion initializing
'void *' with an expression of type 'int'
In your above example:
char in = 5;
write(in, 20);
char is an integer type, so if your compiler allows it, it may be implicitly converted to a pointer type, although it is not part of the C standard and is completely compiler-specific.
Note that converting an integer type to a pointer type using a cast is allowed by the standard, although results are implementation-defined:
char in = 5;
write((char *)in, 20);
The only allowed case of implicit conversion is when integer constant 0, which denotes a null pointer:
write(0, 20); // allowed
Note that the integer constant is itself allowed, but a variable of integer type with the value 0 is not:
char in = 0;
write(in, 20); // not allowed
As for the others, when you pass a pointer, you don't need the &, obviously, because it's already a pointer. When you pass an array, it decays to a pointer so you don't need & there either. In both these cases it would be actually illegal to use it, as your function above expects a char * and be fed with a char ** and a char (*)[20] respectively.

If you copy somehow the prototype of function 'write' to the file where you call this function, as follows.
void write(char *in, int len);
void foo(int bar){
char in=5;
write(in, 1);
}
You will probably get a warning. Because in is not a pointer, though 5 can be an address.
I guess if your program is compiled and linked successfully, it will crash at run-time.

with using;
return_type function_name(prim_data_type* param...)
param is a pointer that pointing an address in the memory and *param is the value in that address.
Answer is about what you want to do with this param.
char in[20];
by saying that "in" is the first element's address. So at the function call:
write(in, 20);
you are sending the first element's address so in the function implementation, you can access the first element by *param, second element with *(param+1) or param[1] etc.
The place that you are confused is here:
char in = 5;
write(in, 1);
Because in is the address 5 (00000005), so in the implementation of the function you are accessing that place whichever value is there. You must be careful with using like this.
In the malloc operation:
char* in = malloc(20);
write(in, 20);
in is a pointer to an address (first element's address) holding up 20 elements of char can be take space. In the function you can access to all elements with param pointer(*param is the first element, *(param+7) or param[7] is the 8. element)
In the conclusion, when you want to play with an primary data typed variable (int, float, char..) in another function, you must use;
write(&in);
By doing that, in the implementation of that write function, you can access that variable, change the value by *param with no confusion.
Note:Some explanations were simplified here for better understanding. Extra warnings would be welcomed here.

Functions and arrays decay into pointers in some contexts. Functions can decay into pointers to functions, and arrays can decay into a pointer to the first element of the array. No other types behave this way.
This example:
char in = 5;
write(in, 1);
Is wrong though. You definitely need the & in that case. Are you sure it worked like that?

Related

Can I safely cast a &char[] to char**?

Having the following code:
char data[2048];
And a function declared like this:
int f(char** data);
Can I safely call it like this:
f((char**)&data);
If I just use &data, the compiler issue the following warning:
warning C4047: 'function' : 'char **' differs in levels of indirection from 'char (*)[2048]'
No, you cannot.
data is an array. &data is a pointer to an array. It is not a pointer to a pointer. Despite the fact that data decays to a pointer in multiple contexts, it is not itself a pointer - taking the address gives you the address of the array.
If you want a pointer to a pointer to the array, you might try something like this:
char *pdata = data; // data decays to a pointer here
// (a pointer to the first element of the array)
f(&pdata); // Now &pdata is of type char ** (pointer to a pointer).
though, of course, what you actually need will depend on what your usecase is.
A pointer-to-pointer is not an array, nor is it a pointer to an array, nor should it be used to point at an array. Except for the special case where it can be used to point at the first item of an array of pointers, which is not the case here.
A function int f(char** data); cannot accept a char data[2048]; array as parameter. Either the array type needs to be changed, or the function needs to be rewritten.
Correct function alternatives would be:
int f (char* data);
int f (char data[2048]); // completely equivalent to char* data
int f (char (*data)[2048]); // array pointer, would require to pass the address of the array
As stated in this more detailed example:
While an array name may decay into a pointer, the address of the array does not decay into a pointer to a pointer. And why should it? What sense does it make to treat an array so?
Pointers to pointers are sometimes passed to modify the pointers (simple pointer arguments don't work here because C passes by value, which would only allow to modify what's pointed, not the pointer itself). Here's some imaginary code (won't compile):
void test(int** p)
{
*p = malloc ... /* retarget '*p' */
}
int main()
{
int arr[] = {30, 450, 14, 5};
int* ptr;
/* Fine!
** test will retarget ptr, and its new value
** will appear after this call.
*/
test(&ptr);
/* Makes no sense!
** You cannot retarget 'arr', since it's a
** constant label created by the compiler.
*/
test(&arr);
return 0;
}
You can do something like that.
char data[2048];
char *a=&data[0];
char **b=&a;
f(b);

What exactly does "const int *ptr=&i" mean?Why is it accepting addresses of non-constants?

Your answers are very much sought to clear this major lacuna in my understanding about const that I realized today.
In my program I have used the statement const int *ptr=&i; but haven't used any const qualifier for the variable i.Two things are confusing me:
1) When I try to modify the value of i using ptr ,where I have used const int *ptr=&i;,I get the error assignment of read-only location '*ptr'|,even though I haven't declared the variable i with the const qualifier.So what exactly the statement const int *ptr=&i; mean and how does it differ from int * const ptr=&i;?
I had it drilled into my head that const int *ptr=&i; means that the pointer stores the address of a constant,while int * const ptr=&i; means the pointer is itself constant and can't change.But today one user told me in discussion(LINK) that const int *ptr means the memory pointed to must be treated as nonmodifiable _through this pointer_.I find this something new as this kinda means "some select pointer can't alter the value(while others can)".I wasn't aware of such selective declarations!!But a 180k veteran attested to it that that user is correct!!.So can you state this in a clearer,more detailed and more rigorous way?What exactly does ``const int *ptr=&i; mean?
2) I was also told that we can lie to the program in the statement const int *ptr=&i; by assigning the address of a non-constant to the pointer.What does it mean?Why are we allowed to do that?Why don't we get a warning if we assign the address of a non-constant to the pointer ptr which expects address of a constant?And if it is so forgiving about being assigned address of non-constant,why it throws an error when we try to change the value of that non-constant,which is a reasonable thing to do,the pointed variable being a non-constant?
#include <stdio.h>
int main ()
{
int i=8;
const int *ptr=&i;
*ptr=9;
printf("%d",*ptr);
}
error: assignment of read-only location '*ptr'|
The definition const int *ptr = &i; essentially says “I will not modify i through ptr.” It does not say that the int that ptr points to is const, it says that ptr should not be used to modify it.
This conversion is allowed because it makes sense: Since i is not const, I am allowed to modify it, but I can also choose not to modify it. No rule is broken when I choose not to modify i. And, if I create a pointer to i and say “I am not going to use this pointer to modify i”, that is fine too.
The reason you would want to do this is so that you can pass the address of an object to a routine that takes a pointer to a const object. For example, consider the strlen routine. The strlen routine does not modify its input, so its parameter is a pointer to const char. Now, I have a pointer to char, and I want to know its length. So I call strlen. Now I am passing a pointer to char as an argument for a parameter that is pointer to const char. We want that conversion to work. It makes complete sense: I can modify my char if I want, but the strlen routine is not going to, so it treats them as const char.
1) You get an error with *ptr = something; because you declared ptr as a pointer to const int, but then you violated your promise not to use ptr to modify the int. The fact that ptr points to an object that is not const does not negate your promise not to use ptr to modify it.
2) It is not a lie to assign the address of a non-const object to a pointer to const. The assignment does not say that the object is const, it says that the pointer should not be used to modify the object.
Additionally, although you have not asked this, the const attribute in C is not completely binding. If you define an object to be const, you should not modify it. However, there are other situations in which a pointer to const is passed to a routine that converts it to a pointer to non-const. This is effectively a defect in the language, an inability to retain all the information necessary to handle const in the ways we might prefer. An example is the strchr routine. Its declaration is char *strchr(const char *s, int c). The parameter s is const char * because strchr does not change its input. However, the pointer that strchr routines is derived from s (it points to one of the characters in the string). So, internally, strchr has converted a const char * to char *.
This allows you to write code that passes a char * to strchr and uses the returned pointer to modify the string, which is fine. But it means the compiler cannot completely protect you from mistakes such as passing a const char * to strchr and using the returned pointer to try to modify the string, which may be an error. This is a shortcoming in C.
Unravelling this:
const int *ptr means that "*ptr is a const int"; i.e. "ptr is a pointer to a const int". You can't write *ptr = 9 since *ptr is a constant. You can however write int j; ptr = &j; since you can allow the pointer ptr to point at a different int.
int * const ptr means "ptr is a constant pointer to an int". You can write *ptr = 9 since you are not changing the address that ptr is pointing to. You can't assign it to something else though; i.e. int j; ptr = &j; will not compile.
As for the second part (2), it's very useful to have const int* ptr, especially in function prototypes since it tells the caller of the function that the variable to which ptr is pointing will not be modified by the function. As for assignment, if you had int* m = &i, then ptr = m is ok but m = ptr is not ok since the latter will 'cast away the constness'; i.e. you've circumvented the const.

sizeof on argument

Even with int foo(char str[]); which will take in an array initialized to a string literal sizeof doesn't work. I was asked to do something like strlen and the approach I want to take is to use sizeof on the whole string then subtract accordingly depending on a certain uncommon token. Cuts some operations than simply counting through everything.
So yea, I tried using the dereferencing operator on the array(and pointer too, tried it) but I end up getting only the first array element.
How can I sizeof passed arguments. I suppose passing by value might work but I don't really know if that's at all possible with strings.
int foo(char str[]); will take in an array initialized to a string literal
That's not what that does. char str[] here is identical to char* str. When an array type is used as the type of a parameter, it is converted to its corresponding pointer type.
If you need the size of a pointed-to array in a function, you either need to pass the size yourself, using another parameter, or you need to compute it yourself in the function, if doing so is possible (e.g., in your scenario with a C string, you can easily find the end of the string).
You can't use sizeof here. In C arrays are decayed to pointers when passed to functions, so sizeof gives you 4 or 8 - size of pointer depending on platform. Use strlen(3) as suggested, or pass size of the array as explicit second argument.
C strings are just arrays of char. Arrays are not passed by value in C; instead, a pointer to their first element is passed.
So these two are the same:
void foo(char blah[]) { ... }
void foo(char *blah) { ... }
and these two are the same:
char str[] = "Hello";
foo(str);
char *p = str;
foo(p);
You cannot pass an array as a function parameter, so you can't use the sizeof trick within the function. Array expressions are implicitly converted to pointer values in most contexts, including function calls. In the context of a function parameter declaration, T a[] and T a[N] are synonymous with T *.
You'll need to pass the array size as a separate parameter.

C Programming - Pass-by-Reference

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.

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