Array of strings and char ** environ variable - c

I want to know how an array of strings is declared? What I do is I declare an array of pointers of pointers to strings. Eg.
char *array[]= {"string1","string2","string3"};
I was reading about modifying environment variables in Linux and stumbled upon the pointer char **environ ( http://www.cs.bham.ac.uk/resources/courses/2005/17423/doc/libc/Environment-Access.html#Environment-Access ).
char **environ is declared as an array of strings. I think it should be a pointer to a pointer. For eg.
char *array[]= {"string1","string2","string3"};
environ = array;
Am I doing something wrong?
I also read somewhere that char *argv[] = char **argv. How is it possible?
Edit: I also found this thread to be very helpful.
Should I use char** argv or char* argv[] in C?

you are mixing up two different things that are in fact difficult to know for someone who is learning C. Declaration of variables inside a function and as a function parameter are not the same thing. The equivalence
char*argv[] ~~~ char **argv
holds because this a parameter (of main). There the array is in fact the same thing as declaring a pointer.
Your assignment environ = array is not wrong, syntactically, the compiler will accept it. But it is wrong semantically for several reasons:
You don't know who and how is
allocated *environ.
You loose the reference to the
initial contents of *eviron.
You assign a local storage that will
be recycled once you leave the scope
of the function. So *environ will
be undefined, once you left the
function.
So environ is a particularly bad example to do such an assignment.

well the problem is this. In your program are several pointer. One you asign to a array of strings and one called environ that points to the environment variables. What you say to C with environ = array is give environ the same value as array.. but array has a pointer to a local array. So after that statement the environ pointer will just point to the array you made but has not made any changes to its previous content.
I think you need to strcpy all elements of array to environ. Or use a api call setenv (i think it is)
and to you'r second question. Yes the first pair of [] can always be rewritten to a pointer. so array[] = *array as is array[][5] = (*array)[5] and there for *array[] = **array
i hope to have helped you.

In C a string is basically just an array of chars. in addition an array name also represents its address.
this is the reason why argv[] is the address of the array of chars (which is a string) and *argv is also the address of the string (since it's the address of the first char).

Related

char * vs char[] and much more

I'm confused about the way C handles strings and char * vs char[].
char name[10] = "asd";
printf("%p\n%p", &name, &name[0]); //0x7ffed617acd
//0x7ffed617acd
If this code gives the same addresses for both arguments, does it mean that the C compiler takes char arrays (strings) as a pointer to the first char in the array and moves in the memory till it gets the null terminator? Why wouldn't the same happen if we changed the char name[] to char *name? (I know they differ but what makes C take both in a different way?)
I know that arrays can't be assigned after declaration (unless you used something like strcpy, strcat) which is also confusing. Why wouldn't C take them as any other data type? (Something tells me the compiler has a specific addr for it while you can assign char* to whatever location in the mem since its a pointer).
I know that char * have fixed size unlike char[] which makes char * not usable for first argument of strcat.
in C a "string" is an array of type "char" (terminated with \0).
When you are referring to an array in C, you are using a pointer to the first element. In this case (char *).
According to the ANSI-C standard the name of an array is a pointer to the first element.
Being able to write name instead of &name[0] is syntactical sugar.
In the same way accessing an array element writing name[i] is analogue to writing *(name+i).
does it mean that the c compiler takes char arrays (strings) as a pointer to the first char in the array
An array is not a pointer. But an array will implicitly convert to a pointer to first element. Such conversion is called "decaying".
... and moves in the memory till it gets the null terminator???
You can write such loop if you know the pointer is to an element of null terminated string. If you write that loop, then the compiler will produce a program that does such thing.
Why wouldn't the same happen if we changed the char name[] to char *name?
Your premise is faulty. You can iterate an array directly, as well as using a pointer.
If this code gives the same addresses for both arguments, does it mean
The address of an object is the first byte of the object. What this "same address" means is that the first byte of the first element of the array is in the same address as the first byte of the array as a whole.
I know that arrays can't be assigned after declaration (unless you used something like strcpy, strcat) which is also confusing.
Neither strcpy nor strcat assign an array. They assign elements of the array which you can also do without calling those functions.
Why wouldn't C take them as any other data type?
This question is unclear. What do you mean by "C taking them"? Why do you think C should take another data type? Which data type do you think it should take?
char name[10] = "asd";
printf("%p\n%p", &name, &name[0]);
The arguments are of type char(*)[10] and char* respectively. The %p format specifier requires that the argument is of type similar to void* which isn't similar to those arguments. Passing an argument of a type other than required by the format specifier results in undefined behaviour. You should cast other pointer types to void* when using %p.

C *argv[] and char array[][]

Im completely stuck on how to convert a output from one of my functions of char fileParameters[10][10] into the format of *argv[] to pass into another function that expects the same format of argv. The purpose of this is to pass a file input through the same parser that parses command line input.
I understand argv is a array of pointers but I'm getting confused as aren't all arrays a pointer to the first element?
So therefore fileParameters[10][10] is a pointer to a pointer and if take the address using & then I expected an array of pointers? I've tried everything and whatever I do results in a segfault.
Any nudges in the right direction would be appreciated.
I understand argv is a array of pointers but im getting confused as arent all arrays a pointer to the first element?
No. Arrays can be implicitly converted to a pointer to the first element, but they are not the same thing. You can easily tell by the fact that after char a[10];, sizeof a will give 10 rather than sizeof(char*). Your char[10][10] is an array of arrays, not an array of pointers.
You need to convert your array to an array of pointers:
char fileParameters[10][10];
// ...
char *fileParameterPtrs[10];
for (int i = 0; i != 10; i++)
fileParameterPtrs[i] = fileParameters[i];
and then you can pass fileParameterPtrs to the code that is expecting an array of pointers.
#Vasfed rightly adds that you mentioned "that expects the same format of argv". This doesn't mean just any array of pointers, it means an array of pointers that is terminated by NULL. Your original fileParameters cannot hold any NULL value, so you will need to add one yourself. If all 10 parameters are filled, this is as simple as changing fileParameterPtrs's length to 11 and adding one more assignment. If you have some other way of keeping track of how many fileParameters are used, adjust the code accordingly.

char *filenames[1] or char *filenames What is the difference?

I am writing a program which will store a list of file names as a string array. When I declare it as
char *filenames[1]
I have no errors... but when I do
char *filenames
I get a few errors. Not at the declaration but in later use. for example when I do:
filenames[3]= (char*)malloc( strlen(line) + 1);//here ERROR is : Cannot assign char* to char.
But with the first declaration with [1] it is all fine. I was just wondering what is the difference between them?
Trust me I tried looking for the answer on google but can't find any good ones regarding this case.
At the outset, char *filenames represents a char * variable named filenames whereas, char *filenames[1] represents an array of char * variables, with one element.
From the point of the usage, both are same, but the major difference is, the first one has to be used as a normal variable and the second one can be indexed, as an array.
If you're in need to use only one variable, don't use an array. You may be in danger of using out of bound indexes if you're not careful enough.
Also, as a note, please see this discussion on why not to cast the return value of malloc() and family in C..
Relative to this statement
filenames[3]= (char*)malloc( strlen(line) + 1);//here ERROR is : Cannot assign char* to char.
the both declarations are wrong.
The firwt declaration
char *filenames[1]
is wrong because it declares an array of pointers to char having only one element. But in the statement with memory allocation there is used index 3. So there is an attempt to access memory beyond the array becuase the only valid index for an array that has one element is 0.
The second declaration
char *filenames
is wrong because it does not declare an array of pointers. So applying the subscript operator for identifier filenames
filenames[3]
gives a scalar object of type char instead of a pointer of type char *

What is an array of constant pointers in C?

Isn't the address of an array and thus of all its elements as well constant anyway?
And if so, in a declaration like:
char *const argv[]
isn't the const qualifier redundant?
No, the const in char *const argv[] is not redundant.
First, const and "constant" are actually two different things in C, even though the const keyword is obviously derived from the word "constant". A constant expression is one that can be evaluated at compile time. const really means "read-only". For example:
const int r = rand();
is perfectly legal.
Yes, the address of an array -- like the address of any object -- is read-only. But that doesn't mean that the value of the array (which consists of the values of its elements) is read-only, any more than any other object is necessarily read-only.
Consider these three declarations:
char *arr1[10];
char *const arr2[10];
const char *arr3[10];
arr1 is a 10-element array of pointers to char. You can modify the char* elements and you can modify the objects that those elements point to.
arr2 is an array of const (read-only) pointers to char. That means that you can't modify the char* elements of the array (once they're initialized) -- but you can still modify the char objects or arrays that those elements point to.
And arr3 is an array of pointers to const char; you can modify the array elements, but you can't modify what they point to.
Now the fact that you used the name argv suggests that you're talking about the second parameter to main, which has some huge effects on this. The language specifies that main's second parameter is
char *argv[]
or, equivalently,
char **argv
There is no const. You can probably get away with adding one, but it's best to follow the form specified by the standard. (Update: I see from your comment that you're asking about the argv parameter of getopt(), which is defined as char * const argv[].)
And since it's a parameter defined as an array, another rule comes into play: a parameter defined as an array of some type is "adjusted" to a pointer to that type. (This rule applies only to parameters.) This isn't a run-time conversion. A function cannot have a parameter of array type.
The relationship between arrays and pointers in C can be confusing -- and there's a lot of misinformation out there. The most important thing to remember is that arrays are not pointers.
Section 6 of the comp.lang.c FAQ is an excellent explanation of the details.
Isn't the address of an array and thus of all its elements as well
constant anyway?
Yes, and it is true for any object in C. Recall that by object here, we mean a location in memory having a value and referenced by an identifier. The identifier is bound to a fixed memory location throughout its scope and you cannot change it. You can change the value of the object though.
int a = 4;
a = 6; // legal. you can change the value of the object
&a = 23456; // illegal. you cannot change the address of the object
Similarly, an array is also an object and each of its elements will have a fixed memory address. However, the value held by an element of the array has nothing to do with the address of the element.
Note that if the declaration appears in a function parameter list, then the following are equivalent
char *const argv[]
char *const *argv
which means that argv is a pointer to an object which is of type char *const, i.e., a constant pointer to a character. It's obvious that char *const *argv and char **argv are different. So let's take another example.
char *const argv[10];
The above statement defines argv to be an array of 10 constant pointers to a character. This means that you have to initialize the array and cannot later change the pointers to point to a different character. However, this has nothing to do with the address of the array elements.
char c = 'A';
char d = 'B';
char *const argv[2] = {&c, &d};
argv = &c; // illegal. you cannot the change the address of an object
argv[0] = &d; // illegal. you cannot change the value of the array element
*argv[0] = 'C'; // legal. you change the value pointed to by the element
Without the const qualifier, char *argv[2] means an array of 2 pointers to characters.
This is clearly different from the case when we have the const qualifier as explained above. Therefore, to answer your second question, no, the const qualifier is not redundant. That's because the const qualifier qualifies the type of the array elements.
No, it isn't. char *const argv[] is an array of constant pointers to char. So the const makes the pointers in the array constant (you cannot change them to point to other strings in memory).

