It sounds like making the width of ints implementation specific in C was a failure. It was supposed to allow better performance (allowing it to be set to the register width) but instead we get the worst of both worlds - portable code can't depend on its size but compilers can't practically change the size.
There's an added complication: in C, there are just five[1] names available for signed values: signed char, short, int, long, and long long. Additional, the number of bits assigned to each of those must be >= to the previous name.
Suppose your C implementation wants to have signed types for 8-bit, 16-bit, and 32-bit values. If int is 64-bits, then you literally don't have names left to label those lesser-sized signed types. So in practice, int can be no more than 32 bits.
The solution is to always use size_t as a count or size of anything. Uses offset_t for a difference between size_t values, or a difference between pointer values.
An alternate practice (one I subscribe to) is to use unsigned as your default type instead of int. In this practice, 'int' means a variable which can be negative. In real programs, the vast majority of variables never contains negative values.
[1] char can be either signed or unsigned, so let's ignore it for this. Also, let's ignore the new char type names introduced in recent C++.