The first case, making upscaling to an int explicit:
cout << ((int)c1 * c2 / c1 << endl;
the second case, assigning to an intermediate variable is equivalent to
cout << ((signed char)((int)c1 * c2)) / c1 << endl;
(Implicit casts to int done by the compiler made explicit.)
With two's complement, multiplying the largest negative value of a type by -1 leaves the value unchanged if it is restricted to the same number of bits. So, the second example, c1*c2 == c1. Even though the multiplication is done as an int, it is cast back to the width of a char.
In the first example, the entire calaulation is done as an int, so the operation has more bits to work with, and so the truncation doesn't occur. Here, c1*c2 == (c1*-1) == -c1. Hence the different result.