0

I was making a C program where I wanted to make a function which takes in an "n" integer, and returns the number of divisors it has.

int divisors(int n) {
int amount = 0;
for(int i = 0; i <= n; i++) {
    printf("%d mod %d = %d\n", n, i, n % i);
    if(n % i == 0) {
        amount++;
    }
}

return amount;
}

The printf is obviously for debugging. The part of the code that would cause problems is that the loop starts with 0, meaning that in the first iteration, the program would have to print N mod 0. However, I tested out the function by just assigning some int a to this function with the input of 8 in main(), and the program prints this:

8 mod 0 = 8
8 mod 1 = 0
8 mod 2 = 0
8 mod 3 = 2
8 mod 4 = 0
8 mod 5 = 3
8 mod 6 = 2
8 mod 7 = 1
8 mod 8 = 0

So 0 modulus is being run with no problem, and returns n instead. The interesting part is if I explicitly tell the program to printf("%d", 8 % 0); then I get the error I expect. So does anyone know why n mod 0 runs without errors in C when it is in loops?

NOTE: The program is compiled with GCC and is not even throwing any warnings/errors when compiling.

EDIT: Added gcc --version.

Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1 Apple clang version 13.0.0 (clang-1300.0.29.3) Target: arm64-apple-darwin20.6.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin

SzAdam
  • 1
  • 1
  • 4
    This is undefined behaviour – Jim Morrison Oct 02 '21 at 21:09
  • 2
    Divide by 0 is undefined behaviour. UB does not mean the program is required to behave in the way that you think it should. It could crash, it could produce wrong values, it could produce "right" values or any other behaviour at any time. – kaylum Oct 02 '21 at 21:12
  • Does this answer your question? [Does "Undefined Behavior" really permit \*anything\* to happen?](https://stackoverflow.com/questions/32132574/does-undefined-behavior-really-permit-anything-to-happen) – kaylum Oct 02 '21 at 21:13
  • The compiler isn't smart enough to catch the problem because it's not going to simulate all possible values that `i` can get. So it's a runtime problem now, when it's most likely going to cause exceptions because of division by 0. – Cheatah Oct 02 '21 at 21:14
  • Also, the compiler is clang, not GCC. The `gcc` is probably a hard link for historic reasons. Note that `/usr/bin/cc`, `/usr/bin/gcc` and `/usr/bin/clang` all have the same size and in fact the same contents. – Cheatah Oct 02 '21 at 21:23
  • This will **tend** to happen on ARM64 because the divide instruction silently returns 0 when the divisor is zero, and `a % b` typically is done internally by computing `a - ((a / b) * b)` as the divide instruction does not return a remainder. In particular it will typically *not* cause runtime exceptions. But again it is undefined behavior, and this is not in any way **guaranteed** to happen. The special case of a constant zero modulus is likely to generate different code, and the compiler might explicitly force an exception in that case. – Nate Eldredge Oct 02 '21 at 23:06
  • Note that you really don't need to test either `i == 1` or `i == n`; you could simply set `amount = 2;` before the loop and run the loop from `2` to `n / 2`, which is a lot better for performance once `n` gets moderately large. – Jonathan Leffler Oct 03 '21 at 18:41

1 Answers1

1

When the second operand of the modulo operator is 0 the behavior is undefined. You can virtually get any result from this operation, and as you saw, for the same operation one time you got 8 and the other time the program terminated.

Jim Morrison
  • 401
  • 4
  • 9