K&R2, exercise 5-4, strend(s,t)

A

arnuld

K&r2, exercise 5-4, page 107

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


#include <stdio.h>
#include <string.h>

int strend(const char* s1, const char* s2);


int main(void)
{
const char* arr1 = "comp.lang.c";
const char* arr2 = "comp.lang.";

printf("strend(%s, %s) = %d\n", arr1, arr2, strend(arr1, arr2));

return 0;
}


/* return 1 if str2 occurs at the end of str1, zero otherwise */
int strend(const char* str1, const char* str2)
{
int ret;

/* If one of arguments is NULL, there is no pint in going next */
if(NULL == str1 || NULL == str2)
{
ret = 0;
}
else if(strlen(str1) < strlen(str2))
{
ret = 0;
}
else if ('\0' == *str1 && '\0' == *str2)
{
ret = 1;
}
else
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
const char* tmp1 = str1 + len1 - 1;
const char* tmp2 = str2 + len2 - 1;
size_t count = 0;
for(; *tmp1 == *tmp2 && count < len2; --tmp1, --tmp2, ++count)
;

if(count == len2 && *++tmp1 == *++tmp2) ret = 1;
else ret = 0;
}

return ret;
}

==================== OUTPUT =========================
[arnuld@dune C]$ gcc -ansi -pedantic -Wall -Wextra strend.c
[arnuld@dune C]$ ./a.out
strend(comp.lang.c, comp.lang.) = 0
[arnuld@dune C]$
 
I

Ian Collins

K&r2, exercise 5-4, page 107

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


#include<stdio.h>
#include<string.h>

int strend(const char* s1, const char* s2);


int main(void)
{
const char* arr1 = "comp.lang.c";
const char* arr2 = "comp.lang.";

printf("strend(%s, %s) = %d\n", arr1, arr2, strend(arr1, arr2));

You should add test cases for the conditions you are testing as well as
some matches.
return 0;
}


/* return 1 if str2 occurs at the end of str1, zero otherwise */
int strend(const char* str1, const char* str2)
{
int ret;

/* If one of arguments is NULL, there is no pint in going next */
if(NULL == str1 || NULL == str2)
{
ret = 0;
}
else if(strlen(str1)< strlen(str2))

I would do this test last, after you assign the strlen results to len1
and len2 to avoid calling strlen twice for each string.
{
ret = 0;
}
else if ('\0' == *str1&& '\0' == *str2)
{
ret = 1;
}
else
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
const char* tmp1 = str1 + len1 - 1;
const char* tmp2 = str2 + len2 - 1;
size_t count = 0;
for(; *tmp1 == *tmp2&& count< len2; --tmp1, --tmp2, ++count)
;

if(count == len2&& *++tmp1 == *++tmp2) ret = 1;
else ret = 0;
}

return ret;
}

==================== OUTPUT =========================
[arnuld@dune C]$ gcc -ansi -pedantic -Wall -Wextra strend.c
[arnuld@dune C]$ ./a.out
strend(comp.lang.c, comp.lang.) = 0
[arnuld@dune C]$
 
B

Ben Bacarisse

arnuld said:
K&r2, exercise 5-4, page 107

