TIP: Click on subject to list as thread! ANSI
echo: c_echo
to: Tom Torfs
from: Bob Stout
date: 2003-09-19 18:01:04
subject: Re: Snippets ini.c

From: rbs{at}snippets.org
To: c_echo{at}yahoogroups.com
To: tomtorfs{at}village.uunet.be
Copy: c_echo{at}yahoogroups.com

Quoting tomtorfs{at}village.uunet.be:

> This file contains a function that demonstrates a common programming
> mistake (hidden in the macro LAST_CHAR):
>
> static char *StripTrailingSpaces(char *string)
> {
>       if (!string || (0 == strlen(string)))
>             return NULL;
>
>       while (isspace(LAST_CHAR(string)))
>             LAST_CHAR(string) = NUL;
>
>       return string;
> }
>
> from sniptype.h:
>
> #define LAST_CHAR(s) (((char *)s)[strlen(s) - 1])
>
> So this function (StripTrailingSpaces) calls strlen() twice for each
> trailing space character in the string, and for those that don't know,
> strlen() has  to walk through the string character by character from the
> beginning looking for the trailing nul-terminator. This is just a silly waste
> of CPU cycles and time.

I'm not exactly sure who wrote that, but it was probably me, so I'll answer...

You're right, of course, it is wasteful. In my defense, I can only point out a
couple of justifications:

1.  Many times, string manipulation is used in a context of the user interface
    where the speed at which data arrives is vastly slower than the CPU, so
    efficiency isn't an issue.

2.  Even when efficiency is an issue, we rarely write to wring the last drop of
    memory savings or speed out of our programs, since hardware is so cheap
    compared to or time. If performance is *that* much of an issue, we should
    probably be working in assembly rather than a HLL.

3.  In any discussion of efficient coding, the first issue is always, "How fast
    is fast enough?" If we write something - even something wasteful or hastily
    thrown together - and it runs fast enough, then it's defensible to consider
    ourselves done.

4.  The LAST_CHAR() is a truly handy macro, usable as either an lvalue or an
    rvalue, which this function demonstrates.

The solution, obviously, would have been to use rmtrail(), also from SNIPPETS. I
can't say now why I didn't use it at the time, but since what I was using it for
ran fast enough, it was OK at the time.

> Also, the initial check for a 0-length string performs yet-another strlen()
> on the string in question. The strlen()-scan of the string is not necessary to
> determine if it is 0-length or not. Simply checking the first element of the
> array (or the first character pointed to) will suffice.
>
> Here is a much faster and more efficient version of the function:
>
> static char *StripTrailingSpaces(char *string)
> {
>         size_t len;
>
>         if(string == NULL || *string == 0)
>                 return string; /* why would you return NULL if empty? */
>
>         len = strlen(str);
>         while(len && isspace(string[len-1]))
>                 len--;
>
>         if(string[len] != 0)
>                 string[len] = 0; /* Don't re-terminate, could be static str
> */
>
>         return string;
> }

OK, let's compare this to rmtrail.c from SNIPPETS (the header and C++ bits
elided for clarity)...

----[ snip ]----
#include 
#include 

char *rmtrail(char *str)
{
      int i;

      if (str && 0 != (i = strlen(str)))
      {
            while (--i >= 0)
            {
                  if (!isspace(str[i]))
                        break;
            }
            str[++i] = NUL;
      }
      return str;
}
----[ snip ]----

As you can see, they're essentially identical, with a few differences:

1.  First of all, we both test for a null pointer. However, you next test the
    initial character, and only then call strlen(). Your way is slightly faster,
    but I see little algorithmic reason not to combine the two operations since
    the strlen() call will also effectively test for a leading NUL character.

2.  The speed you gained by the explicit test for NUL before the while() loop,
    you now give back with a questionable test for NUL after the while() loop. I
    also don't understand your comment about static pointers. The call to
    strlen() guaranteed that all the chars you passed over in the while() loop
    were non-NUL, so this test seems totally extraneous.

3.  An obvious flaw in both functions is the chance that the buffer will be
    under-run. After all, we have no assurance that the byte in memory just
    prior to the beginning of the string isn't NUL.

I'll fix #3 for rmtrail.c this weekend... ;-)

-------------------------------------------------------------
Consulting: http://www.MicroFirm.biz/
Web graphics development: http://Image-Magicians.com/
Software archives: http://snippets.snippets.org/
  c.snippets.org/   cpp.snippets.org/      java.snippets.org/
  d.snippets.org/   python.snippets.org/   perl.snippets.org/
  dos.snippets.org/ embedded.snippets.org/ apps.snippets.org/
Audio and loudspeaker design:
  http://LDSG.snippets.org/   http://www.diyspeakers.net/


-------------------------------------------------
This mail sent through IMP: http://horde.org/imp/

--- SoupGate-Win32 v1.05
* Origin: rbs{at}snippets.org (2:292/516.666)
SEEN-BY: 633/267 270
@PATH: 292/516 854 140/1 106/2000 633/267

SOURCE: echomail via fidonet.ozzmosis.com

Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.