Super Ted said:
The `strcpy' and `strlen' functions you use are declared in <string.h>.
The `isspace' function is declared in <ctype.h>. You should include
these headers if you use the functions. I believe that the implicit
declaration of `isspace' if you omit <ctype.h> is sufficient to be able
to call it in a well-defined way; but this is not the case for `strcpy'
or `strlen', neither of which return `int' -- the former returns a
`char *' and the latter a `size_t'.
My compiler gives me warnings about the above. You should probably turn
on your compiler's warnings.
#include <ctype.h>
#include said:
static int TrimWSpaceL(char *s)
{
if(isspace(*s)) {
The ismumble functions from <ctype.h> expect to receive their argument
in a rather peculiar form: an `unsigned char' converted to an `int'. If
the `char' type is signed on your system (as it is in many), and the
first character of the string is negative (and not EOF, a special
exemption) then the behaviour is undefined.
The source and destination arguments for `strcpy' mustn't overlap. This
has undefined behaviour. Also, you do this copy for every leading
whitespace character you find: if the string consists entirely of
n whitespace then you move a total of n (n + 1)/2 characters, taking
quadratic time to solve a linear-time problem.
This is not a tail position. You have potentially unbounded non-tail
recursion, and therefore run the risk of exhausting the stack. There is
no reason to use more than a constant amount of memory to solve this
problem.
Also, the type `int' may be too small to store the number of whitespace
characters removed from the string. I'd use `size_t' instead.
static size_t trimspace_l(char *p)
{
const char *q;
for (q = p; *q && isspace((unsigned char)*q); q++);
if (q > p) memmove(p, q, strlen(q) + 1);
return (q - p);
}
static int TrimWSpaceR(char *s)
{
if(isspace(*(s+strlen(s)-1))) {
Same remarks about isspace again. Many people would find
`s[strlen(s) - 1]' clearer, though the two are entirely equivalent.
More seriously, this has a bug if the string is empty: strlen(s) is
zero, so this is s[-1], probably outside the bounds of the string, and
undefined behaviour.
The `strlen' function has to scan the entire string trying to find a
zero byte. For long strings, this takes a fair amount of time. You
call it twice for each trailing whitespace character. Again, this leads
to quadratic running time for ought to be a linear-time operation.
And again the unbounded recursion, using linear space for what ought to
be a constant-space operation. And again, `int' may not be large
enough.
static size_t trimspace_r(char *p)
{
char *q = p + strlen(p);
const char *r = q;
while (q > p && isspace((unsigned char)q[-1])) *--q = 0;
return (r - q);
}
/* trims white-space, returning #spaces trimmed */
int TrimWSpace(char *s)
{
return TrimWSpaceL(s) + TrimWSpaceR(s);
This performs the two trimmings in an arbitrary order. Either order is
correct, but it's slightly (negligibly!) more efficient to trim the
right hand side first (why?).
static size_t trimspace(char *p)
{
size_t n = 0;
n += trimspace_r(p);
n += trimspace_l(p);
return (n);
}
It's more usual nowadays to write `int main(void)' in C. The above
doesn't provide a `prototype', which probably doesn't matter for `main'
because calling `main' is usually only done in IOCCC submissions.
{
char s[] = " \t *Hello world* \n ";
int i = TrimWSpace(s);
printf("%s\n(deleted %d spaces)\n", s,i);
return 0;
}
This seems OK.
However I get an unexpected output:
*Hello world
(deleted 11 spaces)
Both lines are wrong! In the first line, a * has disappeared. In the 2nd
line, 11 spaces are reported, not 10!
I don't know which of the mistakes I've pointed above causes this. (On
my system, I get the expected output for your program. That doesn't
mean that the mistakes aren't serious -- only that they don't cause
observable failures on one particular machine with one particular
compiler version using one particular collection of compiler options.
-- [mdw]