Reputation: 123
I'm currently learning C by working through K&R's The C Programming Language, and have reached the point in the book where command-line arguments are discussed. In the book, the main routine would be written something like this:
int main(int argc, char *argv[])
{
do something
}
From my understanding, at some point the number of arguments passed to the program must be counted and stored in argc
. Also, the arguments themselves must be stored, and pointers to the first characters of each are stored in the array argv
, where argv[0]
is a pointer to the name of the command and argv[argc]
is a null pointer. These steps can't just magically occur, this behaviour must be defined somewhere!
As an example, imagine that I want to store the first character of each argument passed to the program, firstc
, and discard the remainder of that argument (let's pretend that I had a really, really good reason for doing this). I could write main() like so:
int main(char firstc[])
{
do something
}
Clearly, this can already be done quite easily with the default argc
and argv
, and I wouldn't actually do it in practice. I can't even imagine a scenario in which this would actually be necessary, but I'm curious to know if it's possible.
So my (entirely theoretical, completely impractical) question is this: is it possible to define my own behaviour for the command line arguments? If it is, how would one go about doing so? If it's relevant, I'm using Ubuntu 16.04 and the GNOME Terminal.
P.S.
I just realized while writing this question that it is entirely possible (perhaps probable) that the C script is completely blind to what's going on outside, and that the terminal emulator is what prepares the command-line arguments for the C program.
Upvotes: 2
Views: 926
Reputation: 6407
There are standards (like ANSI C, C89, etc.), that provide main rules and set set of restrictions, and there are agreements, that do not violate the standards and provide you some possibility.
Fist, I have one more example for you:
#include <stdio.h>
int main(int argc, char * argv[], char * envs[])
{
int i = 1;
while (envs[i] != NULL)
{
printf("%d : %s\n", i, envs[i]);
i++;
}
return 0;
}
Just try and see how this third argument of main
can be useful.
Also, I want to explain my approach to command line arguments processing. I make ParseCommandLine
(or EvaluateParameters
) and call it at the beginning of main
. That function analyze strings from command line and store all settings for further convenient usage. E.g. if I expect my program to be run as
prog.exe -i input_file_name -o output_file_name -e
I will do something like:
#include <string.h>
#include <stdio.h>
#define FNAME_LEN 20
struct settings
{
char inpFName[FNAME_LEN];
char outFName[FNAME_LEN];
bool isEncoded;
} globalSettings;
bool ParseCommandLine(int argc, char * argv[])
{
int c;
for (int c = 1; c < argc; c += 2)
{
if (!strcmp(argv[c], "-i") && c < argc - 1)
{
strncpy(globalSettings.inpFName, argv[c + 1], FNAME_LEN - 1);
continue;
}
if (!strcmp(argv[c], "-o") && c < argc - 1)
{
strncpy(globalSettings.outFName, argv[c + 1], FNAME_LEN - 1);
continue;
}
if (!strcmp(argv[c], "-e"))
{
globalSettings.isEncoded = true;
c--;
continue;
}
}
// rules to check mandatory values
if (strlen(globalSettings.inpFName) == 0 || strlen(globalSettings.outFName) == 0)
{
return false;
}
return true;
}
int main(int argc, char * argv[])
{
if (ParseCommandLine(argc, argv))
{
// do something
}
else
{
// explain how to run program
}
return 0;
}
Upvotes: 0
Reputation: 882426
The setup of arguments is not actually within the purview of the C standard, it simply dictates the allowable forms of main
that you can use. There are two canonical forms of this (assuming a hosted implementation), one being the argc/argv
option, the other being the void
option (although note that an implementation is free to provide others).
Typically, there is code that runs before main
is called, such as from startup code in an object file like crt0.o
.
However, as stated, the standard doesn't dictate anything that happens at that stage, it's the responsibility of the "environment" to set up things correctly so that main
can be called.
In terms of doing what you request, I suspect the easiest solution would be to provide a main
taking the canonical form and simply call a myMain
with the first character of each argument, though you would probably need to intelligently handle any number of arguments that may be given to main
.
An example follows which can handle between one and three arguments:
#include <stdio.h>
int myMain0(void) {
printf ("myMain0\n");
return 0;
}
int myMain1(char p1) {
printf ("myMain1 [%c]\n", p1);
return 0;
}
int myMain2(char p1, char p2) {
printf ("myMain2 [%c] [%c]\n", p1, p2);
return 0;
}
int main(int argc, char *argv[]) {
switch (argc) {
case 1: return myMain0();
case 2: return myMain1(argv[1][0]);
case 3: return myMain2(argv[1][0], argv[2][0]);
}
printf ("Invalid argument count of %d\n", argc - 1);
return 1;
}
Upvotes: 5
Reputation: 9570
Your main()
declaration does not actually define what parameters you get. That is the responsibility of the program's environment: the operating system and the calling program, usually the command line processor.
The caller (typically a shell program) prepares parameters and passes them to appropriate operating system's routine for the program to be called. The OS routine prepares those data for a callee, typically on the stack, and makes a jump into your program's entry point, which then follows to the main
function.
Your main()
declaration just declares what your main
expects on the stack, which indirectly defines how you can use those data, but not what they are. That's also why you can declare main
without parameters as main(void)
– that simply means 'whatever is passed to me, I'll ignore it anyway'.
Upvotes: 0
Reputation: 13520
The operating system is the one that passes the number of arguments and the arguments themselves from the command line to the C program.
The function main
is not picky about its arguments. You can get no argument at all, you can get only argc
, and you can get both argc
and argv
.
You can even get 3 or 4 arguments with whatever types you want, but they will contain garbage. The operating system will always pass the number of arguments and their names as an int and an array of pointers to strings.
Upvotes: 0