a pointer about *argv[] - c

This is my main.c
......
int main(int argc, char **argv)
{
init_arg(&argc, &argv);
......
}
This is my init_arg.c
......
void init_arg(int *argc, char ***argv)
{
printf("%s\n", *argv[1]);
......
}
I compiler it with no error and warning.
I run it:
./a.out include
It get Segmentation fault
When I debug it, I found step printf("%s\n", *argv[1]);
get wrong, It show:
print *argv[1]
Cannot access memory at address 0x300402bfd
I want to know, How to print argv[1] in init_arg() function.

You need to add a pair of parentheses around (*argv) to change the order of evaluation. The way you currently have it, the [1] is evaluated first, yielding an invalid pointer, which then gets dereferenced, causing undefined behavior.
printf("%s\n", (*argv)[1]);

Argv is already a pointer. Just pass it like this:
init_arg(&argc, argv);
And init_arg should look like this:
void init_arg(int *argc, char **argv) {
printf("%s\n", argv[1]);
}

I'm assuming that the reason for passing &argc and &argv in the first place is so that you can update them inside init_arg. Here's how I prefer to write such functions, in general:
/*
* init_arg: do something useful with argc and argv, and update argc and argv
* before returning so that the caller can do something else useful that's
* not shared with all the other callers of init_arg().
* (this comment of course needs updating to describe the useful things)
*/
void init_arg(int *argc0, char ***argv0) {
int argc = *argc0;
char **argv = *argv0;
... all the operative code goes here, and then ...
*argc0 = argc;
*argv0 = argv;
}
Of course this means you must not do early returns inside init_arg, so there are some tradeoffs, but it sure is a lot easier to work with the same regular old argc and argv inside init_arg.

Related

Execv with arguments starting from second element

