You should read more about C's int promotion rules. That's the rule, so the compiler must obey. Without it, some "trivial" code won't work (like you expected). For example
char d[4] = {0xFF, 0xFE, 0x80, 40};
int i = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
int j = d[2] - d[0]*d[1];
In some compilers for 8-bit microcontrollers there is an option to disable C standard conformation to speed up maths since always promoting a char to int to do the maths may cause unnecessary operations and slow down the process. It also consumes more memory which is valueable in such embedded systems. Of course it will make some code not portable or not work at all, but that's the trade off.
Modern microprocessors are 16 bit or more, so using char won't save you from any operation but may also increase code size and speed because of the sign adjustment before/after each operations. Most RISC architectures don't even have instruction for operating on operand different from its native size except for load/store and sign extending. So it's recommended to use the native int size for all temporaries. Types smaller than native register size should only be used in case of very large arrays that could increase cache miss, to save or read data from some other types of memory or for compatibility with other systems/libraries