I'm trying to wrap my head around the different qualifiers for an array of pointers, or pointer to a pointer. What would be the difference between the following three declarations (the last one of which I think is wrong, but I'm not sure why):
char ** strings;
const * char * strings / char * const * strings (?);
const char ** strings;
When would one be used over another? And why does the third one (I think?) not make sense at all?
The const qualifier marks an object as immutable, that is "read-only", but you knew that most probably.
char ** strings;
strings is a mutable pointer to a mutable pointer to a mutable character. None of them is const obviously.
const * char * strings;
is syntactically wrong.
char * const * strings;
strings is a mutable pointer to an immutable pointer to a mutable character.
const char ** strings;
strings is a mutable pointer to a mutable pointer to an immutable character.
This makes sense if you need such a variable.
Related
I have set a structure type:
typedef struct {
char *snt[MAX_LINE_LENGTH];
} sentence;
And this line is getting a cast specifies array type error:
sentence copySentence(sentence *source) {
sentence nw;
nw.snt = (char *[])source->snt; //Here is the error
return nw;
}
What is the best fix for this code line and what is the problem?
Both nw.snt and source->snt are arrays of pointer. To "deep copy" the whole array, you may want to use memmove(nw.snt, source->snt, MAX_LINE_LENGTH * sizeof (char *));.
Also, people usually prefer passing a pointer to a struct than pass that struct directly to reduce the cost of argument passing. In this case, you can
sentence *copySentence(sentence *source) {
sentence *nw;
nw = malloc(sizeof (struct sentence));
memmove(nw.snt, source->snt, MAX_LINE_LENGTH * sizeof (char *));
return nw;
}
You are declaring snt as an array of pointers to charactes. You probably mean it t be an array of charactes, or a pointer to an array of characters:
char snt[MAX_LINE_LENGTH]; // array of characters to hold your sentence
char *snt; // pointer to array of characters
When you assign an element to a compatible element, there is no need for a cast and casting is here considered harmful because you prevent the compiler from giving you warnings. Note that you do not copy the characters, you only make two structs point to the same sentence.
I leave fixing this to you, as an excercise.
I'm attempting to run execvp using the data from a char[][] type (aka an array of strings). Now I know that execvp() takes a pointer to a string as its first parameter and then a pointer to an array of strings as its second - in fact I have even used it successfully before as such - however I cannot seem to get the correct combination of pointers & strings to get it to work out below - whatever I try is deemed incompatible!
Any help very grateful :) - I've removed my headers to compact down the code a bit!
struct userinput {
char anyargs[30][30]; //The tokenised command
};
int main() {
struct userinput input = { { { 0 } } }; //I believe is valid to set input to 0's
struct userinput *inPtr = &input; //Pointer to input (direct access will be unavailable)
strcpy(inPtr->anyargs[0], "ls"); //Hard code anyargs to arbitary values
strcpy(inPtr->anyargs[1], "-lh");
char (*arrPointer)[30]; //Pointer to an array of char *
arrPointer = &(inPtr->anyargs[0]);
printf("arrPointer[0]: %s, arrPointer[1]: %s\n", arrPointer[0],
arrPointer[1]);
printf("At exec case; ");
execvp( arrPointer[0], arrPointer);
perror("Command not recognised"); //Prints string then error message from errno
return 0;
}
There is no such thing as char[][] in C. execvp requires an array of pointers to const char. This can be written as either char * const * or char * const [].
You however have an array of 30-characters-long arrays, not an array of pointers. The two types are not compatible, not interchangeable, and not convertible one to another in either direction.
In this line
char (*arrPointer)[30]; //Pointer to an array of char *
you attempt to declare a pointer to an array of char*, incorrectly. What you have declared instead is a pointer to char[30], which is very different from what execvp expects.
The next line
arrPointer = &(inPtr->anyargs[0]);
purports to initialize a pointer to an array of char* with a pointer to char[30], which cannot possibly be correct even if you declare a pointer to an array of char*, because the right hand side of the assignment is not a pointer to an array of char*, it's a pointer to char[30] and no sequence of casts, indices, addresses and dereferences will turn one to the other.
An array of 30 pointers to char is declared like this:
char* arguments[30];
A dynamically-sized array of pointers to char is made like this:
char** arguments = calloc (nargs, sizeof(char*));
You need to use one of those if you want to call execvp.
In either case each pointer in the array of pointers must be initialized to point to an individual NUL-terminated character array (possibly to elements of your char[30][30] array) and the last pointer (one after all the argumenrs we want to pass) must be set to NULL. (I wonder how you expected to find a NULL in a char[30][30]).
The execvp() expects as second argument a char *const argv[]. This means an array of pointers to char. This is different from a char[30][30] which is represented in memory as 30x30 contiguous chars (so no pointer).
To solve this, define your structure
struct userinput {
char *anyargs[30]; //space for 30 char* pointers
};
You could as well define anyargs as char** and initalize if dynamically with (char**)calloc(number_of_args+1,sizeof(char*))
Later, assign directly the pointers:
inPtr->anyargs[0] = "ls"; //Hard code (or use strdup() )
inPtr->anyargs[1] = "-lh";
inPtr->anyargs[2] = NULL; // end of the argument list !!!
char **arrPointer; //Pointer to an array of char *
arrPointer = inPtr->anyargs;
Edit: Caution: "The array of pointers must be terminated by a NULL pointer.".
I have a C code that creates an array of char pointers as the following:
char* arr[100];
I use each element in this array to point to some string that is being calculated by another function. So basically arr[0] would point to string1 and arr[1] to string2, etc.
This works just fine. However I'm now asked to be more flexible by having the user to specify the number of strings as a parameter.
How can I do this with minimal changes to my code? I understand that I need to use malloc. However I'm getting a lot of warnings in all of the assignment statements I had before. I changed the declaration of the array as the following:
char* arr = (char*)malloc(n * sizeof(char*)); //where n is provided by user
I thought that I only needed to change the declaration. Now all of the assignment statements are giving warnings ("assignment makes integer from pointer without a cast"). The following is an example of an assignment statement:
arr[i] = str; //where str is defined as char* and is calculated by another function
Am I missing something here?
If you're looking to create an array of char *, you need a char **arr. Think of it as an array of char * -- if you had an array of int, you would have int *. Since you have an array of char *s, you need char **.
char** arr = malloc(n * sizeof(char*));
You are declaring arr as a pointer to char: either a single string or, if you prefer, an array of chars.
To allocate an array of pointers, declare arr as
char **arr = malloc(n * sizeof(char *));
By the way, remove the cast: it is unnecessary in C. See also Question 7.7 in the comp.lang.c FAQ.
Don't forget that string is an array too (char *), so u will need array of pointers, which should look like this:
char** arr = (char**)malloc(n * sizeof(char*));
you want to declare arr as as char ** since you are pointing to an array of pointers. If you declare arr only as char * (not char ** or char *[]) you only have one "string".
I'm learning the C language.
My question is:
Why is the param of strlen a "const" ?
size_t strlen(const char * string);
I'm thinking it's because string is an address so it doesn't change after initialization. If this is right, does that mean every time you build a function using a pointer as a param, it should be set to a constant ?
Like if I decide to build a function that sets an int variable to its double, should it be defined as:
void timesTwo(const int *num)
{
*num *= 2;
}
or
void timesTwo(int *num)
{
*num *= 2;
}
Or does it make no difference at all ?
C string is a pointer to a zero-terminated sequence of characters. const in front of char * indicates to the compiler and to the programmer calling the function that strlen is not going to modify the data pointed to by the string pointer.
This point is easier to understand when you look at strcpy:
char * strcpy ( char * destination, const char * source );
its second argument is const, but its first argument is not. This tells the programmer that the data pointed to by the first pointer may be modified by the function, while the data pointed to by the second pointer will remain constant upon return from strcpy.
The parameter to strlen function is a pointer to a const because the function is not expected to change what the pointer points to - it is only expected to do something with the string without altering it.
In your function 'timesTwo', if you intend to change the value that 'num' points to, you should not pass it in as a pointer-to-const. So, use the second example.
Basically, the function is promising that it will not modify the contents of of the input string through that pointer; it says that the expression *string may not be written to.
Here are the various permutations of the const qualifier:
const char *s; // s may be modified, *s may not be modified
char const *s; // same as above
char * const s; // s may not be modified, *s may be modified
const char * const s; // neither s nor *s may be modified
char const * const s; // same as above
Do excuse me to the basic"ness" of this question. I am at a loss with pointers at times. I have a char * but I need to convert it to a char * const * to be able to correctly use it in the fts() function. How do i do that?
Thanks
You are not supposed to do that kind of conversion, because the types are not compatible.
About pointers and pointers to pointers
char * is a pointer to a string of characters, whereas char ** is an pointer to a pointer to a string of characters. (the const is a bonus). This probably means that instead of supplying a string of characters, you should provide an array of string of characters.
Those two things are clearly incompatible. Don't mix them with a cast.
About the fts_* API
To find the solution to your problem, we need to read the fts_* function API (e.g. at http://linux.die.net/man/3/fts), I see that:
FTS *fts_open(char * const *path_argv, int options,
int (*compar)(const FTSENT **, const FTSENT **));
with your char * const * parameter path_argv, the description explains:
[...] If the compar() argument is NULL, the directory traversal order is in the order listed in path_argv for the root paths [...]
which confirms that the fts_open function is really expected a collection of paths, not one only path.
So I guess you need to pass to it something like the following:
char *p[] = { "/my/path", "/my/other/path", "/another/path", NULL } ;
About the const
Types in C and C++ are read from right to left. So if you have:
char * : pointer to char
char const * : pointer to const char (i.e. you can't modify the pointed string, but you can modify the pointer)
const char * : the same as char const *
char * const : const pointer to char (i.e. you can modify the pointed string, but you can't modify the pointer)
char ** : pointer to pointer to char
char * const * : pointer to const pointer to char (i.e. you can modify the pointer, and you can modify the strings of char, but you can't modify the intermediary pointer
It can be confusing, but reading them in the right-to-left order will be clear once you are more familiar with pointers (and if you programming in C or C++, you want to become familiar with pointers).
If we go back to the initial example (which sends a bunch of warnings on gcc with C99) :
char ** p = { "/my/path", "/my/other/path", "/another/path", NULL } ;
I played with the API, and you can feed it your paths two ways:
char * p0 = "/my/path" ;
char * p1 = "/my/other/path" ;
char * p2 = "/another/path" ;
/* with a fixed-size array */
char * pp[] = {p0, p1, p2, NULL} ;
FTS * fts_result = fts_open(pp, 0, NULL);
Edit 2011-11-10: snogglethorpe rightfully commented this solution is not a C89 valid solution, even if it compiles successfully with gcc (excluding pendantic + C89 flags). See Error: initializer element is not computable at load time for more info on that
or:
/* with a malloc-ed array */
char ** pp = malloc(4 * sizeof(char *)) ;
pp[0] = p0 ;
pp[1] = p1 ;
pp[2] = p2 ;
pp[3] = NULL ;
FTS * fts_result2 = fts_open(pp, 0, NULL);
free(pp) ;
Edit
After reading others answers, only two of them (mkb and moshbear) avoid the "just cast the data" error.
In my own answer, I forgot the NULL terminator for the array (but then I don't know the Linux API, nor the fts_* class of functions, so...)
I'm assuming you're talking about fts_open:
FTS *fts_open(char * const *path_argv, int options,
int (*compar)(const FTSENT **, const FTSENT **));
What it's wanting of you is an array of const char* pointers, ie. an array of strings. The const is there just to tell you that it's not going to modify your strings, and gives you the opportunity to pass const strings.
Non-const variables can be treated as const, however you shouldn't usually treat them the other way around.
The first argument is just like argv passed to main, you could just have:
char *path_argv[] = { "/first_path/", "/second_path/", NULL };
It's important that the last element is NULL to indicate the end of the array.
Note also that path_argv could also be declared as:
char **path_argv
OR*
char * const *path_argv
Any of these are suitable types to be passed as the first argument to fts_open. You do, however, obviously have to initialize it differently than the above, but those are other ways you can declare path_argv. I made that unclear previously.
And then just fts_open(path_argv, options, compar), where options is your options, and compar is your compare function.
You need to make a second array, which is NULL-terminated (because fts()'s first argument is argv).
E.g.
char *const buf2[2] = { buf, NULL };
fts(buf2);