Both works the same way. rand() gives you a big random number between 0 and RAND_MAX. Using % 47 gives you the remainder of dividing the random number with 47, which gives you a number between 0 and 46.
When you do rand() % 47 it is calculating the remainder. That's why you get a number in the range 0-46.
I don't know exactly how % is implemented. Is it important to know? It probably depends on the hardware and on the values involved anyway. I guess it's just a few instructions at most.
x % 46 + 1 is not an equivalent expression to x % (46 + 1). It's equivalent to (x % 46) + 1.
x % 46 is a number between 0 and 45 inclusive. x % 46 + 1 is a number between 1 and 46 inclusive. x % 47 is a number between 0 and 46 inclusive.
By definition of integer division, the remainder of a dividend and a divisor cannot have a value greater than or equal to the divisor.
dividend = divisor * quotient + remainder
If remainder = divisor then
dividend = divisor * quotient + divisor
dividend = divisor * (quotient + 1)
dividend = divisor * quotient'
1 2 3 4 5 6
#include <iostream>
int main(){
for (int i = 0; i < 100; i++)
std::cout << i << ' ' << (i % 46) << ' ' << (i % 46 + 1) << ' ' << (i % 47) << std::endl;
}