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.
Related
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.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have this code:
char** SplitToWords(char* str);
int main()
{
char** wordarr;
char str[] = "This is a sentence";
wordarr = SplitToWords(str);
return 0;
}
After the main comes the function implementation.
I am not sure the following does what I want it to do (i.e. receive an array of strings from a function):
wordarr = SplitToWords(str);
I somehow managed to convince the compiler that it's ok, but I assume it just does something else.
If it does, how do I find out the length of the array (the number of strings in it).
Thanks
I'll try to quickly visit all aspects you might not yet fully understand:
A string in C is described as a contiguous sequence of chars, ending with a char of value 0 (as a literal: '\0'). It is not a first class object, therefore hasn't its own type. So what you use to hold a string is an array of char. Therefore, taking your question by the word, "receive an array of strings from a function" is not possible.
An array is a contiguous sequence of objects of the same type. In C, the identifier of an array doesn't have a value itself; when it's evaluated, it decays as a pointer to the array's first element instead. This is especially important when passing arrays to functions or returning them from functions -- you can't actually pass the array, you always pass a pointer
e.g. you could write:
char x[] = "foo"; // initialize a char array from a string literal
char *xp = x; // here, x evaluates as a pointer to the first element of the array
You already use pointer types for your function's argument and return value, I just think it's quite important to understand what happens entirely.
You write char** SplitToWords(char* str); and ask whether this returns an "array of strings" -- well, sort of, as you should understand after reading 1. and 2. -- What it does is returning a pointer to char *. This pointer could be a pointer to the first element of an array. So in this case, it would return a pointer to an array of char * pointers. Each of these pointers could itself be a pointer to an array of chars, therefore point to a string. But what's very important is to understand you never return an array, you always return a pointer to it. It's so important because:
You might get the idea to do something like this:
char** SplitToWords(char* str)
{
char *words[16];
// code to fill `words` with pointers to the actual words
return words; // WRONG!
}
Here, because you're not returning the array words but a pointer to it (see point 2), you return a pointer to an object that no longer exists. words is in the scope of your function and has automatic storage duration, that means it only lives as long as the execution is inside of the function. One solution would be to declare words with the static storage class specifier. This way, it lives for the entire execution time of the program. But be aware that this also means there's only a single instance ever, it's always the same object. This will be a major headache for threaded programs, for example. The other way around is to dynamically allocate words using malloc(). But then, the caller of the function must free() it later.
As for your second question, how to let the caller know the number of words -- it's in the comments already, but just for completeness, a typical approach to solve this is to append another entry that is a NULL pointer. So the caller can iterate over the pointers until it finds NULL.
Regarding your comment, of course you can create the array outside the function and pass a pointer to the function, so the function only fills it. This is a common idiom in C (e.g. think about fgets(), which takes a pointer to the char array that's filled with a string by the function).
Functions working this way will need an additional size_t parameter, so they know the size of the array they should fill through the pointer, otherwise you'd have the risk of buffer overflows (this is why gets() was finally removed from the C standard). If you decide that the caller provides the storage, your function should have this prototype:
// returns the number of words found, up to `nwords`
size_t SplitToTwords(char **words, size_t nwords, char *str);
It should be called e.g. like this:
char *words[16];
size_t nwords = SplitToWords(words, 16, "the quick brown fox"); // returns 4
Remember that the strings holding the words themselves need storage as well. You can either manipulate the bytes in str to insert a '\0' after each word, overwriting the first whitespace character (this is what strtok() does) or you can copy the words to new strings, but then you would have to malloc() each of them again and the caller has to free() them later.
Yes, you could solve it by using a function with return value char **. However, there's no way to find out how many words there are afterwards.
You can solve this by allocating one more element for the return pointer and set it to NULL. Then you can get the number of words with this code:
wordarr = SplitToWords(str);
char **ptr=wordarr;
int noWords=0;
while(!*(ptr+noWords))
noWords++;
But if you want to return multiple data in C, you either need to define a return struct or using return arguments. In this case, it could look like this for the first option:
typedef struct wordList {
char **wordarr;
int noWords;
}
wordList SplitToWords(char* str);
And the second:
char** SplitToWords(char* str, int *noWords);
or
void SplitToWords(char* str, char*** wordarr, int *noWords);
Note that there's three *. That's because we want it to be a pointer to char **
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXSTRINGS 5000
int main(int argc, char *argv[]) {
char *stringTable[MAXSTRINGS];
char sentence[] = "This is a sentence";
char *token = NULL;
int i = 0;
while ((token = strtok(token == NULL ? sentence : NULL, " ")) != NULL)
{
printf("%s\n\r", token);
stringTable[i] = (char *)malloc(strlen(token) + 1); //have no "plain" C compiler - VS C++ used so cast needed :)
strcpy(stringTable[i++], token);
}
stringTable[i] = NULL; // if you need to iterate through later
printf("%d tokens found\n\r", i);
for (int y = 0; y < i; y++)
free(stringTable[y]);
}
I know this was asked a few times, but I still do not fully understand the issue.
I have an assigment where I need to save and sort the arguments.
int main(int argc, char* argv[])
{
int c;
char* s = malloc(argc * sizeof(char));
extern char *optarg;
extern int optind;
extern int optopt;
while ( (c = getopt(argc, argv, ":adh")) != -1) {
s[argc] = argv;
switch (c) {
case 'a': printf("a\n");
break;
case 'd': printf("d\n");
break;
case 'h': printf("h\n");
break;
}
}
return 0;
}
I know that it has something to do with me saving a pointer to an integer or not doing it.
char* argv[] is what? An array of pointers?
And s is only an array of chars?
char* argv[] is what? An array of pointers?
When such a declaration appears in a function prototype, as in your code, it is equivalent to char **argv. That's a pointer to a pointer to char. In this particular case, the pointed-to pointer is the first element of an array of pointers.
And s is only an array of
chars?
As you have declared an initialized it, s is a pointer to char, and in particular, it points to the first char in a dynamically allocated block. You can use the block via s as if it were an array of char, but it is not technically an array, and s definitely is not an array. Although related, pointers and arrays are very different things.
The compiler should have told you at least on which line the error occurs, but you did not pass that information on to us. Nevertheless, I think I can guess. This line ...
s[argc] = argv;
... is nonsense. Since s is a pointer to char, s[argc] designates one char (and in C, char is among the integer data types). On the other hand, argv is a pointer (to pointer to char). Although C permits pointers to be converted to integers, it is rarely useful to do so, and anyway, a conforming program must use a cast to perform such a conversion. Since you don't actually use s for anything, your best bet is probably to just remove that line.
For what it's worth, if you wanted to set s to point to one of the command line arguments, say the second, the syntax would be
s = argv[2];
(The zeroth element of argv is conventionally the name of the program; the arguments start at index 1.) But if you intend to do that then you don't need to malloc() any memory for s; whatever you do allocate for it is leaked when you assign a new value to it.
char *argv[] is a pointer to an array of strings. AKA char **argv
int argc is an integer count of the number of strings on the command line, including exe name.
Note also that the length of each string in argv[] has nothing to do with the value of argc. The strings can be as short as two characters, such as a\0 or as long as (or longer than) 1000 characters.
So the statement char* s = malloc(argc * sizeof(char)); will not do what you are thinking it will do.
First it is the form of an expression that will create some space for only one string, not argc of them.
Second, if your command line included 3 item, ( argc == 3 ) and one of the arguments was a string of length 9, say arguments, then your malloc statement would create space for 1 string, with only 3 characters. Not big enough to contain the string `arguments\0', not to mention the other two.
Use something like the following to determine the length of the longest of the argc strings:
int len = 0, lenKeep = 0;
for(i=0;i<argc;i++)
{
len = strlen(argv[i] > len)
if(len > lenKeep) lenKeep = len;
}
Now, lenKeep + 1 has been established as the length needed to contain the longest string (+ 1 for the NULL character, which strlen(...) does not include in its count.). This can be used, along with knowing the number of strings, argc, to allocate memory for each of them.
For example:
int main(int argc, char* argv[])
{
...
char **string = Create2DStr(argc, lenKeep + 1);//will create space
//sufficient to contain
//strings of your command line
...
}
Where Create2DStr(...) could be defined as:
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
(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.
I've been trying this for about an hour and looked at other places online but still was not able to accomplish what I'm trying to do. This should probably be something simple but I have not coded in C in quite awhile so I may be missing something. Anyway I want to use strcmp to compare something in a variable called char *args and a literal like "qw".
I am in main and pass a function this args variable and when I get it back I want to compare like this: Maybe I am just completely doing this in a dumb way.
char *args;
char **svdArray;
int keepTrack = 0;
int beforeTrack = 0;
svdArray = malloc(5*sizeof(char*));
while (1)
{
if(!(strcmp(args[0], "r")) && (!strcmp(args[0], "rr")))
{
svdArray[keepTrack] = args[0];
keepTrack++;
}
All I want to happen is if I args[0] has something in it besides rr or r I want to execute the code inside the if statement. However as of now the flow just never enters it and I don't know why.
Any help would greatly be appreciated!!!
strcmp() compares both strings in whole.
To only test for parts use strstr() like for example so:
if (strstr(args, "r")) || strstr(args, "rr")))
{
svdArray[keepTrack] = args;
keepTrack++;
}
You need to pass a char* to strcmp, like args.
Here is the prototype for strcmp from here:
int strcmp ( const char * str1, const char * str2 );
So since args is a char* and "r" will give you a char* you should try:
/* since you're learning, print out what this thing does */
#include <stdio.h>
printf("Output: %d\n",strcmp(args,"r"));