0

I have an array

char type;
char items[3][15] = {"Fish", "Beef", "Veggie"};

And this as a menu

printf("1.  Fish – $5 \n");
printf("2.  Beef – $10\n");
printf("3.  Veggie – $15 \n");

With a printf statement as follows

printf("\nYou've selected %s \n", items[(int)(type - '1')]);

I found this being done on a completely different type of question but don't really understand what the items[(int)(type - '1')] is doing in my code. It just seems to works.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Does this answer your question? [Convert a character digit to the corresponding integer in C](https://stackoverflow.com/questions/628761/convert-a-character-digit-to-the-corresponding-integer-in-c) – Mad Physicist May 27 '21 at 16:06
  • Any decent book, tutorial or class should have some contents about *casting* as a way to convert a value from one type to another. – Some programmer dude May 27 '21 at 16:07
  • 3
    By the way, the casting isn't really needed here, because the result of `type - '1'` already is an `int`. – Some programmer dude May 27 '21 at 16:08
  • 2
    The code had better check that `type` has a value in the range `'1'` .. `'3'` in code that isn't shown, or else there will be problems with out-of-bounds array access. – Jonathan Leffler May 27 '21 at 16:18

1 Answers1

3

items[…] is subsetting the array items.

(int)(type - '1') is calculating the index to use for subsetting.

(int)(…) is a cast to int. It’s worth noting that this cast is completely unnecessary here: the result of subtracting two character values (or an integer from a character) already yields the type int in C due to integer promotion. This means that we can rewrite the expression to

items[type - '1']

Lastly, type - '1' subtracts the numeric character code of the character constant '1' from the numeric character code of the value in variable type. The result of this transformation is that if type is '1', the value of type - '1' is 0, if type is '2' the value is 1, and so on.


Alternatively, the code could be implemented as follows:

char type_str[2] = { type };
int choice = atoi(type_str) - 1;
printf("\nYou've selected %s\n", items[choice]);

This makes it perhaps slightly clearer that we’re translating a character value into an integer value. However, it’s actually a much more complex implementation:

  • we first need to construct a string from the character (by assigning it into an array and zero-terminating the string, which happens implicitly in the initialisation of type_str above)
  • then we invoke atoi to translate "1" into 1, "2" into 2, …
  • the we subtract 1, to get a zero-based index.

In the end, this isn’t more readable or less error-prone — neither for beginners nor for expert C programmers.

In a more realistic application, it’s conceivable that the user input was directly a string rather than a character, which makes the application of atoi more useful. We’d also potentially want to handle errors (what happens if the user entered an invalid value? The current code — both mine and the one you posted — happily attempt to subset the array after an invalid input, with potentially bad consequences).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    While this is a great explanation, the idea of using character subtraction instead of simply converting the user's choice into an int and directly indexing seems error prone. Again, you're just explaining the code and have done nothing wrong, but imho best practice would be for the OP to refactor their code so indexing doesn't occur based on character arithmetic. – h0r53 May 27 '21 at 16:10
  • 2
    @h0r53 I’m normally a zealot about using strong typing and proper parsing APIs but even I would just subtract the character constant in this scenario in C, and the code OP posted is *idiomatic C code* (minus the weird cast). I don’t think there’s anything wrong with it in this instance. But I’ll add an alternative implementation to my answer! – Konrad Rudolph May 27 '21 at 16:13
  • It's functionally equivalent, just generally more confusing to the programmer. Also I'd be mostly concerned with invalid input (ie: a character that doesn't represent a valid choice of '1','2','3', etc). The logic to ensure input is within the proper constraints becomes more complex when using characters instead of ints. But I digress, nice explanation. – h0r53 May 27 '21 at 16:16
  • Note that doing math with `char` is inherently non-portable if you use values outside the ASCII range (if the OP is confirming the value is between `'1'` and `'3'` before doing the subtraction it's fine). `char` has implementation defined signedness, so `type - '1'` where `type` has the high bit set might be promoted to a negative number from -128 to -1 or might be treated as a positive int from 128 to 255, depending on the compiler (and for some compilers, depending on compiler flags). You have to use `signed char` or `unsigned char` to get a `char` with predictable promotion rules. – ShadowRanger May 27 '21 at 16:37