Write the functions strend(s,t), which returns 1 if the string t occurs
at the end of the string s, and zero otherwise.
/* return 1 if str2 occurs at the end of str1, zero otherwise */
int strend(const char* str1, const char* str2)
{
int ret;

/* If one of arguments is NULL, there is no pint in going next */
if(NULL == str1 || NULL == str2)
{
ret = 0;
}
else if(strlen(str1) < strlen(str2))
{
ret = 0;
}
else if ('\0' == *str1 && '\0' == *str2)
{
ret = 1;
}
else
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
const char* tmp1 = str1 + len1 - 1;
const char* tmp2 = str2 + len2 - 1;

Same error as I posted about in another thread. When either string is
"", the tmp1 or tmp2 pointer will be invalid.
size_t count = 0;
for(; *tmp1 == *tmp2 && count < len2; --tmp1, --tmp2, ++count)
;

And, you access the pointer. In fact, you do so for all matching
strings because the decrements will always take one of the tmp pointers
to before the start of the strings. You probably wanted test count
before you test anything else.

BTW, this is for loop abuse! I'd write this as a while loop.
if(count == len2 && *++tmp1 == *++tmp2) ret = 1;
else ret = 0;

This looks wrong but given that the previous loops is wrong I can't
really say exactly how. I think you intended, in the loop, to test
count first and then you'd need this extra test, but I am not sure.

I've found that "end" string processing is almost always clearer if you
point at the null:

const char* tmp1 = str1 + len1;

and you decrement before testing a character:

if (tmp1 > str1 && *--tmp1 == 'X') ...

etc. However, this is a case where you should use strcmp, rather than
rolling your own loop.
 
M

Mark Bluemel

K&r2, exercise 5-4, page 107

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

Your solution presupposes strlen(), so it may as well presuppose
strcmp(). If you don't already have them, write them as building blocks
for this...

As others have suggested one testcase doesn't prove much - even a
stopped clock is right twice a day.

Here's something I threw together.

#include <stdio.h>
#include <string.h>

int strend(const char* s1, const char* s2);

int main(void)
{
const char* arr1[] = { NULL,
"",
"comp.lang.c",
"comp.lang.perl",
"uk.fan.monkees"};
const char* arr2[] = { NULL,
"",
"comp.lang.",
"unutterablyLongString",
"lang.c",
"ees",
"l"
};
int i;
int j;

for (i = 0 ; i < (sizeof arr1/sizeof (char *)); i++) {
for (j = 0 ; j < (sizeof arr2/sizeof (char *)); j++) {
printf("strend(%s, %s) = %d\n", arr1, arr2[j], strend(arr1,
arr2[j]));
}
}

return 0;
}


/* return 1 if str2 occurs at the end of str1, zero otherwise */
int strend(const char* str1, const char* str2)
{
int str1_len;
int str2_len;
const char *comparison_start;

if ((str1 == NULL) || (str2 == NULL)) {
return 0;
}

str1_len = strlen(str1);
str2_len = strlen(str2);

if (str2_len == 0) {
return 0;
}

if (str2_len > str1_len) {
return 0;
}

comparison_start = str1 + str1_len - str2_len;
return !strcmp(comparison_start,str2);
}
 
P

Phil Carmody

Mark Bluemel said:
/* return 1 if str2 occurs at the end of str1, zero otherwise */

This is pretty much what I'd do too, with a few tweaks.
int strend(const char* str1, const char* str2)
{
int str1_len;
int str2_len;

size_t is better.
const char *comparison_start;

if ((str1 == NULL) || (str2 == NULL)) {
return 0;
}

str1_len = strlen(str1);
str2_len = strlen(str2);

if (str2_len == 0) {
return 0;

Doesn't the empty string appear at the end of every string? Or did you mean
str1_len?

Either way, the calculation of the length you've not used yet can be
delayed until this point, so that some trivial cases can be handled
more quickly.
}

if (str2_len > str1_len) {

If you calculate str1_len first, then you can use a strnlen[*] rather than
strlen on str2, in order to be able to bail early on the above condition.
return 0;
}

comparison_start = str1 + str1_len - str2_len;
return !strcmp(comparison_start,str2);

Memcmp doesn't need to keep checking for '\0' characters, which you know
will be absent in the sections you're interested in comparing.

Phil

[* GNU provides one for you, but you might need to write your own,
it's a strlen with a maximum length.]
 
G

Gene

Mark Bluemel said:
/* return 1 if str2 occurs at the end of str1, zero otherwise */

This is pretty much what I'd do too, with a few tweaks.
  int strend(const char* str1, const char* str2)
   {
     int str1_len;
     int str2_len;

size_t is better.
     const char *comparison_start;
     if ((str1 == NULL) || (str2 == NULL)) {
       return 0;
     }
     str1_len = strlen(str1);
     str2_len = strlen(str2);
     if (str2_len  == 0) {
       return 0;

Doesn't the empty string appear at the end of every string? Or did you mean
str1_len?

Either way, the calculation of the length you've not used yet can be
delayed until this point, so that some trivial cases can be handled
more quickly.
     }
     if (str2_len > str1_len) {

If you calculate str1_len first, then you can use a strnlen[*] rather than
strlen on str2, in order to be able to bail early on the above condition.
       return 0;
     }
     comparison_start = str1 + str1_len - str2_len;
     return !strcmp(comparison_start,str2);

Memcmp doesn't need to keep checking for '\0' characters, which you know
will be absent in the sections you're interested in comparing.

Another approach is to keep finding the first occurrence until you
find the last:


// UNCOMPILED; UNTESTED
int strend(const char* str1, const char* str2)
{
int len2;
char *p, *str2loc;

if (str1 && str2) {
len2 = strlen(str2);
// p points to the unsearched part of str1
p = str1;
for (;;) {
char *str2loc = strstr(p, str2);
if (!str2loc)
return 0;
p = str2loc + len2;
if (*p == '\0')
return 1;
}
}
return 0;
}
 
I

Ike Naar

// UNCOMPILED; UNTESTED
int strend(const char* str1, const char* str2)
{
int len2;
char *p, *str2loc;

if (str1 && str2) {
len2 = strlen(str2);
// p points to the unsearched part of str1
p = str1;
for (;;) {
char *str2loc = strstr(p, str2);
if (!str2loc)
return 0;
p = str2loc + len2;
if (*p == '\0')
return 1;
}
}
return 0;
}

You mentioned that the code is uncompiled and untested, and in
fact it has a few compilation errors but they are easy to fix.

One "real" problem: the for loop does not terminate if str2
is the empty string.
 
I

Ike Naar

You mentioned that the code is uncompiled and untested, and in
fact it has a few compilation errors but they are easy to fix.

To Gene: Sorry about the phrase "a few compilation errors"; there's
only a single compilation error in the above fragment (the assigment
``p = str1'' assigns a pointer-to-const to a pointer-to-nonconst).

There was another compilation error, but that was in the code that
I added to turn your code fragment into a complete program.
 
T

Tim Rentsch

Mark Bluemel said:
K&r2, exercise 5-4, page 107

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

Your solution presupposes strlen(), so it may as well presuppose
strcmp(). If you don't already have them, write them as building
blocks for this...
[snip]

/* return 1 if str2 occurs at the end of str1, zero otherwise */
int strend(const char* str1, const char* str2)
{
int str1_len;
int str2_len;
const char *comparison_start;

if ((str1 == NULL) || (str2 == NULL)) {
return 0;
}

str1_len = strlen(str1);
str2_len = strlen(str2);

if (str2_len == 0) {
return 0;
}

if (str2_len > str1_len) {
return 0;
}

comparison_start = str1 + str1_len - str2_len;
return !strcmp(comparison_start,str2);
}


An alternative offered for everyone's reading pleasure
and/or amusement (as each may individually determine).


/* Return 1 if 'tail' occurs at the end of 'whole', or 0 if not. */

int
strend( const char *whole, const char *tail ){
size_t w, t;

return
whole && tail
&& (w = strlen( whole )) >= (t = strlen( tail ))
&& strcmp( whole+w -t, tail ) == 0
;
}
 
A

arnuld

An alternative offered for everyone's reading pleasure and/or amusement
(as each may individually determine).


/* Return 1 if 'tail' occurs at the end of 'whole', or 0 if not. */

int
strend( const char *whole, const char *tail ){
size_t w, t;

return
whole && tail
&& (w = strlen( whole )) >= (t = strlen( tail )) && strcmp(
whole+w -t, tail ) == 0
;
}


Interesting :) , if we assume not to use any of those standard string
library functions (strcmp, strlen etc) then can we accomplish ?
 
M

Mark Bluemel

An alternative offered for everyone's reading pleasure
and/or amusement (as each may individually determine).


/* Return 1 if 'tail' occurs at the end of 'whole', or 0 if not. */

int
strend( const char *whole, const char *tail ){
size_t w, t;

return
whole&& tail
&& (w = strlen( whole ))>= (t = strlen( tail ))
&& strcmp( whole+w -t, tail ) == 0
;
}

Now if you'd used 'lo1' for "whole", "l0l" for "tail", "lol" for "w" and
"l01" for "t", it would have been even more perfect...
 
M

Mark Bluemel

..if we assume not to use any of those standard string
library functions (strcmp, strlen etc) then can we accomplish ?

You have at least two obvious choices :-

1) write strend in its entirety from scratch.
It's not going to be terribly difficult.
This function probably has limited general application.

2) write strlen and strcmp first and then use them to write strend.
strlen and strcmp will probably be more generally useful.
 
T

Tim Rentsch

Mark Bluemel said:
Now if you'd used 'lo1' for "whole", "l0l" for "tail", "lol" for "w"
and "l01" for "t", it would have been even more perfect...

Personally I think the single character identifiers do just fine
here, considering the length of the function, and their obvious
relationship to identifiers 'whole' and 'tail'. But if you think
longer variable names would be helpful, by all means feel free.

If there are some other reservations or objections, please do
the group a favor and try to articulate what they are and why.
 
T

Tim Rentsch

arnuld said:
Interesting :) , if we assume not to use any of those standard string
library functions (strcmp, strlen etc) then can we accomplish ?

