views:

1395

answers:

14

Why is the command line arguments count variable (traditionally "argc") an 'int' instead of an 'unsigned int'? Is there a technical reason for this?

I've always just ignored it when trying rid of all my signed unsigned comparison warnings, but never understood why it is the way that it is.

+1  A: 

I suppose it is designed to be compatible with C, and in C times people didn't really care that much about signed/unsigned correctness.

quant_dev
"C times" are hardly over. I for one won't stop using C anytime soon.
Matt Joiner
The times when C ruled are over, and that's what I meant.
quant_dev
+10  A: 

Because C is old, and it was designed that way from the start. It's too late to change it now.

rlbond
C *is* old. It *was* designed that way from the start. But I have to disagree with that last sentence (not enough to downvote though). ISO could quite easily add a third prototype definition for main with little impact. In fact, implementors are also free to add other main prototypes - it's explicitly mentioned in the standard.
paxdiablo
@paxdiablo agreed, standards can and should be evolved and changed [with care]. In this particular case, however, how does this unsigned argc thing helps with world peace?
mjv
+3  A: 

As a solution to your warning problem, you could do something like this to suppress the warnings:

const unsigned int uargc = (unsigned int) argc;
Greg Hewgill
or `static_cast` in C++.
GMan
Right, since the question was tagged `c` I thought I'd use what would work in both.
Greg Hewgill
+1  A: 

I see how it might seem weird: argc should not be negative! But look at it this way: both int and unsigned int cover the range of values you'd accept (if you have 2^31 command line parameters, you have a problem) and int is shorter to type.

Interview puzzle question: how many keyboards would have been used up typing the unsigned if C had gone with unsigned int argc?

Harold L
C could have named them int and uint, then it is only one extra character.
Zan Lynx
Great answer ;) like millions I think
Elalfer
@Zan: I have always wished this were so
Catskul
ints are not necessarily 32-bit values, their size is unspecified (other than a minimum).
paxdiablo
it's a little known secret that most verbose languages are sponsored by the keyboard companies.
cobbal
And languages with one letter keywords are suppressed by the international keyboard conspiracy?
Zan Lynx
I believe COBOL was supported by the Amalgamated Union of Caps Lock and Shift Key Designers and Craftsmen.
caf
um... what about APL?
Jason S
....and haven't you ever seen "typedef int int32_t; typedef unsigned int uint32_t;" in a header file? (for 32-bit int machines)
Jason S
+36  A: 

The fact that the original C language was such that by default any variable or argument was defined as type int, is probably another factor. In other words you could have:

  main(argc, char* argv[]);  /* see remark below... */

rather than

int main(int argc, char *argv[]);

Edit: effectively, as Aaron reminded us, the very original syntax would have been something like

  main(argc, argv) char **argv {... }

Since the "prototypes" were only introduced later. That came roughly after everyone had logged a minimum of at least 10 hours chasing subtle (and not so subtle) type-related bugs

