unsigned long long x;
unsigned int y, z;
x = y*z;
The evaluation of the expression y*z is not affected by the context in which it appears. It multiplies two unsigned int values, yielding an unsigned int result. If the mathematical result cannot be represented as an unsigned int value, the result will wrap around. The assignment then implicitly converts the (possibly truncated) result from unsigned int to unsigned long long.
If you want a multiplication that yields an unsigned long long result, you need to explicitly convert one or both of the operands:
x = (unsigned long long)y * z;
or, to be more explicit:
x = (unsigned long long)y * (unsigned long long)z;
C's * multiplication operator applies only to two operands of the same type. Because of this, when you give it operands of different types, they're converted to some common type before the multiplication is performed. The rules can be a bit complex when you're mixing signed and unsigned types, but in this case if you multiply an unsigned long long by an unsigned int, the unsigned int operand is promoted to unsigned long long.
If unsigned long long is at least twice as wide as unsigned int, as it is on most systems, then the result will neither overflow nor wrap around, because, for example, a 64-bit unsigned long long can hold the result of multiplying any two 32-bit unsigned int values. But if you're on a system where, for example, int and long long are both 64 bits wide, you can still have overflow wraparound, giving you a result in x that's not equal to the mathematical product of y and z.
yandzare bothunsigned int.