Coming from Python, where I would simply use type() to find out the type of an object, C, lacking introspection, is forcing me to better grasp its data types, their relatedness, and pointers, before moving on to more advanced topics. This is a good thing. So I have the following piece of code, which I will tweak in various ways and try understand the resulting behavior:
int main(int argc, char *argv[])
{
int i = 0;
for(i = 0; argv[1][i] != '\0'; i++) {
printf("%d\n", argv[1][i]);
char letter = argv[1][i];
switch(letter) {
case 2:
printf("%d: 2\n", i);
break;
If I run this and pass the number 2 as a single argument, nothing happens. My understanding then is that because I have defined argv1[i] as a char, comparing it to 2 (an int) will return false, hence the code does not get called. And indeed, if I change the case from 2 to '2', the code does get called. Makes sense, but it leads me to my first question:
Q1. I have read in various places that to C, a character and an integer are essentially the same thing. So why doesn't C "know" that the 2 passed as an argument should be interpreted as an integer and not a string? After all, C allows me to, for example, use the %d string formatter for characters.
If I then change the type of variable letter from a char to an int:
int letter = argv[1][i];
... I still get the same behavior as in the first variant (i.e. nothing happens), even though now I am apparently comparing an int to and int in the case statement. This leads me to surmise that although I am defining letter now as an int, C is still reading it in as a char on the command line, and just calling it an int isn't enough to change it's type from the point of view of subsequent program flow.
Q2. Is the above reasoning correct?
So now I figure that if I change the type of letter to an int using atoi(), things should go OK. So:
int letter = atoi(argv[1][i]);
When I now try compile, I get:
Switch.c:14:27: warning: incompatible integer to pointer conversion passing
'char' to parameter of type 'const char *'; take the address with &
[-Wint-conversion]
int letter = atoi(argv[1][i]);
^~~~~~~~~~
&
/usr/include/stdlib.h:132:23: note: passing argument to parameter here
int atoi(const char *);
I then look up the documentation for atoi() and see that it can only be used to converted a string (a more precisely, a const char *), not a character. I would have though that since a char * is just a sequence of chars, that atoi() would work with both. And apparently there is no equivalent of atoi() for a char, rather only workarounds, such as the one described here.
Anyway, I decide the take the warning's instructions, and place an ampersand before the value (knowing that this implies a memory address, but not yet knowing why it is being suggested). So:
int letter = atoi(&argv[1][i]);
When I do so, it compiles. And now the program in this final form - with letter defined as an int, with the case statement comparing to an int, and with atoi being passed the address rather than value of argv[1][i]
- runs successfully.
But I don't know why, so I strip this down to test the values of argv[1][i]
and &argv[1][i]
by printing them. I observe that the program will only compile if I use the %s string formatter to print &argv[1][i]
, as it tells me that &argv[1][i]
is a char *.
Q3. Why is &argv[1][i]
, an address in memory, a char *?
In my printout, I observe that the values of &argv[1][i]
and argv[1][i]
are the same, namely: 2. So:
Q4. Why didn't the compiler allow me to use argv[1][i]
, if its value is no different to that of &argv[1][i]
?
Q5. Any time I've printed a memory address in a C program, it has always been some long number such as 1246377222. Why is the memory address == the value in this case?
No doubt there will be someone objecting that this mammoth post should be split into separate posts with separate questions, but I think the flow of trial-and-error, and the answers you provide, will help not only me but others looking to understand these aspects of C. Also feel free to suggest a better title for the post.
Many thanks.