memchr
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;
}