Need some help with type conversaion with string pointers in C. I have a function that gets the the *argv from main loop to pass command-line parameters to it. Since the parameters are fixed, I am trying to give my own *argv style parameter to it, but gcc everytime gives a warning:
passing argument 2 of ‘DirectFBInit’ from incompatible pointer type
Code:
int main (int argc, char *argv[])
{
...
char *argx[2] = {"self","--dfb:no-vt"};
char *argxPtr = argx;
DFBCHECK (DirectFBInit (&fakeArgc, &argxPtr));
...
}
I should mention that the function is manipulation argv (hence argx).
Here are the definition of DirectFBInit:
DFBResult DirectFBInit(
int *argc, /* pointer to main()'s argc */
char *(*argv[]) /* pointer to main()'s argv */
);
The prog is running but I'm concerned about it.
The web-site for DirectFB probably has useful information.
Your code should probably be:
int main(int argc, char **argv)
{
...
char *argvxData[] = { "self", "--dfb:no-vt", 0 };
char **argvx = argvxData;
int argcx = 2;
DFBCHECK(DirectFBInit(&argcx, &argvx));
...
}
Note the added null pointer to match the null pointer at argv[argc] in the main() program. I've not read the manual to ensure that's required, but consistency is good. When I checked the first edition of my answer, it did not compile without warnings — in fact, it got your warning; I've fixed that with the argvxData array and argvx double pointer. The [] in the prototype is mostly a red-herring, but avoids accusations of being a three-star programmer. It is equivalent to char ***argv.
Or you could pass the arguments to main:
DFBCHECK(DirectFBInit(&argc, &argv));
If we call char * "string" then that simplifies it a bit. The function expects a pointer to an array of strings. You are passing in a string, and an incorrectly-initialized string at that (argx has type char*[] but you are assigning it to a variable of type char*).
&argxPtr should actually be &argx (an expression of type char*(*[]) as expected by the function), and you don't need argxPtr at all.
Something like this?
int myargc = 2;
const char *myargv[] = { "self", "--dfb:no-vt" };
DirectFBInit(&myargc, &myargv);
A simple way to verify is to compile with -g and run gdb. Once in gdb, run the program, and type ptype and you will see the difference. I have replaced the func name with foo()
foo(int *argc, char *(*argv[]))
{
}
int
main (int argc, char *argv[])
{
char *argx[2] = {"self","--dfb:no-vt"};
char *argxPtr = argx;
foo (&argc, &argxPtr);
}
(gdb) ptype foo
type = int (int *, char ***)
(gdb) ptype main
type = int (int, char **)
Hence the warning. Hope this helps.
Related
Okay, I'm trying to build a daemon (for a Debian machine) that will take the command line arguments it receives (via cron) and pass them to different script files.
The daemon's main() is
int main(int argc , char *argv[])
{
if(argc != 3)
{
exit(0);
}
daemonize(argv[1], argv[2]);
return 0;
}
And the function daemonize has this set up
int daemonize(const char *cmd1, const char *cmd2) {...}
The troubling part in daemonize is here:
if (strcmp(cmd1,"sample_script") == 0)
{
static char *argv[] = {"/etc/init.d/sample_script", ["%s",cmd2], NULL };
execv("/etc/init.d/sample_script",argv);
exit(127);
}
On the line
static char *argv[] = {"/etc/init.d/sample_script", ("%s",cmd2), NULL };
I am getting this error
initializer element is not constant (near initialization for ‘argv[1]’)
Obviously ("%s",cmd2) is wrong. Because using "start" works fine.
So, how do I get cmd2 to be correctly put into *argv[]? Or is it something else I am doing wrong?
You need to change
static char *argv[] = {"/etc/init.d/sample_script", ["%s",cmd2], NULL };
to
const char *argv[] = {"/etc/init.d/sample_script", cmd2, NULL };
You have to remove the static keyword. As per chapter 6.7.9, C11 standard,
All the expressions in an initializer for an object that has static or thread storage duration
shall be constant expressions or string literals.
Which says, in C language objects with static storage duration have to be initialized with constant expressions or with aggregate initializers containing constant expressions.
And, regarding constant expression, from chapter 6.6 of the same document
A constant expression can be evaluated during translation rather than runtime, and
accordingly may be used in any place that a constant may be.
So, in C, a variable (name), even if declared as const is never a constant expression.
EDIT:
To resolve the latest issue, you can try the following
change int daemonize(char *cmd1, char *cmd2) {..
use char * const argv[] = {"/etc/init.d/sample_script", cmd2, NULL };
and you can keep the rest of the code unchanged.
The function declaration for execv is
int execv(const char *path, char *const argv[]);
The proper interpretation of the second argument is a "const array of char *", which is different than an "array of const char *". In other words, execv reserves the right to change the contents of the strings that the array points to, but won't change any of the pointers in the array.
Which is to say that all of the strings in the argv array must be in writable memory. So to be perfectly technically correct, the code should make writable copies of all the strings, like so
if (strcmp(cmd1,"sample_script") == 0)
{
char arg0[] = "/etc/init.d/sample_script";
char *arg1 = strdup( cmd2 );
char *argv[] = { arg0, arg1, NULL };
execv( argv[0], argv );
exit(127);
}
you can't initialize a static with a variable/argument only known at runtime.
leave the static out:
char *argv[] = {"/etc/init.d/sample_script", ["%s",cmd2], NULL };
I am just trying to write something like that:
u64 get_env(char *argv[]);
char* g_argv[];
static char * Sample ()
{
return (char*)get_env(g_argv);
}
int main(int argc, char *argv[])
{
g_argv = argv;
Sample();
}
Getting error: 'g_argv' has an incomplete type
warning: array 'g_argv' assumed to have one element [enabled by default]
I've tried many different ways. How to write it right?
The compiler sees you declaring g_argv as an array of char pointers, but you don't specify how many.
int main(int argc, char *argv[])
Despite what it looks like, argv is not an array; arrays aren't first class objects in C and cannot be passed to functions, only their addresses can. So because argv is a parameter, it's actually a pointer to a pointer. For this reason I think it's better to tell the truth and use
int main(int argc, char** argv)
which is exactly equivalent to the above. This confusion in the language has led you to
char* g_argv[];
You're saying this is an array of pointers, without saying how big the array is, but that's not what you want; you want a pointer to the first of several pointers:
char** g_argv;
That fixes the problem you asked about, but I wonder about this declaration:
u64 get_env(char *argv[]);
Why declare it as returning u64 when the name and usage clearly indicate that it returns a char*? Actually, you should not be declaring it here at all ... it should be declared in a header file that specifies the API that includes get_env. Hopefully that header file declares it as returning a char*, and then you can remove the cast from
return (char*)get_env(g_argv);
My c skills are very rusty, so I apologize if this is a dumb question, but I could not even think were to look for the answer to this simple question.
This code compiles without any warnings:
#include <ruby.h>
int run_script( int argc, char *argv[] ) {
ruby_sysinit(&argc, &argv);
}
But when I compile this code, I get the following warning:
#include <ruby.h>
int run_script( char * parameters ) {
int argc=0;
char *argv[1];
ruby_sysinit(&argc, &argv);
}
run_script_3.c: In function 'run_script':
run_script_3.c:7: warning: passing argument 2 of 'ruby_sysinit' from incompatible pointer type
it seems like i am passing the same pointer type in both cases.
The problem is that an array in formal parameter declaration is equivalent to pointer. Declaration of
int run_script( int argc, char *argv[] ) { ... }
is equivalent to
int run_script( int argc, char **argv) { ... }
because you can not send an array as parameter. You can send only a pointer and even if you put an array as actual parameter it is always converted to a pointer. Then if you put &argv you are getting address of this parameter, so it is an address in system stack where argv's value is stored.
On the other hand array in code is still an array, which is different type. In your case &argv is of type char *** inside the first version of run_script while it is of type char *(*)[1] in the second version. It looks like the same type, but it is not. In run time, there is even bigger difference, because your two invocations are sending two completely different values to ruby_sysinit.
Your second example:
int run_script( char * parameters ) {
int argc=0;
char *argv[1];
ruby_sysinit(&argc, &argv);
}
will probably cause segfault because opearator & applied on an array gives the pointer to the first element of the array. So the call ruby_sysinit(&argc, &argv); is sending the same values as ruby_sysinit(&argc, argv);, i.e. a value which points to char*, not to pointer to char* as expected by ruby_sysinit.
The signature of ruby_sysinit is as follows (correct me if I picked the wrong ruby version, I don't have the header file on my system):
void ruby_sysinit(int *argc, char ***argv);
This means that the following compiles:
extern void ruby_sysinit(int *argc, char ***argv);
int run_script(char * params) {
int argc = 0;
char **argv;
ruby_sysinit(&argc, &argv);
}
Now, when you declare char *argv[1], you tell the compiler that this is an array with one element. This is not the same as char **argv.
EDIT: you might find this question on SO and this article (as linked in the other question) useful.
I'm learning about the pointers in C. I don't understand why this code fails during the compilation process.
#include <stdio.h>
void set_error(int *err);
int main(int argc, const char * argv[])
{
const char *err;
set_error(&err);
return 0;
}
void set_error(int *err) {
*err = "Error message";
}
You declare the function to expect a pointer-to-int (int *). But you give it a pointer-to-pointer-to-char and set_error treats it as such. Change the declaration thusly:
void set_error(const char ** err)
If you had compiled with warnings enabled (-Wall for GCC) it would give the following warnings:
In function 'main':
warning: passing argument 1 of 'set_error' from incompatible pointer type [enabled by default]
set_error(&err);
^
note: expected 'int *' but argument is of type 'const char **'
void set_error(int *err);
^
In function 'set_error':
warning: assignment makes integer from pointer without a cast [enabled by default]
*err = "Error message";
^
Your function expects int * type argument but you are passing to it const char ** type argument.
Change your function declaration to
void set_error(const char **err);
The issue you have unearths an important facts about strings in C.
It also raises an interesting fact about scoping.
1. There is no such thing as a string in C; only a pointer to an array of characters.
Therefore, your statement *err = "Error message"; is wrong because by derefencing err you're not getting to the value of the string, but it's first character. (You can't quantify the 'value of a string' in C because there's no such thing as a string in C)
*err is actually undefined because nothing is yet assigned.
Note that the usual definition of a string is const char * or char * so I've changed this from what you had for the note below:
#include <stdio.h>
int main(void){
char * a = "hello";
if (*a == 'h'){
printf("it's an 'H'\n");
}
else{
printf("no it isn't\n");
}
}
You'll see that *err actually returns the value of the first character because a[0] == *a
2. You cannot return pointers to locally scoped data in C
set_error() has the correct intentions, but is doomed to fail. Although "Error message"looks like a value, it is actually already a pointer (because strings in C are pointers to character arrays, as mentioned above).
Therefore, taking (1) into account you might expect to be able to do this:
void set_int(int *myint) {
*myint = 1; //works just fine because 1 is a value, not a reference
}
void set_error(char *err) {
// doesn't work because you're trying to assign a pointer to a char
*err = "Error message";
void set_error_new(char *err) {
//doesn't work because when the function returns, "Error Message" is no longer available on the stack" (assignment works, but when you later try to get at that data, you'll segfault
err = "Error message";
}
You need to take a different approach to how you play with so-called 'strings' in C. Think of them as a pointer to a character array and you'll get better at understanding these issues. Also see C: differences between char pointer and array
One problem is that set_error expects an int * parameter, but you're passing the address of a char *, which makes it a char **. In addition, as noted by #Kninnug there's a buffer overwrite problem here which needs to be dealt with. Try rewriting your code as:
#include <stdio.h>
#include <string.h>
void set_error(char *err, size_t errbuf_size);
int main(int argc, const char * argv[])
{
char err_buf[1000];
set_error(err_buf, sizeof(err_buf));
printf("err_buf = '%s'\n", err_buf);
return 0;
}
void set_error(char *err, size_t errbuf_size) {
strncpy(err, "Error message", errbuf_size-1);
}
As you'll notice in the rewritten version of set_error, another problem is that you can't just assign a value to a pointer and have the target buffer changed - you need to use the string functions from the standard library (here I'm use strncpy to copy the constant "Error message" to the buffer pointed to by the char * variable err). You may want to get familiar with these.
Share and enjoy.
Firstly you have to change your function's declaration to
void set_error(char **err);
The body of the function is the same. Also you declared err variable as const char *err and tried change it. It generates a warning.
Let's start by talking about types. In your main function, you declare err as
const char *err;
and when you call the set_error function, you pass the expression &err, which will have type "pointer to const char *", or const char **.
However, in your function declaration and definition, you declare the parameter err as
int *err;
The types const char ** and int * aren't compatible, which is why the compiler is yakking. C doesn't allow you to assign pointer values of one type to pointer variables of a different type (unless one is a void *, which is a "generic" pointer type). Different pointer types are not guaranteed to have the same size or representation on a particular platform.
So that's where the compiler issue is coming from; what's the solution?
In C, string literals like "Error message" have type char *1 (const char * in C++), so whatever I assign it to needs to have a type of either char * or const char *. Since we're dealing with a string literal, the latter is preferable (attempting to modify the contents of a string literal invokes undefined behavior; some platforms put string literals in read-only memory, some don't). So you need to make the following changes to your code2:
void set_error( const char **err )
{
*err = "Error message";
}
int main( void ) // you're not dealing with command line arguments, so for this
{ // exercise you can use `void` for your parameter list
const char *err;
set_error( &err );
return 0;
}
Remember that C passes all function arguments by value; this means that the formal parameter err in set_error is a different object in memory than the actual parameter err in main; if the code had been
void set_error( const char *err )
{
err = "Error message";
}
int main( void )
{
const char *err;
set_error( err );
return 0;
}
then the change to err in set_error would not be reflected in the variable err in main. If we want set_error to modify the value of err in main, we need to pass set_error a pointer to err and dereference it in the function. Since the parameter err has type const char **, the expression *err has type const char *, which is the type we need for this assignment to succeed.
1. Actually, that's not true; string literals have type "N-element array of char", where N is the number of characters in the string plus the 0 terminator. However, for reasons that aren't really worth going into here, the compiler will convert expressions of array type to expressions of pointer type in most circumstances. In this case, the string literal "Error message" is converted from an expression of type "14-element array of char" to "pointer to char".
2. A function definition also serves as a declaration; I typically put the called function before the caller so I don't have to mess with separate declarations. It means my code reads "backwards" or from the bottom up, but it saves some maintenance headaches.
1st error--> You are noticing is due to the fact that your function expects a pointer to int and you are passing a pointer to const char
2nd error--> You dereferenced the pointer and inserted the value "Error Message" which is a string and you pointer was pointer to char.
3rd error--> set_error(&err); --> This statement is wrong as err itself stores an address so there is no need to put & putting & means you are passing the address of the pointer *err and not the address which it is holding. So try this.
include <stdio.h>
void set_error(const char* err[]); //Function Declaration
int main()
{
const char* err[1000];
set_error(err);
printf("%s",*err);
return 0;
}
void set_error(const char* err[])
{
*err = "Error Message";
}
I happen to have several functions which access different arguments of the program through the argv[] array. Right now, those functions are nested inside the main() function because of a language extension the compiler provides to allow such structures.
I would like to get rid of the nested functions so that interoperability is possible without depending on a language extension.
First of all I thought of an array pointer which I would point to argv[] once the program starts, this variable would be outside of the main() function and declared before the functions so that it could be used by them.
So I declared such a pointer as follows:
char *(*name)[];
Which should be a pointer to an array of pointers to characters. However, when I try to point it to argv[] I get a warning on an assignment from an incompatible pointer type:
name = &argv;
What could be the problem? Do you think of another way to access the argv[] array from outside the main() function?
char ** name;
...
name = argv;
will do the trick :)
you see char *(*name) [] is a pointer to array of pointers to char. Whereas your function argument argv has type pointer to pointer to char, and therefore &argv has type pointer to pointer to pointer to char. Why? Because when you declare a function to take an array it is the same for the compiler as a function taking a pointer. That is,
void f(char* a[]);
void f(char** a);
void f(char* a[4]);
are absolutely identical equivalent declarations. Not that an array is a pointer, but as a function argument it is
HTH
This should work,
char **global_argv;
int f(){
printf("%s\n", global_argv[0]);
}
int main(int argc, char *argv[]){
global_argv = argv;
f();
}
#include <stdio.h>
int foo(int pArgc, char **pArgv);
int foo(int pArgc, char **pArgv) {
int argIdx;
/* do stuff with pArgv[] elements, e.g. */
for (argIdx = 0; argIdx < pArgc; argIdx++)
fprintf(stderr, "%s\n", pArgv[argIdx]);
return 0;
}
int main(int argc, char **argv) {
foo(argc, argv);
}