All integers or pointers can be used in an expression that is interpreted as “truth value”.

int main(int argc, char* argv[]) {
  if (argc % 4) {
    puts("arguments number is not divisible by 4");
  } else {
    puts("argument number is divisible by 4");
  }
...

The expression argc % 4 is evaluated and leads to one of the values 0, 1, 2 or 3. The first, 0 is the only value that is “false” and brings execution into the else part. All other values are “true” and go into the if part.

double* A = malloc(n*sizeof *A);
if (!A) {
   perror("allocation problems");
   exit(EXIT_FAILURE);
}

Here the pointer A is evaluated and if it is a null pointer, an error is detected and the program exits.

Many people prefer to write something as A == NULL, instead, but if you have such pointer comparisons as part of other complicated expressions, things become quickly difficult to read.

char const* s = ....;   /* some pointer that we receive */
if (s != NULL && s[0] != '\\0' && isalpha(s[0])) {
   printf("this starts well, %c is alphabetic\\n", s[0]);
}

For this to check, you’d have to scan a complicated code in the expression and be sure about operator preference.

char const* s = ....;   /* some pointer that we receive */
if (s && s[0] && isalpha(s[0])) {
   printf("this starts well, %c is alphabetic\\n", s[0]);
}

is relatively easy to capture: if the pointer is valid we check if the first character is non-zero and then check if it is a letter.