Why is it int argc?

  • Thread starter MartinBroadhurst
  • Start date
K

Keith Thompson

MartinBroadhurst said:
I've never used a negative number of command line arguments.

I think the argc/argv convention predates the introduction of unsigned
int into the language. (In ancient versions of C, I think it was common
to use pointers when you needed to do unsigned arithmetic.)

Using a signed type might also make some idioms a bit easier; for
example, a test for argc >= 0 would always (appear to) succeed if argc
were unsigned.

Finally, have you ever used more than INT_MAX command line arguments?
 
M

MartinBroadhurst

I think the argc/argv convention predates the introduction of unsigned
int into the language.  (In ancient versions of C, I think it was common
to use pointers when you needed to do unsigned arithmetic.)

That seems a likely explanation.
Using a signed type might also make some idioms a bit easier; for
example, a test for argc >= 0 would always (appear to) succeed if argc
were unsigned.

Are there implementations in which argc can be 0 then?
Finally, have you ever used more than INT_MAX command line arguments?

Not unless I've perpetrated some sort of console pasting disaster. I
wasn't so concerned about the efficiency of using an unnecessarily
large type, more the semantics.

Martin
 
K

Keith Thompson

MartinBroadhurst said:
That seems a likely explanation.

See, for example, <http://cm.bell-labs.com/cm/cs/who/dmr/cman.pdf>,
in which the only numeric types were char, int, float, and double.
(On the other hand, I just noticed that document doesn't mention
argc and argv.)

Even as late as C90, int was considered the default type for many
things; you might define main as:

main(argc, argv)
char **argv;
{
...
}
Are there implementations in which argc can be 0 then?

Yes the standard permits argc to be 0 (C99 5.1.2.2.1p2: "The value
of argc shall be nonnegative.") In that case, the program name is
not available, at least not via argv[0].

And on POSIX-based systems, the exec() family of functions lets you
invoke a program with pretty much any argc and argv values you like.

But what I actually had in mind is that argc is modifiable.
I've seen code that steps through the arguments by repeatedly
decrementing argc and incrementing argv. It's not the way I'd do
it, but it's valid nonetheless.
Not unless I've perpetrated some sort of console pasting disaster. I
wasn't so concerned about the efficiency of using an unnecessarily
large type, more the semantics.

Well, both int and unsigned int are the same size. Both have a
range that covers any reasonable number of arguments. int has the
advantage that the lower and upper bounds are both well outside any
reasonable requirement; unsigned int's lower bound of 0 is right
at the edge, which might be error-prone in some cases. But that's
not a very strong argument (if argc were unsigned, we'd get by),
nor is it the actual rationale.
 
A

Andrew Smallshaw

Yes. UNIX. Just have a program call, for example:

execl(path, (char *) NULL);

On most modern Unix systems that won't initialise main()'s argc to
_anything_. Program invocation with a null argv is generally taken
as an instruction to the dynamic linker. You'll get a listing of
requested and linked in libraries and the program will terminate
without ever calling main().
 
D

Dr Nick

That's gives me a warning about not enough variable arguments. execl
needs a null terminated list and clearly the compiler "knows" to expect
a real one in there.
On most modern Unix systems that won't initialise main()'s argc to
_anything_. Program invocation with a null argv is generally taken
as an instruction to the dynamic linker. You'll get a listing of
requested and linked in libraries and the program will terminate
without ever calling main().

With an extra NULL, or with execv I get a segfault from:

#include <unistd.h>

int main(void) {
execv("/bin/ls",(char * const *)NULL);
/* or, and ignore the warning */
execl("/bin/ls",(char *)NULL);
/* or */
execl("/bin/ls",(const *)NULL,(char *)NULL);
return 0;
}

and debugging does seem to show it was in the dynamic linker.

execl("/bin/ls","",(char * const *)NULL);
works but, of course, has an argument.

So I don't think it's as easy as we thought!
 
B

Ben Bacarisse

Dr Nick said:
That's gives me a warning about not enough variable arguments. execl
needs a null terminated list and clearly the compiler "knows" to expect
a real one in there.


With an extra NULL, or with execv I get a segfault from:

#include <unistd.h>

int main(void) {
execv("/bin/ls",(char * const *)NULL);
/* or, and ignore the warning */
execl("/bin/ls",(char *)NULL);
/* or */
execl("/bin/ls",(const *)NULL,(char *)NULL);
return 0;
}

and debugging does seem to show it was in the dynamic linker.

execl("/bin/ls","",(char * const *)NULL);
works but, of course, has an argument.

So I don't think it's as easy as we thought!

That depends on what you thought! Andrew is talking about a null argv
and that's not the same as the original proposal of starting a program
with argc == 0. When argc == 0, argv must have at least one element: a
null pointer.

You can do this:

#include <unistd.h>

int main(void)
{
char *argv[] = {0};
execv("./args", argv);
}

and in when args is this program:

#include <stdio.h>

int main(int argc, char **argv)
{
printf("argc=%d argv[0]=%p\n", argc, (void *)argv[0]);
return 0;
}

you get:

$ ./exec
argc=0 argv[0]=(nil)

which is answers the question at the top of this post.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,054
Latest member
LucyCarper

Latest Threads

Top