Once you have collected or generated data, you may want to search through it to find the information you're interested in. memchr is the first in the collection of search functions that can be used to accomplish such a task. It takes the object being search, a character to find, and a number of characters to search through.

void *
memchr(const void *s, int c, size_t n);

The return value is a pointer to the found character, or NULL if it does not exist in the object.

Local Variables

The description of memchr is very specific with respect to the data types used for searching. As you see with the prototype, memchr allows any object to be searched since it takes and returns a void *. In addition, it takes a search character which is actually an int (which is what a character would be promoted to anyways). However, The Standard has this to say about the data types

"The memchr function locates the first occurrence of c (converted to an unsigned char) in the initial n characters (each interpreted as unsigned char) of the object pointed to by s."

As such, rather than adding casts into our code, we can declare local variables of the appropriate types and assign them to implicitly perform the casts for us.

const unsigned char *pCur   = s;
const unsigned char search  = c;

Parameter Validation

Since this function will do all of its own work, we need to perform some parameter validation. This includes checking for a NULL pointer and when the search would wrap around memory. We exclude checking n against 0 because this isn't likely to happen and is handled by the logic to come. If the parameters are invalid then the search can't be performed and we return NULL.

if ( !s ||
    /* Check for wrapping while searching */
    ((((unsigned long) -1) - ((unsigned long) s)) < n))
{
    return NULL;
}

Implementation

A similar approach to memcmp will be taken where we loop over each character, decrementing n each iteration, but we will always compare against our search character. It's important to remember than after the if statement is executed, pCur will point one past the character that was just searched. If that was the character we were searching for then we must decrement pCur before returning its value as a void *. Finally, if the character was never found, return NULL to indicate as such.

while (n-- > 0)
{
    if (search == *(pCur++))
    {
        return (void *) --pCur;
    }
}

return NULL;

Testing

First we'll test our parameter validation by passing a NULL pointer, and attempting to trigger a search which would wrap memory. Next we'll search for a character which exists in the object, but specify that no characters should be searched, along with a character that doesn't exist. Lastly, search for characters at the boundaries of the object and in the middle.

int
memchrTest(void)
{
    int ret = 1;
    char obj[] = "ABCDEFG";
    char *p = NULL;

    do
    {
        /* NULL object */
        if (NULL != memchr(NULL, 'a', 10))
        {
            break;
        }

        /* Wrapped search */
        if (NULL != memchr((void *) ((unsigned long) -5), 'A', 10))
        {
            break;
        }

        /* Search no characters */
        if (NULL != memchr(obj, obj[0], 0))
        {
            break;
        }

        /* Search for non-existent character */
        if (NULL != memchr(obj, 'a', sizeof(obj)))
        {
            break;
        }

        /* Valid search at beginning */
        if (obj != memchr(obj, obj[0], sizeof(obj)))
        {
            break;
        }

        /* Valid search in middle*/
        if ((obj + 4) != memchr(obj, obj[4], sizeof(obj)))
        {
            break;
        }

        /* Search for null-character at end */
        p = memchr(obj, '\0', sizeof(obj));
        if (strlen(obj) != (size_t) (p - obj))
        {
            break;
        }

        ret = 0;
    } while (0);

    return ret;
}

Conclusion

While memchr represents a simple operation, we must ensure that we follow the standard by using the correct data types for comparison. Aside from this, we see the same pattern used in functions like memcmp to iterate over the object and perform the search operation.

void *
memchr(const void *s, int c, size_t n)
{
    const unsigned char *pCur   = s;
    const unsigned char search  = c;

    if ( !s ||
        /* Check for wrapping while searching */
        ((((unsigned long) -1) - ((unsigned long) s)) < n))
    {
        return NULL;
    }

    while (n-- > 0)
    {
        if (search == *(pCur++))
        {
            return (void *) --pCur;
        }
    }

    return NULL;
}
comments powered by Disqus