Search code examples
cstringconstantsconst-correctnessctype

Why do C standard libraries neglect const correctness?


Looking at most of the functions in the C standard libraries, there appears to be a lack of const, where specifying so, would normally be preferred.

For example:
ctype.h/c

extern int isupper(int __c) __ATTR_CONST__;
extern int islower(int __c) __ATTR_CONST__;
extern int isdigit(int __c) __ATTR_CONST__;  

Why aren't these instead:

extern int isupper(const int __c) __ATTR_CONST__;
extern int islower(const int __c) __ATTR_CONST__;
extern int isdigit(const int __c) __ATTR_CONST__; 

They only observe the parameter after all:

int isupper(int c) {
  return _pctype[c] & _UPPER;
}

int islower(int c) {
  return _pctype[c] & _LOWER;
}

int isdigit(int c) {
  return _pctype[c] & _DIGIT;
}

Or let's take a function in string.c:

void *memcpy(void *dest, const void *src, size_t count)
{
         char *tmp = dest;
         const char *s = src;

         while (count--)
                 *tmp++ = *s++;
         return dest;
}

Why not:

void *memcpy(void *const dest, const void *const src, size_t count);

Is const excluded in these places for a reason?
Would it be wrong to include const in the ways I've shown?

I assume the functions have remained this way for historical reasons,
but I figured I should ask in case I've missed something.


Solution

  • Those are const-correct signatures.

    You almost never write const before pass-by-value arguments. The function gets its own copy so there's no danger there.

    The implementation of the function could make a promise to itself not to modify the parameter int isupper(const int c){ ... } and the compiler would enforce it to help the implementation with its logic, but that top-level qualifier is not part of the function signature and it is fully compatible with int isupper(int); (applies to top level volatile and restrict too).

     int isupper(int);
     int isupper(int const); 
     //fully compatible prototype -- top level qualifiers are not
     //part of the function signature
    

    void *memcpy(void *dest, const void *src, size_t count) is const-correct too. Only the second pointer promises not to change what it points to. The destination pointer, on the other hand, is all about changing what it points to.