mjv
Actually it would have been `main(argc,argv) char* argv { } as inline parameter declaration and prototypes weren't introduced until ANSI C (ahh - the good old days... I'm so glad they're gone)
Aaron
@right, Aaron! I forgot... We'd better stop before we show our age too much ;-)
mjv
A: 

By setting it to int, the range is limited to between 1 and INT_MAX inclusive. This will typically mean that no accidental cast or alias will take it out of range from an inadvertent wrap around. It also allows implementations to use the entire negative and 0 range for system specific scenarios.

Ok, I just made that up. The real reason is that it was just an arbitrary decision that one of the original C language developers made, and nobody really thought that hard about it until now. :)

Paul Hsieh
+9  A: 

Here's a history of the C programming language in dmr's own words. It's not stated explicitly (at least not from the quick skim I gave it), but the earliest versions of C didn't support unsigned types. mjv's point about implicit typing to int is also salient.

John Bode
You beat me to it. Yea, I don't believe B (predecessor of C) had anything but `char` and `int` and every function returned an `int`
Earlz
B didn't have char. Nor int for that matter. It was untyped. char==int==void*
jbcreix
+1 for citing sources (interesting at that)
Jason S
A: 

Just a simple question: Do you expect more than 231 (or even more than 215) command line arguments? I doubt that most operating systems could handle that many.

Loadmaster
**int** though must be at least `2^15-1`
Johannes Schaub - litb
thats not why I was asking, but definitely possible `rm $(find / -mindepth 3)`
Catskul
`find / -mindepth 3 -exec rm {} \;`
Tom
The `find -exec` option command provides `rm` with exactly one argument at a time. The previous `rm` command might fail on systems that have an upper limit on the number (or total length) of a command line.
Loadmaster
@paxdiablo: false. C guarantees INT_MAX is at least 32767.
R..
+3  A: 

It was a prescient design decision to make it easier to port C programs to Java in the future since there are no unsigned types in Java.

Rob Walker
You must not know c programmers very well. If they had prescience, they surely would have made sure to make porting to Java *more* difficult rather than less ;)
Catskul
+1 for the awesome sarcasm. that's sarcasm Catskul.
Matt Joiner
@Rob: ??? I wouldn't call it "easier" -- it's only easier for people doing signed arithmetic. For handling unsigned arithmetic and bitstrings Java is a real pain.
Jason S
+15  A: 

A few reasons:

  • because it doesn't matter
  • because C did not originally have the unsigned keyword or unsigned integer types
  • because C did not originally check parameter types and did not even have prototypes.
    As a result, it was common practice to not even declare int types, as this was the default.
  • because int was, in a sense, more important back then. Everything was an int. C evolved in part from a language that did not even have types. Every single varable was a word, which is what int originally was used for.

UPDATE: Jason S asked for sources. I think you can dig all of this (except for "it doesn't matter") out of a paper by dmr which is on line: The Development of the C Language. You may need to look up the earlier languages BCPL and B in the usual places.

DigitalRoss
...cite sources?
Jason S
............ok!
Jason S
+4  A: 

The Google C++ Style Guide suggests never to use unsigned int types unless you're working with actual bit patterns. Their rationale applies to C as well. Quick summary line:

... C's type-promotion scheme causes unsigned types to behave differently than one might expect. ... Don't use an unsigned type.

This was probably not in the minds of the original creator's of C, but who knows‽

Adam Goode
I don't understand Google's claim that unsigned types behave "unexpectedly" (which in context must mean, more unexpectedly than signed types). Signed and unsigned types misbehave when used together, one will be promoted to the signedness of the other. So, the type promotion system causes all integer types to "behave different than one might expect".
Steve Jessop
ditto. wtf? bits are bits until you do plain arithmetic on them, at which point unsigned and signed each are perfectly valid for particular applications.
Jason S
One thing that's nice about signed is that you always have a way to represent an invalid value. You do have to waste an entire bit on it though.
Adam Goode
-1U is an equally-useful "invalid value" in most contexts. For instance nothing could actually have size equal to the entire size of address space, minus 1. Actually I like Jason's comment. One change I wish could be made to C is eliminating signed/unsigned types and replacing them with signed/unsigned operator variants. But alas...
R..
+4  A: 

Another reason could be that unsigned types can be inconvenient for iteration. For example, this snippet iterating down:

for (size_t i = SIZE - 1; i >= 0; --i)
  ...

Is, in fact, a bug. When i reaches 0 in the last iteration, it will go on right into 4294967295 (on a 32-bit machine) and the loop won't terminate.

For this reason, I personally find plain ints more convenient for iteration. You don't have to be especially careful when you switch a for loop from counting up to counting down when using ints.

Eli Bendersky
That's due to the fact that C++ lacks a proper for_each construct. I ran in the same problem multiple times, but I'm always glad if I can use the std containers with proper iterators.
xtofl
agreed, but in C loops is all you have
Eli Bendersky
+2  A: 

The declaration for main() was defined before the unsigned types were added to the language - see DMR's page on 'Primeval C'. It was too late to change when unsigned was added.

Jonathan Leffler
A: 

Is there any way to have zero arguments, since the initial argument is the program name? In other words, is argv[0] always safe to access, or should it be qualified by if (argc > 0)?

GlitchCog
If this is a question, you need to start a new question, not bump an old one. To answer your question in C++: [ISO03] *"argv[0] shall be the pointer to the initial character of a NTMBS that represents the name used to invoke the program or ""."* So yes, 0 is always valid, but no it's not necessarily the program name.
GMan