C: Why does C need the memory address of a char in order to convert it to an int?

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.

Answers


Your misunderstanding is that 2 != '2'. They both have integral values but those values are different from one another. The ascii value of '2' does not equal 2 it equals 50. This means that int a = '2'; causes a to evaluate to 50. The standard way of converting integral chars to a numeric value is writing int a = '2' - '0'; This will cause a to evaluate to 2.

argv is an array of char *. This means that argv[j][i] is a char and &argv[j][i] is a char * it is the address of the character at location argv[j][i]. This means that atoi(&argv[j][i]) will compile but I am not sure it is doing what you expect because it will try to translate the entire string starting at argv[j][i] into a number instead of only the specific character at argv[j][i].


Q1: char and int are "the same" only in the sense that both are (signed - usually) integers. There are multiple differences, for example char is (usually) 1 byte long, while int is (usually) at least 4 bytes long.

Q2: your reasoning is wrong, because you compare an ASCII code of letter '2' (which is 50) with a number 2 (which has no visual representation in ASCII).

Q3: you made a mistake in your debugging - &argv[1][i] is an address of i-th character in the argv[1] string. So essentially this is a pointer. In your debugger you probably saw the character that was "pointed to".

Q4: see above - argv[1][i] is '2', while &argv[1][i] is an address in memory where this '2' can be found.

Q5: you probably made a mistake in debugging - see answer to Q3.


A string in C is a sequence of characters with a null terminator. It will always be referenced by address (of the first character), because its length is variable.


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]?

They are different

&argv[1][i] is a pointer to a memory position and

argv[1][i] is the value of the char in that position

It is just that

printf("%s", &argv[1][i]); // Prints the c-string at memory position
printf("%c", argv[1][i]); // Prints the char

I assume that when you say "printed" you mean the printf() function.


For Q5, as you say you come from Python, the id in implemented in C Python as the address of the variable. And even in Python, the id of a numeric variable is not the value of the variable.


Need Your Help

How To Update An Image In Mysql

php mysql image image-uploading

I have a php page that upload image to a folder and the name is inserted in mysql table . Now I want to create a page that will update the picture and delete the old picture in the directory folder...

Subclassing UITextField

ios

I'd like to create a text field which uses the number pad and accepts only numbers, even when pasted. Currently, I have a header as such: