HiveBrain v1.2.0
Get Started
← Back to all entries
patterncModerate

Determine if one string occurs at the end of another

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
theoneanotherdetermineendoccursstring

Problem

Exercise 5-4


Write the function strend(s,t), which returns one if the string t occurs at the end of the string s, and zero otherwise.

#include 

int str_end(const char *, const char*);

int main(void)
{
    char *s1 = "Man is a rope stretched over an abyss.";
    char *s2 = "an abyss.";

    printf("%s\n", str_end(s1, s2) ? "Yes" : "No");
    return 0;
}

int str_end(const char *s, const char *t)
{
    const char *init = t;       /* Hold the initial position of *t */

    while (*s) {
        while (*s == *t) { 
            if (!(*s)) {
                return 1;
            }
            s++;
            t++;
        }
        s++;
        t = init;
    }
    return 0;
}

Solution

A few notes:

-
Analyzing your code, I see a simple bug that isn't accounted for. Consider the following case:

char *s1 = "Man is a rope stretched over an abyss.";
char *s2 = "an abyss..c9f0c50bc2e417b078e3b0bf82d5386d418cfba4";


Your method still returns 1 even though it clearly shouldn't, because you only checked for \0 in s and not in t. You need to check if the NULL terminators are in matching positions. Also, s2 is clearly a larger string than s1, we never compared the lengths to see if s2 could even fit into s1.

-
You are somewhat approaching this inefficiently. You are comparing all of the characters in the string. You don't need to do this, we can just compare at the index strlen(s) - strlen(t).

-
You could shorten your while loop a bit.

while (*s == *t) { 
        if (!(*s)) {
            return 1;
        }
        s++;
        t++;
    }


Increment s and t inside of your while test conditional.

-
You could also forgo the while loop completely and use the standard function strncmp(). However, I would use memcmp() since it is faster.

-
You wouldn't need the function prototype if you define your strend() function before you define main().

-
What if you pass an invalid string to your function? Right now it can't handle it and will throw a "Segmentation fault". That's not any fun, so let's implement a simple test to check for that.

if (!s || !t) return 0;


Based on the discussion between @RolandIllig and myself, I have decided to not implement this check. Why? Because this is a runtime check that comes with a certain overhead time to process. That time may not be much, but on certain systems (such as embedded ones) that processing power would be better used somewhere else. Therefore, I decided to follow the philosophy behind the design of the C standard library: that the programmer is ultimately in the best position to know whether a runtime check really needs to be performed.

-
I'm not sure how you want to handle empty strings. I'm not going to mess with that too much since there was nothing stating so in the exercise.

-
You should document all of your code. Even for a simple function such as this.

Final Product

#include   // strlen(), memcmp()

/**
 * @fn int strend(const char *s, const char *t)
 * @brief Searches the end of string s for string t
 * @param s the string to be searched
 * @param t the substring to locate at the end of string s
 * @return one if the string t occurs at the end of the string s, and zero otherwise
 */
int strend(const char *s, const char *t)
{
    size_t ls = strlen(s); // find length of s
    size_t lt = strlen(t); // find length of t
    if (ls >= lt)  // check if t can fit in s
    {
        // point s to where t should start and compare the strings from there
        return (0 == memcmp(t, s + (ls - lt), lt));
    }
    return 0; // t was longer than s
}


Tests and Benchmarking

Here are a few trial runs I did of the method, and their output. All seems well by the tests.

-
strend("Man is a rope stretched over an abyss.", "an abyss..c9f0c"); returns 0.

-
strend("Man is a rope stretched over an abyss.", "an abyss"); returns 0.

-
strend("Man is a rope stretched over an abyss.", "an abyss."); returns 1.

-
strend("Man is an man man.", "an man."); returns 1.

-
strend("an abyssan abyssan abyssan abyss.", "an abyss."); returns 1.

-
strend(NULL, NULL); returns 0.

Using a benchmarking program I wrote to profile the code, I tested your function as well as the others supplied against mine. Here are the results, averaged out over 100000000 test iterations:

Search string: simple
End string: le
syb0rg's function average runtime: 1.47728e-08 seconds
ao2130's function average runtime: 1.87163e-08 seconds
Edward's function average runtime: 2.29287e-08 seconds
Josay's function average runtime: 2.58304e-08 seconds

Search string: this is a test
End string: test
syb0rg's function average runtime: 1.81173e-08 seconds
ao2130's function average runtime: 4.7522e-08 seconds
Edward's function average runtime: 2.31506e-08 seconds
Josay's function average runtime: 4.62652e-08 seconds

Search string: this is a longer string and may cause certain functions to take a bit longer to process
End string: a bit longer to process
syb0rg's function average runtime: 3.17312e-08 seconds
ao2130's function average runtime: 2.44403e-07 seconds
Edward's function average runtime: 3.33028e-08 seconds
Josay's function average runtime: 3.25736e-07 seconds


As you can see, my function runs a tiny bit faster than yours and the other functions supplied here (watch the scientific notation).

Code Snippets

char *s1 = "Man is a rope stretched over an abyss.";
char *s2 = "an abyss..c9f0c50bc2e417b078e3b0bf82d5386d418cfba4";
while (*s == *t) { 
        if (!(*s)) {
            return 1;
        }
        s++;
        t++;
    }
if (!s || !t) return 0;
#include <string.h>  // strlen(), memcmp()

/**
 * @fn int strend(const char *s, const char *t)
 * @brief Searches the end of string s for string t
 * @param s the string to be searched
 * @param t the substring to locate at the end of string s
 * @return one if the string t occurs at the end of the string s, and zero otherwise
 */
int strend(const char *s, const char *t)
{
    size_t ls = strlen(s); // find length of s
    size_t lt = strlen(t); // find length of t
    if (ls >= lt)  // check if t can fit in s
    {
        // point s to where t should start and compare the strings from there
        return (0 == memcmp(t, s + (ls - lt), lt));
    }
    return 0; // t was longer than s
}

Context

StackExchange Code Review Q#54722, answer score: 19

Revisions (0)

No revisions yet.