Is this typecast correct?

Basically my code crashes in NucleoProf_init, judging by gdb's stack-trace, and by the fact that is the only function that I call.
#include <HsFFI.h>
static char *argv[] = {"NucleoProf", "", "", 0};
static int argc = 1;
HsBool NucleoProf_init(void){
// Initialize Haskell runtime
hs_init(&argc, (char***)&argv );
return HS_BOOL_TRUE;
}
I suspect that it is the way I pass the argv argument, or perhaps, the typecast of argv, because the stack-trace contains the following:
#3 0x00007ffff5956282 in setFullProgArgv ()
from /usr/lib/ghc/libHSrts-ghc7.4.1.so
#4 0x00007ffff5956d04 in hs_init_ghc () from /usr/lib/ghc/libHSrts-ghc7.4.1.so
#5 0x00007ffff5b9ed4f in NucleoProf_init ()
Question: Is this the correct way of "synthesizing" a trivial command line?
You can try this:
char ** p = argv;
hs_init(&argc, &p);
It's unclear why you would need to pass the array by address, but I don't know the API you're using. Double-check the manual to see if those values can be changed by the function and if you need to process them afterwards.
It's wrong because, firstly, it's the wrong type. Taking the address of an array results in a pointer to an array. &argv has type char *(*)[4], i.e. "a pointer to an array of 4 pointers to char". This is not the char *** type that you want. You are forcibly casting it to hide the type incompatibility.
You want a char ***, which is a pointer to an actual char ** variable. But you do not have a char ** variable anywhere. You have argv, a char *[4] variable, which is completely different. An array variable is just that -- a collection of its elements in sequence in memory; there is no address of anything stored anywhere in memory.
Your fundamental confusion might be the fact that arrays are not pointers. Arrays have different sizes (the size of an array is its length times size of its component type), pointer and array types (pointer to an array is very different from from a pointer to a pointer; same with array of arrays and array of pointers), and semantics (arrays cannot be assigned or passed). An array expression can implicitly degrade to a pointer rvalue in some situations; but in this case, you are taking the address, which needs an lvalue, so it does not apply. Never confuse arrays with pointers.

Resources