Studying the K&R book in C I had a few questions regarding complicated pointer declarations and pointer-array relationships.
1) What exactly is the difference between
char amessage[] = "this is a string";
and
char *pmessage
pmessage = "this is a string"
and when would you use one or the other?
From my understanding the first one allocates some amount of memory according to the size of the string, and then stores the chars in the memory. Then when you access amessage[] you just directly access whatever char you’re looking for. For the second one you also allocate memory except you just access the data through a pointer whenever you need it. Is this the correct way of looking at it?
2) The book says that arrays when passed into functions are treated as if you gave the pointer to the first index of the array and thus you manipulate the array through manipulating the pointer even though you can still do syntax like a[i]. Is this true if you just created an array somewhere and want to access it or is it only true if you pass in an array into a function? For example:
char amessage[]= "hi";
char x = *(amessage + 1); // can I do this?
3) The book says the use of static is great in this particular function:
/* month_name: return name of n-th month */
char *month_name(int n)
{
static char *name[] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return (n < 1 || n > 12) ? name[0] : name[n];
}
I don’t understand why exactly this is a good use of static. Is it because the char *name[] would get deleted after function return if it is not static (because its a local variable)? Then does that mean in c you can’t do stuff like:
void testFunction(){
int x = 1;
return x;
}
Without x being deleted before you use the return value? (Sorry I guess this might not be a pointer question but it was in the pointer chapter).
4) There are some complicated declaration like
char (*(*x())[])()
I’m really confused as to what is going on. So the x() part means a function x that returns a pointer? But what kind of pointer does it return its just a “” without like int or void or w/e. Or does that mean a pointer to a function (but I thought that would be like (*x)())? And then after you add brackets (because I assume brackets have the next precedence)…what is that? An array of functions?
This kind of ties to my confusion with function pointers. If you have something like
int (*func)()
That means a pointer to a function that returns an int, and the name of that pointer is func, but what does it mean when its like int (*x[3])(). I don’t understand how you can replace the pointer name with an array.
Thanks for any help!
Kevin
amessagewill always refer to the memory holdingthis is a string\0. You cannot change the address it refers to.pmessagecan be updated to point to any character in memory, whether or not it is part of a string. If you assign topmessage, you might lose your only reference tothis is a string\0. (It depends if you made references anywhere else.)I would use
char amessage[]if I intended to modify the contents ofamessage[]in place. You cannot modify the memory thatpmessagepoints to. Try this little program; comment outamessage[0]='H'andpmessage[0]='H';one at a time and see thatpmessage[0]='H';causes a segmentation violation:Modifying a string that was hard-coded in the program is relatively rare;
char *foo = "literal";is probably more common, and the immutability of the string might be one reason why.You can do that, however it is pretty unusual:
At least, I have never seen a “production” program that did this with character strings. (And I’m having trouble thinking of a program that used pointer arithmetic rather than array subscripting on arrays of other types.)
In this specific case, I believe the
staticis needless; at least GCC is able to determine that the strings are not modified and stores them in the.rodataread-only data segment. However, that might be an optimization with string literals. Your example with another primitive data type (int) also works fine because C passes everything by value both on function calls and function returns. However, if you’re returning a pointer to an object allocated on the stack then thestaticis absolutely necessary, because it determines where in memory the object lives:If you change the storage duration of
arraytostatic, then the address that is being returned is not automatically allocated, and will continue to work even after the function has returned:You can see where the memory allocation changes between
stackarrayandstaticstackarray:The
.bsssection in the version withoutstaticis 8 bytes smaller than the.bsssection in the version withstatic. Those 8 bytes in the.bsssection provide the persistent address that is returned.So you can see that the case with strings didn’t really make a difference — at least GCC doesn’t care — but pointers to other types of objects, the
staticmakes all the difference in the world.However, most functions that return data in function-local-
staticstorage have fallen out of favor.strtok(3), for example, extracts tokens from a string, and if subsequent calls tostrtok(3)includeNULLas the first argument to indicate that the function should re-use the string passed in the first call. This is neat, but means a program can never tokenize two separate strings simultaneously, and multiple-threaded programs cannot reliably use this routine. So a reentrant version is available,strtok_r(3), that takes an additional argument to store information between calls.man -k _rwill show a surprising number of functions that have reentrant versions available, and the primary change is reducingstaticuse in functions.First, don’t panic. You’ll almost never need anything this complicated. Sometimes it is very handy to have a table of function pointers and call the next one based on a state transition diagram. Sometimes you’re installing signal handlers with
sigaction(2). You’ll need slightly complicated function pointers then. However, if you usecdecl(1)to decipher what you need, it’ll make sense:cdecl(1)only understands a subset of C native types, so replacesiginfo_twithvoidand you can see roughly what is required:Expert C Programming: Deep C Secrets has an excellent chapter devoted to understanding more complicated declarations, and even includes a version of
cdecl, in case you wish to extend it to include more types andtypedefhandling. It’s well worth reading.