I have a library which has its own command-line arguments parsing mechanism when the library is compiled as stand alone binary and here is the code:
int main( int argc, char **argv )
{
int result;
argc = wool_init( argc, argv );
....
}
int wool_init( int argc, char **argv )
{
us_elapsed();
argc = decode_options( argc, argv );
.....
}
Now I am linking this library statically into another one which has its own Command-line parsing mechanism and arguments. I would like to initialize this first library with some arguments at run-time for example I am passing these arguments as follows to mimic command-line:
/* initializing wool run-time environment */
char **woolArg;
*woolArg[0] = "-p 3";
wool_init(1, woolArg);
But I am getting following error.
:113:14: error: assignment makes integer from pointer without a cast [-Werror]
*woolArg[0] = "-p 3";
^
Can somebody please help?
Let's count the stars.
char **woolArg;
Two stars at declaration.
woolArg[0]
This uses up one star. We're left with a char*.
*woolArg[0]
This uses up another star. We're left with a char.
*woolArg[0] = "-p 3";
There is a char* on the right side of the assignment. char and char* are two very different things. You may want to try
woolArg[0] = "-p 3";
... except it won't work, because woolArg is uninitialized and so you cannot touch woolArg[0]. But instead of trying to fix that, only to get stuck at the next problem, and then the next one and one after that, learn the simple and correct form:
char* woolArg[] = { "library", "-p", "3", NULL };
The first string is arbitrary. You can use argv[0] here if it is accessible.
woolArg = malloc(sizeof(char *) * 3/*number of args*/);
woolArg[0] = /*program name*/;
woolArg[1] = "-p";
woolArg[2] = "3";
Let's put the memory management issues aside, other answers here already deal with that. Let's explain the error message. Your statement is:
*woolArg[0] = "-p 3";
What are the types?
woolArg is a char **
*woolArg is a char *
*woolArg[0] is a char
"-p 3" is a char[] but decays into a char *
So the left side of your assignment expects a char, but the right side provides a char *. Hence the warning/error that you are implicitly turning a pointer into an integer.
To fix this, you need get rid of an indirection:
woolArg[0] = "-p 3";
Related
I'm studying C in order to start doing some fun low-level code stuff, and I've stumbled into a scenario which I can't wrap my head around, and I'm sure it's because I don't have a lot of experience with it.
Currently my code is very simple: it takes some arguments, and gets the first parameter passed to main, and stores it as a path string. The first question that came to mind, was whether it would be correct to store the main params as char *args[] or char **args, and I decided to go with char **args since according to this question there could be some scenarios where the first would not be accessible, and I just wanted to make a code that would be as complete as possible, and learn the whys on the process.
Here's is the code:
int main(int argc, char **args) {
if (args[1] == NULL) return 1;
// Get path of input file
char *path = &*args[1];
fputs(path, stdout);
return 0;
}
Given the code above, what would be a better way of fetching the value stored in *args[1]? It seems very cryptic when I look at it, and it took me a while to get to it as well.
My understanding is that char **args, is a pointer, to an array of pointers. Thus, if I'm to store a string or any other value for later use in one of the indexes of args, I would have to assign a new pointer to a memory location (*path), and assign the value of the given index to it (&*args[i]). Am I over complicating things? Or is this thought process correct?
For starters these two function declarations
int main(int argc, char **args)
and
int main(int argc, char *args[])
are fully equivalent because the compiler adjusts the parameter that has an array type to pointer to the array element type.
In the initializer expression of this declaration
char *path = &*args[1];
applying the two operators & and * sequentially is redundant. So you may just write
char *path = args[1];
Also in general instead of the condition in the if statement
if ( args[1] == NULL) return 1;
it will be more safer to write
if ( argc < 2 ) return 1;
You can simply write:
char *path = args[1];
& and * operators are inverses of each other, so &* or *& can simply be removed from an expression.
I am doing a word search program and keep getting the same error that doesn't give me much information about whats wrong. Specifically it says this...
wordSearch.c:38:32: error: expected
expression
returnWord = (char *) strstr(char const *sentence, char const *phrase);
^
^
what could this be?
returnWord = char *strstr(const char *sentence, const char *phrase);
is not how you call a function. Get rid of the return type, simply use
returnWord = strstr(sentence, phrase);
assuming sentence and phrase are variables are defined and having proper values.
Based on the pictures, it looks as if something is wrong with strstr. This makes sense because of the way you are passing arguments. strstr expects two const char * arguments, however you have casted them incorrectly. Additionally, since strstr already returns a char * there is no need to cast that. Thus, line 38 should be returnword = strstr((const char *) sentence, (const char *) phrase);
This is the prototype for execv:
int execv(const char *path, char *const argv[]);
Can I pass an array of const char pointers as the second argument?
This example program gives a warning when USE_CAST is not set:
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc > 0) {
const char *exe_name = "/bin/echo", *message = "You ran";
const char *exe_args[] = { exe_name, message, argv[0], NULL };
#ifdef USE_CAST
execv("/bin/echo", (char **) exe_args);
#else
execv("/bin/echo", exe_args);
#endif
}
return 0;
}
When compiling, gcc says, "passing argument 2 of 'execv' from incompatible pointer type" if I don't use the cast.
From the POSIX documentation for execv (halfway through the Rationale section), it looks like the second argument is a char *const array only for backwards compatibility:
The statement about argv[] and envp[] being constants is included to make explicit to future writers of language bindings that these objects are completely constant. ... It is unfortunate that the fourth column cannot be used...
where the "fourth column" refers to const char* const[].
Is the (char **) cast safe to use here? Should I create a char * array and pass that to execv instead?
Can I pass an array of const char pointers as the second argument?
Well yes, you already know that you can cast in order to do so.
From the POSIX documentation for execv (halfway through the Rationale section), it looks like the second argument is a char *const array only for backwards compatibility:
I wouldn't put it in those terms, but yes, there is a compatibility aspect to the chosen signature. The section you reference explains that C does not have a wholly satisfactory way to express the degree of const-ness that POSIX requires execv() to provide for the arguments. POSIX guarantees that the function will not change either the pointers in argv or the strings to which they point.
With that being the case, I think it not unreasonable to cast the argv pointer as you propose to do, though I would leave a comment in my code explaining why doing so is safe.
On the other hand, you should consider simply leaving the const off of your array declaration:
char *exe_name = "echo", *message = "You ran";
char *exe_args[] = { exe_name, message, argv[0], NULL };
Or, in your simple example, even this would do:
char *exe_args[] = { "echo", message, argv[0], "You ran", NULL };
C string literals correspond to arrays of type char, not const char, so this is perfectly legal as far as C is concerned, even though actually trying to modify the contents of those strings might fail.
On the third hand, modern C has array literals, so you could even do this:
execv("/bin/echo", (char *[]) { "echo", "You ran ", argv[0], NULL });
In that last case you don't even have a cast (the thing that resembles one is just part of the syntax for an array literal).
If you're just going to cast away the const, then you shouldn't use const to begin with. Most (dare I say all) compilers will accept the following code
char *exe_name = "/bin/echo";
char *message = "You ran";
char *exe_args[] = { exe_name, message, argv[0], NULL };
execv( exe_args[0], exe_args );
If that is not pedantically correct enough for you, then the other option is
char exe_name[] = "/bin/echo";
char message[] = "You ran";
char *exe_args[] = { exe_name, message, argv[0], NULL };
execv( exe_args[0], exe_args );
Note that execv is going to make copies of the strings (to create the argv array for the executable), so it doesn't matter whether the strings are actually const or not.
(I am a beginner in C, maybe my question is not very smart, but I did google before I ask.)
I saw following code in git source code:
int main(int argc, char **av) {
const char **argv = (const char **) av;
// ... more code ...
}
It converts char **av to const char **argv, I thought it meant to make the argument immutable, but I wrote a program and found that both argv and argv[i] are mutable.
Question 1: What is the purpose & goodness of that line of code?
Question 2: What is the behavior of a const pointer? I did google but didn't find a good answer.
#Update
I test more according to the answers, and it seems that argv[i][j] is immutable, but argv and argv[i] is mutable.
So the const on pointer makes the original value immutable, but the pointer itself is still mutable.
Thus I guess the major purpose of the code from git is also to prevent change of the original arguments.
testing code:
#include <stdio.h>
int main(int argc, char * av[]) {
// modify value pointed by a non-const pointer - ok
av[0][0] = 'h';
printf("argv[0] = %s\n", av[0]);
// modify const pointer itself - ok
const char **argv = (const char **) av;
argv[0] = "fake";
printf("argv[0] = %s\n", argv[0]);
char *arr[] = {"how", "are", "you"};
argv = (const char **)arr;
printf("argv[0] = %s\n", argv[0]);
// modify the value itself which is pointed by a const pointer - bad, an error will be thrown,
/*
argv[0][0] = 'x';
printf("argv[0] = %s\n", argv[0]);
*/
return 0;
}
The current code could compile & run without warning or error, but if un-comment the 2 commented lines at end, then it will throw following error when compile:
error: assignment of read-only location ‘**argv’
In practice it is not very useful here (and the generated code won't change much, if the compiler is optimizing).
However, argv is not mutable, so the compiler would for instance catch as an error an assignment like
argv[1][0] = '_'; // wrong
A const thing cannot be assigned to. So a const pointer can't be assigned, and a pointer to const means that the dereferenced pointer is a location which cannot be assigned. (and you can mix both: having a const pointer to const)
BTW, main -in standard C99- is a very special function. You cannot declare it in arbitrary ways (it almost always should be declared int main(int, char**) or int main(void) ....) and you perhaps cannot call it (e.g. it cannot be recursive), but that may be different in C and in C++. So declaring int main (int, const char**) would be illegal.
1) There is really no point in using a const pointer to access the parameters later on, except to make sure they are not changed.
2) The purpose of const pointers is to make sure that they are not changed throughout the code. You can live without them, but it helps avoiding bugs.
so i have a struct call Process_Info
struct Process_Info {
char name[128];
int pid;
int parent_pid;
int priority;
int status;
};
and an array of Process_Info call info. I set pid in info to an integer, it works but when I try to set name in info to "{kernel}" like this
info[i].name="{kernel}";
and it give me incompatible type in assignment error. I search online it seem i can do this, like in http://www.cs.bu.edu/teaching/cpp/string/array-vs-ptr/, they did char label[] = "Single"; So what am i doing wrong?
The short answer: A C compiler will bake constant strings into the binary, so you need to use strncpy (or strcpy if you aren't worried about security) to copy "{kernel}" into info[i].name.
The longer answer: Whenever you write
char label[] = "Single";
the C compiler will bake the string "Single" into the binary it produces, and make label into a pointer to that string. In C language terms, "Single" is of type const char * and thus cannot be changed in any way. However, you cannot assign a const char * to a char *, since a char * can be modified.
In other words, you cannot write
char label[] = "Single";
label[0] = "T";
because the compiler won't allow the second line. However, you can change info[i].name by writing something like
info[i].name[0] = '[';
because info[i].name if of type char *. To solve this problem, you should use strncpy (I referenced a manual page above) to copy the string "{Kernel}" into info[i].name as
strncpy(info[i].name, "{Kernel}", 256);
info[i].name[255] = '\0';
which will ensure that you don't overflow the buffer.
I think you may be mistaken. The way you would assign this would be one char at a time like so...
name[] = {'a','b','c','d'};