argv[1] giving segfaults - c++

I have the following code:
int main(int argc, char *argv[])
{
if(strcmp(argv[1],"-e")==0)
{
//perform code
}
//code if argv[1] is not "-e"
return 0;
}
When I take out the whole if statement:
if(strcmp(argv[1], "-e")==0)
my code if strcmp(argv[1], "-e") == 0) is not "-e" works fine. But when I leave it in, my code only works for when the if statement above produces true. Any ideas on why this may happen? For example:
If I compile with argv[1] not commented out:
//example program will be an executable
exampleProgram -e < a.txt works but exampleProgram < a.txt doesn't work. Any ideas why?

Simple, if you don't send any argument to the program then args[1] doesn't exist and you are trying to access to an unassigned memory position.
You may do something like this:
int main(int argc, char *argv[])
{
if(argc > 1 && strcmp(argv[1],"-e")==0)
{
//perform code
}
//code if argv[1] is not "-e"
return 0;
}

argv is an array. When you try to index into array beyond last element you get segfault. To not go past the last element check the value of argc, and argc - 1 will be the index of the last element you can access. More on segfaults here: http://en.wikipedia.org/wiki/Segmentation_fault

the reason
exampleProgram -e < a.txt works and
exampleProgram < a.txt doesn't
is because when there is no argument being passed into the program, where is no argv[1], it is out of bounds of the args array. what you need to do before testing argv[1] is test to see if there are that many arguments to begin with.
For example:
if (argc > 1) {
if(strcmp(argv[1],"-e")==0) {
}
}
argc is used to hold how many arguments exist in the program so you can test this before going out of bounds.

Related

Batch one line to call an executable using arguments from file

For convenience, I have renamed all the files to simple names for my example.
I'm trying to run an executable (test.exe), with a C++ entrypoint int main(int argc, char* argv[]) from a batch file (test.bat), and pass arguments from a text file (test.txt). The end goal is to run unit tests on an SDK using the testing software (test.exe).
My issue is that I do not want to have to use a variable when I call the executable since it makes the code harder to read :
rem This works
set /p test_input=<test.txt& call test.exe %test_input%
After some research, I figured I should use input redirection like so :
rem This does not work
call test.exe < test.txt
This does not work, and I don't understand why.
This is what I initially tried, and it has been suggested before on SO (here).
I have access to the test.exe code, so I can print argc and argv :
int main(int argc, char* argv[])
{
if(new_argc >= 2)
{
if(strcmp("-help", argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < argc; i++)
{
if(strcmp("-framerate", argv[i]) ==0)
{
i++;
if(i < argc)
{
FrameRate = (float)atof(argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
}
If I enter the arguments and parameters manually, it works as expected.
test.txt
-arg1 param1 -arg2 param2 ...
test.bat
call test.exe < test.txt
Output : test.exe runs as if there are no arguments or parameters.
Edit :
Added a few details about the entrypoint and renamed the batch variable.
Thanks to the comments under my question, I was pushed in the right direction.
The problem was my understanding of <. It literally means "Read file to STDIN" (as mentionned here). Many other documentation sites give vague definitions like (as mentionned here)
command < filename : Type a text file and pass the text to command
I need to parse the input correctly, since stdin isn't available in argc or argv, but through std::cin.
My batch code and text file remain unchanged, and I want to maintain the same form of parsing to avoid rewriting multiple projects, so I split the input string using the Solution 1.3 from here (slightly modified) and created a new_argv.
std::vector<char*> split(const std::string& s, char delimiter)
{
std::vector<char*> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(_strdup(token.c_str()));
}
return tokens;
}
int main(int argc, char* argv[])
{
std::string extra_input; // Variable to store the contents of test.txt
std::getline(std::cin, extra_input); // Recuperate the contents of test.txt
std::vector<char*> new_argv = split(extra_input, ' '); // Split the args
for(int i = argc - 1; i >= 0; i--)
new_argv.insert(new_argv.begin(), argv[i]); // Add the original args to the beginning
const size_t new_argc = new_argv.size(); // Create the new argc based on the final argument list (vector)
if(new_argc >= 2)
{
if(strcmp("-help", new_argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < new_argc; i++)
{
if(strcmp("-framerate", new_argv[i]) ==0)
{
i++;
if(i < new_argc)
{
FrameRate = (float)atof(new_argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
// Important, don't forget to free the memory used by the _strdup
for(int i=1; i < new_argc; i++)
{
if(i >= argc)
free(new_argv[i]);
}
}
test.bat
call test.exe < test.txt
test.txt
-arg1 param1 -arg2 param2 ...
Of course, I need to add some checks to make it properly handle whitespace, but that's the gist of it. Thank you for your help and external point of view.
Edit : Fixed a mistake in the code.

Error in copying argv to char array

I am trying to copy argv to char array, been through some solutions online but ending up in getting Segmentation Fault. Following is the code i used:
void main (int argc,const char *argv[])
{
char *arr;
arr = (char *) malloc(strlen(argv[1])+1);
strcpy(arr,argv[1]);
}
Please help to identify what I am doing wrong.
It seems that argv[1] is equal to NULL or even does not exist (The C Standard allows that argc may be equal to 0).
Add the following check
char *arr;
if ( argc > 1 )
{
arr = (char *) malloc(strlen(argv[1])+1);
strcpy(arr,argv[1]);
}
else
{
// print some error message
}
Please help to identify what I am doing wrong.
All right then sir. You are asking for argv[1], but you are not sure whether it exists. accessing an array outside its bounds has undefined behavior. You should always check if number of parameters is what you expect to avoid undefined behavior:
if ( argc < 2 )
{
// error, cannot copy argv[1] because it doesn't exist. Explain this to user
}
// now OK..., also we postponed allocation of arr pointer
char *arr = malloc( strlen( argv[1]) + 1);
//^^^^
// no need to cast return value of malloc in C
strcpy( arr, argv[1]);
When using command line input, we should deal with number of arguments.
You can try something like this..
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void main (int argc, const char *argv[])
{
if(argc==2)//change condition based on your requirements
{
char *arr;
arr = (char *) malloc(strlen(argv[1])+1);
strcpy(arr,argv[1]);
printf("string is %s\n",arr);
}
else
{
printf("check your command line input (only 2 parameters)\n");
}
}
OUTPUT:
$ ./a.out
check your command line input (only 2 parameters)
$ ./a.out hello
string is hello
$ ./a.out hi hello
check your command line input (only 2 parameters)
$

Can main function have default argument values?

How can I put the default values for main function arguments like the user defined function?
Well, the standard says nothing which prohibits main from having default arguments and say you've successfully coalesced the compiler to agree with you like this
#include <iostream>
const char *defaults[] = { "abc", "efg" };
int main(int argc = 2, const char **argv = defaults)
{
std::cout << argc << std::endl;
}
Live example. It compiles with no errors or warnings, still it's useless; a futile experiment. It almost always would print 1.
Every time you invoke the program, say, with no arguments (or any number of arguments for that matter), argc gets set to 1 and argv[0] points to the program name, so doing it is pointless i.e. these variables are never left untouched and hence having defaults makes little sense, since the defaults would never get used.
Hence such a thing is usually achieved with local variables. Like this
int main(int argc, char **argv)
{
int const default_argc = 2;
char* const default_args[] = { "abc", "efg" };
if (argc == 1) // no arguments were passed
{
// do things for no arguments
// usually those variables are set here for a generic flow onwards
argc = default_argc;
argv = default_args;
}
}
I think you want to do two different things for the following cases.
When no arguments are passed
When arguments are passed.
Here is how you do it.
int main(int argc, char *argv[])
{
if(argc == 1)
{
// case #1
}
else
{
// case #2
}
}
Using argc and argv? Thoses will pass argument from the command line to your program. You can't really use default arguments. You have to pass them during the call to your program like this :
$> ./my_addition "4" "7"
int main(int argc, char *argv[])
{
// argc <=> 'argument count' (=3)
// argv <=> 'argument vector' (i.e. argv[1] == "4")
// argv[0] is usually the bin name, here "my_addition"
for (int i = 0; i < argc; ++i)
std::cout << argv[i] << std::endl;
return (0);
}
Maybe you could use a script to run your program, this could maybe be the closest solution to default argument for main().
exec_my_prog.sh:
#!/bin/zsh
call_your_program + very_meny_args
And calling ./exec_my_prog.sh would run your program with the "default" arguments.

Checking arguments in terminal

How can I write a program to check the arguments in the terminal are correct?
For example, if I have a program hello.cpp and I want to call it as:
./hello yes 10
I want the program to make sure that the first argument is yes or no and the second argument is a number between 1-10. So how can I read these arguments into my program to do the checking?
Thanks!
Command line arguments are passed as a count and individual strings in the argc and argv arguments to main().
int main(int argc, char *argv[])
{
...
}
Simply check the value in argc and the strings in argv for the appropriate values.
You meant to execute like this, ./hello yes 10
there is an option argc and argv in c
where argc is the number of arguments passed and argv with the index shows the argument passed itself.
Take a look at the below code for iterating through all arguments.
int main(int argc, char *argv[]){
int i = 0;
for (i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
As mentioned by other users, The main function is the entry point of your program, and the way it gets data from the command line is through its parameters.
The first int argument is the count of all the arguments passed, including the program name, the second char ** argument is a pointer to each parameter passed, including the program name:
int main
(
int argc, // <-- how many parameters has been provided?
char **argv, // <-- what values has each parameter?
)
{
...
return 0;
}
So, knowing that, your call ./hello yes 10 must be like that:
argc = 3
argv[0] = "./hello"
argv[1] = "yes"
argv[2] = "10"
The names argc and argv are just a convention, you can name them at your pleasure, but it's a good practice to keep the names that everyone are used for.
And the argument doesn't are forced to be int, char ** they must follow a quite rigid convention, borrowed from this answer:
It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both the following definitions of main: int main() and int main(int argc, char* argv[])
Knowing that, let's focus on your question:
First of all, you must ensure that 2 arguments are passed, so you must check the argc value and ensure that equals exactly 3.
the first argument is yes or no
Next, you must store your argv[1] (because 0 contains the program name) into a string and compare it with the values "yes" and "no":
std::string YesOrNo(argv[1]);
if (YesOrNo == "yes" || YesOrNo == "no")
And finally, you must store your argv[2] into an integer and check if it is equal or less to 10:
std::stringstream Stream;
int Value = 0;
Stream << argv[2];
Stream >> Value;
if (Value <= 10)
So, the result is:
int main(int argc, char **argv)
{
if (argc == 3)
{
std::string YesOrNo(argv[1]);
if (YesOrNo == "yes" || YesOrNo == "no")
{
std::stringstream Stream;
int Value = 0;
Stream << argv[2];
Stream >> Value;
if (Value <= 10)
{
// Your stuff..
}
}
}
return 0;
}
I let you deal with all the uppercase and lowercase stuff and the false positives with the numeric argument, at least I'm not going to do all your homework ;)

Segmentation Fault With argv

I have a program that reads in a single argument from the command line and performs certain operations on it. I'm using argv and argc. When I fail to pass an argument to the program, it segfaults. I've tried checking if argc isn't a certain value and then printing the value out, but it still segfaults. Here's the code in question. Note that it works as expected when passed a single argument. Here's the code in question:
int main(int argc, char *argv[])
{
int numTimes = atoi(argv[1]); //converts content of argv[1] into integer
if(argc != 2)
{
printf("Enter a valid integer.");
}
You need to check argc before you try to access that argument. Just move the argc test to sometime before before you call atoi(argv[1]).
Just check the number of arguments before trying to accessing a specific element. Something like this:
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Enter a valid integer.");
return 0;
}
int numTimes = atoi(argv[1]); // now we're sure to have at least 1 argument passed
// ...
}
You have to do the check before attempting to access the arguments.