BigInteger check in C from a string
Good observation

Many new C programmers do not realize that n = strlen(s) when called in a loop can take code from linear performance O(n) to worse O(n*n) performance when  s may change or with weak compilers.

// I'd like to avoid repeated strlen() calls
int string_length = strlen(str);  // Good!!


Good use of const

Nice. string_contains_integer(const char *str)

Nicely formatted code

I hope you are using an auto formatter.  Life is too short for manual formatting.



"whatever its length is"

Strings can have a length that exceeds INT_MAX.  Note that strlen() returns type size_t.  That is the right-sized type for array indexing and sizing.  Note: size_t is some unsigned type.

// int string_length = strlen(str);
// for (int i = 0; i &lt; string_length; i++)

size_t string_length = strlen(str);
for (size_t i = 0; i &lt; string_length; i++)


Expensive sign compare

Rather than call strncmp(), just compare characters.

// char chr = str[i];
// if (strncmp("-", &amp;chr, 1) == 0)
if (str[i] == '-')


OP's code is tricky here.  strncmp() can handle non-strings (which is what &amp;chr points to) as long as they do not exceed n.  With n==1, code is effectively doing a char compare.

Sign at the beginning

Conversion of strings to numeric values allow a leading sign, either '-', '+' or none.  This code errantly allows a '-' someplace.  @Martin R. 

Just start with 

bool string_contains_integer(const char *str) {
  if (*str == '-' || *str == '+') {
    str++;
  }
  ....

  // Other sign code not needed


strlen() not needed.

Rather than i &lt; string_length, just test str[i] != '\0'.  This speeds up detection of non-numeric strings as the entire string does not need to be read.

is...(int ch) quirk

is...() functions are specified for int values in the unsigned char range and EOF.  On common platforms where char is signed, isdigit(chr) risks undefined behavior (UB) when chr &lt; 0.  Best to cast here.

// isdigit(chr)
isdigit((unsigned char) chr)


{ block }

Of coding style, I recommend to always use {} after if,else, even if one line.

//if (string_length == 0)
//    return false;

if (string_length == 0) {
    return false;
}


Minor: Simplification

To simply prints a line, code could use puts().  But what you did is OK.  Note puts() appends a '\n'.

// printf("%s\n", "PASS: Input is a number.");
puts("PASS: Input is a number.");




Alternative

This does not use array indexing.  Just increment the pointer as needed.

bool string_contains_integer_alt(const char *str) {
  if (*str == '-' || *str == '+') {
    str++;
  }

  if (*str == '\0') {
    return false;
  }

  while (isdigit((unsigned char)*str)) {
    str++;
  }

  return *str == '\0';
}