Then the function would be written differently. I wrote the
above predicated on the stated presumption of availability of
said functions.
 
M

Mark Bluemel

Personally I think the single character identifiers do just fine
here, considering the length of the function, and their obvious
relationship to identifiers 'whole' and 'tail'. But if you think
longer variable names would be helpful, by all means feel free.

If there are some other reservations or objections, please do
the group a favor and try to articulate what they are and why.

No objections or reservations. My posting was decidedly facetious.

I very much like your solution, but it's terseness somehow made me think
of the IOCC and hence led me to suggest some obvious obfuscations.
 
T

Tim Rentsch

Mark Bluemel said:
No objections or reservations. My posting was decidedly facetious.

Oh okay. I missed the intended tone then.
I very much like your solution, but it's terseness somehow made me
think of the IOCC and hence led me to suggest some obvious
obfuscations.

I know some people think code being terse automatically makes
it obfuscated, but I don't share that opinion. Writing code
compactly sometimes gives good code and sometimes bad, depending
on the particulars. I do think that some aesthetic judgment
is required: choices that work well in short functions often
fall down in longer functions, and vice versa. Being exposed
to different styles is IMO a benefit, and that's a large part
of why I posted this alternative.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

K&R2, exercise 5.4 28
K&R2, exercise 4-2 12
K&R2 Exercise 4-12 14
Fibonacci 0
K&R2 section 2.8 (exercise 2.4) "squeeze" 5
K&R2, exercise 1-23 12
Adding adressing of IPv6 to program 1
K&R2 , section 5.4 2

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top