I am making a program that can take another program as an argument and run it.
I am using execv() to run the second program, but how can I use argv[] (that comes from the main program, is a char*) as arguments for execv()?
The first element of argv[] is the name of the main program, so I need to start at the second element where the name of the next program is located.
I am thinking there might be different solutions, for example:
Tell execv() to start from the second argument
copy the content of argv[] (except first element) to another array and use that
Something with pointers so maybe I can point to the second element and start there
What is the best way to do this?
And can I get an example of how to do it?
First of all, the argv is actually a pointer to array of pointers. The more correct way to define it would be char **argv.
Defining it as *argv[] is just a syntactic sugar. So your third guess is a best one:
#include <stdio.h>
void print_array(int array_size, char **array) {
for(int i=0; i<array_size; i++) {
printf("\t%s\n", array[i]);
}
}
int main(int argc, char **argv) {
char **argv_new = argv+1;
printf("argv:\n");
print_array(argc, argv);
printf("argv_new:\n");
print_array(argc-1, argv_new);
return 0;
}
You can also play with interchanging function arguments char **argv vs char *argv[] and you wont see a difference.
But code like:
int main(int argc, char *argv[]) {
char *argv_new[] = argv+1;
will give you an error - of invalid type conversion. That is due to a fact that defining an array on a stack requires to know the size of array during the compilation. But defining array as a function parameter - does not, the compiler in that case presumes that the stack manipulation is done in the caller function.

Is there a better way to fetch this value from "char **"?

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.

c - why convert main() argument to const

(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.

What arguments does a c application like snmpget take?

I want to call snmpget.c from another c program in the same project. For that reason I have changed the main() into a function say get_func() which takes the same arguments. But i an not sure how to give the arguments namely argv[0]
My arguments look something like this:
char *argstr[]=
{
"v",
"1",
"c",
"public",
"-Ovq",
"192.168.1.1",
"ifInOctets.7",
"ifOutOctets.7",
NULL
};
And then
i = get_func(10, argstr);
1.Should argv[0] be the app name or path?
2.Is using char *argstr[] correct for c?
3.snmpget is not taking these arguments correctly. What could the reason be?
It works correctly with the same args in command.
Your get_func expects the arguments starting at argv[1], so your argstr argument should not start with "v" but with something else (e.g. the programme name or just an empty string if get_func doesn’t use it).
Yes. But be aware that your argstr contains non-modifiable strings, if get_func wants to modify them, you can use compound literals
char *argstr[]=
{
(char []){ "v" },
(char []){ "1" },
/* etc */
NULL
};
See 1. and 2. Additionally, argc is incorrect (must be sizeof argstr/sizeof *argstr - 1, which is 8 in your case, not 10).
Not directly an answer to your question, but consider redesigning this (depends on what exactly you’re currently doing, however). For example, write a function accepting a structure where the different options are stored (already parsed and validated) and change the old main from snmpget.c to a function only scanning and validating arguments, initializing such a structure object, and calling this function. And then, perhaps split your files into snmpget.c, snmpget_main.c, another_c_file.c (with better names, of course) and link both user interface implementations against the object file of snmpget.c.
Yes, if your main uses it. If not, just pass NULL is enough >o<
Sure, it's array of pointers. char *argstr[9] is equal to
typedef char *pchar;
pchar argstr[9];
Well, I assume you don't give appropriate argc and don't pass the app name by argv[0] because the argc is 10, but the number of content of argv is 8. (I've counted excluding NULL, but the NULL is required yet - argv[argc] should be NULL.)
To reduce mistakes, I suggest to use sizeof(argstr) / sizeof(argstr[0]) - 1 instead of calculating argc yourself.
See live example. Code:
#include <stdio.h>
int test(int argc, char *argv[]);
int main()
{
char *argstr[] = {
"test.exe",
"--opt-1",
"--output",
"test.txt",
NULL
};
int argcount = sizeof(argstr) / sizeof(argstr[0]) - 1;
return test(argcount, argstr);
}
int test(int argc, char *argv[])
{
int i;
printf("argc: %d\n", argc);
printf("program name: %s\n", argv[0]);
for (i = 1; argv[i] != NULL; i++)
{
printf("argument %d is: %s\n", i, argv[i]);
}
return 0;
}
Output:
argc: 4
program name: test.exe
argument 1 is: --opt-1
argument 2 is: --output
argument 3 is: test.txt

subsequent printf statement ruining unrelated array

In the following code, the printed result for char array dir is gibberish.
However, if I comment out the indicated printf statement, the printed result is intelligible. What is going on here? Thanks. sing code blocks / gcc.
#include <stdio.h>
#include <string.h>
char* handle_input(int argc,char *argv[]){
char dir[200];
printf("Number of arguments: %d\n",argc);
if(argc<2) {
printf("No argument specified\n");
strcpy(dir,"Default argument");
}
else{
printf("Command line directory was specified\n");
++argv;
strcpy(dir,argv[0]);
}
strcat(dir,"_CAT");
return dir;
}
int main(int argc, char *argv[]){
char* dir;
dir = handle_input(argc,argv);
printf("This one messes it up!\n");
printf("%s\n",dir);
printf("DONE\n");
return 0;
}
In your handle_input function, you're returning a local array, which is a huge no-no; after the function ends, that array no longer exists, and what you return is just nonsense. This invokes undefined behavior later in your program. That printf call happens to overwrite the memory which previously belonged to the array. Without the printf call, that memory just happens to be intact, which is why it comes out intelligibly.
The right way to return an array is like this:
char* handle_input(int argc,char *argv[]){
char* dir = malloc(200 * sizeof(char)); // sizeof unnecessary for char,
// but it's a good habit to have
printf("Number of arguments: %d\n",argc);
if(argc<2) {
printf("No argument specified\n");
strcpy(dir,"Default argument");
}
else{
printf("Command line directory was specified\n");
++argv;
strcpy(dir,argv[0]); // This is unsafe! use strncpy instead
}
strcat(dir,"_CAT");
return dir;
}
Using malloc ensures that the memory allocated will continue to exist after the end of the function, and that you will no longer be causing undefined behavior. Note that arrays are somewhat "special"; you can return primitive values (int, char, etc) and structs without having this sort of behavior occur.
You are returning a local char dir[200] in the function handle_input, after the function exits, it's undefined behavior to access it.
Use dynamic memory allocation instead, like:
char* handle_input(int argc,char *argv[]){
char *dir = malloc(200);
//do the processing
return dir;
}
Remember to free it once it's out of use.
You are returning a local vasriable
Either
Use malloc (as you are in the C world with printf)
Pass in a pointer to be filled up

Resources