Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pointer arithmetic question.

96 views
Skip to first unread message

Adrian Sch

unread,
Oct 30, 2012, 1:33:28 PM10/30/12
to
Hello! I am doing some exercises from K&R 2nd edition and I seem to be stuck: Here's a working copy of a custom strcpy:

char *strcpy(char * s, char * t)
{

char x;
while(x=*s)
s++;

while((*s++=*t++))
;

return ;
}
int main(void)
{
char message[100]="string1";
char * message2="string2";
strcpy(message, message2);
printf("%s", message);
return 0;

}

And here's a version that doesn't work:
char *strcpy(char * s, char * t)
{

while(*s++)
;

while((*s++=*t++))
;

return ;
}
int main(void)
{
char message[100]="string1";
char * message2="string2";
strcpy(message, message2);
printf("%s", message);
return 0;

}
It seems that for some reason the while(*s++) is 0 so it doesn't increment. Why? What am I missing here?

tom st denis

unread,
Oct 30, 2012, 1:53:42 PM10/30/12
to
On Oct 30, 1:33 pm, Adrian Sch <gloha...@gmail.com> wrote:
> Hello! I am doing some exercises from K&R 2nd edition and I seem to be stuck: Here's a working copy of a custom strcpy:
>
> char *strcpy(char * s, char * t)
> {
>
> char x;
> while(x=*s)
> s++;
>
> while((*s++=*t++))
> ;
>
> return ;}
>
> int main(void)
> {
> char message[100]="string1";
> char * message2="string2";
> strcpy(message, message2);
> printf("%s", message);
> return 0;
>
> }
>
> And here's a version that doesn't work:
> char *strcpy(char * s, char * t)
> {
>
> while(*s++)
> ;

Ask your self, where does "s" point now. (hint: compare the value of
's' in this version against the other via printf("%p\n", s)).

> while((*s++=*t++))
> ;
>
> return ;}
>
> int main(void)
> {
> char message[100]="string1";
> char * message2="string2";
> strcpy(message, message2);
> printf("%s", message);
> return 0;
>
> }
>
> It seems that for some reason the while(*s++) is 0 so it doesn't increment. Why? What am I missing here?

Once you figure out where 's' points to after the while(*s++) loop
you'll figure out the bug in the 2nd copy.

Tom

Greg Martin

unread,
Oct 30, 2012, 2:00:47 PM10/30/12
to
I made a couple of changes to your program. Some to make my compiler
happy, some for readability and one to make the program work. The
pre-increment will return the value after incrementing where as the
post-increment returns it then increments the value. In your version you
begin the to write after the NIL, in mine I overwrite it.

#include <stdio.h>

char* strcpy(char* s, char* t) {
while(*++s);

while((*s++=*t++) != '\0');

return s;
}

int main(void) {
char message[100]="string1";
char* message2="string2";

strcpy(message, message2);
printf("%s\n", message);

return 0;
}

BartC

unread,
Oct 30, 2012, 2:03:04 PM10/30/12
to
"Adrian Sch" <gloh...@gmail.com> wrote in message
news:8651baee-14bd-488d...@googlegroups.com...
> Hello! I am doing some exercises from K&R 2nd edition and I seem to be
> stuck: Here's a working copy of a custom strcpy:
>
> char *strcpy(char * s, char * t)
> {
>
> char x;
> while(x=*s)
> s++;
>
> while((*s++=*t++))
> ;
>
> return ;
> }

Do you mean strcat rather than strcpy?

--
Bartc

Ben Bacarisse

unread,
Oct 30, 2012, 2:04:09 PM10/30/12
to
Adrian Sch <gloh...@gmail.com> writes:

> Hello! I am doing some exercises from K&R 2nd edition and I seem to be
> stuck: Here's a working copy of a custom strcpy:

No, it's a not-quite working version on strcat.

> char *strcpy(char * s, char * t)
> {
>
> char x;
> while(x=*s)
> s++;

What is x for? You never use it.

> while((*s++=*t++))
> ;
>
> return ;

You compiler should have complained to you about this. Adjust the
warning level to the highest you can. There's no reason not to get the
maximum help you can.

> }
> int main(void)
> {
> char message[100]="string1";
> char * message2="string2";
> strcpy(message, message2);
> printf("%s", message);
> return 0;
>
> }

Also, get into the habit of layingout your code using some standard or
other. I know that the above layout could have been due to Google's
awful Usenet interface, but some of it is yours. Pick a style and stick
to it.

> And here's a version that doesn't work:
> char *strcpy(char * s, char * t)
> {
>
> while(*s++)
> ;
>
> while((*s++=*t++))
> ;
>
> return ;
> }
> int main(void)
> {
> char message[100]="string1";
> char * message2="string2";
> strcpy(message, message2);
> printf("%s", message);
> return 0;
>
> }
> It seems that for some reason the while(*s++) is 0

That makes not sense. A while loop always ends when it's condition is
zero. That's as true of the working as of the the non-working version.

> so it doesn't increment. Why? What am I missing here?

Play computer: draw a string and a pointer to it, then step through what
this loop does until you see what's wrong. I'll gladly tell you, but
you'll learn more by stepping through it yourself:

+------------+
s: | o |
+-----+------+
|
V
+-----+-----+-----+-----+-----+-----+-----+-----+
| s | t | r | i | n | g | 1 | \0 |
+-----+-----+-----+-----+-----+-----+-----+-----+

(you'll need a fixed-width font to see this.)

--
Ben.

James Kuyper

unread,
Oct 30, 2012, 2:41:23 PM10/30/12
to
The 'x' is irrelevant. You may find it easier to understand what's going
on if you remove it. Your working version had the equivalent of:

while(*s) s++;

The version that didn't work had the equivalent of:

while(*s++);

When s points at "string1", how many times does s++ get executed with
the working version of the code? How many times with the version that
failed? In each case, where does s point after the loop is finished
executing?
--
James Kuyper

Eric Sosman

unread,
Oct 30, 2012, 3:11:01 PM10/30/12
to
On 10/30/2012 1:33 PM, Adrian Sch wrote:
> Hello! I am doing some exercises from K&R 2nd edition and I seem to be stuck: Here's a working copy of a custom strcpy:
>
> char *strcpy(char * s, char * t)
> {
>
> char x;
> while(x=*s)
> s++;
>
> while((*s++=*t++))
> ;
>
> return ;
> }

That's not a strcpy() work-alike. Looks more like strcat(),
which is a different breed of, well, cat.

> And here's a version that doesn't work:
> char *strcpy(char * s, char * t)
> {
>
> while(*s++)
> ;
>
> while((*s++=*t++))
> ;
>
> return ;
> }

This isn't a work-alike for any of the standard string
functions, and certainly not for strcpy() or strcat(). A name
like strabut() might describe what it does, which is: "Copy the
`t' string to the memory area just after the `s' string, leaving
the '\0' at the end of `s' undisturbed."

I think what you've overlooked is that the test in the first
`while' loop will detect the '\0' character *and* will advance
`s' past that character. If you want to append the `t' characters
to `s', you should deposit the first of them right where the '\0'
was originally; as things stand, you're depositing that first
character just after the '\0'.

--
Eric Sosman
eso...@comcast-dot-net.invalid

Eric Sosman

unread,
Oct 30, 2012, 3:17:01 PM10/30/12
to
On 10/30/2012 2:00 PM, Greg Martin wrote:
> [...]
> I made a couple of changes to your program. Some to make my compiler
> happy, some for readability and one to make the program work. The
> pre-increment will return the value after incrementing where as the
> post-increment returns it then increments the value. In your version you
> begin the to write after the NIL, in mine I overwrite it.
>
> #include <stdio.h>
>
> char* strcpy(char* s, char* t) {
> while(*++s);

This fails if `s' points to an empty string: It skips right
over the '\0' at the beginning and starts exploring the trackless
wilderness after the terminator. (It's very bad to find yourself
on the wrong side of The Terminator -- just ask Ahhnold.)

--
Eric Sosman
eso...@comcast-dot-net.invalid

Greg Martin

unread,
Oct 30, 2012, 3:21:33 PM10/30/12
to
Very true. Thanks Eric. There's a reason I hate those little shortcuts!

Adrian Sch

unread,
Oct 30, 2012, 3:50:10 PM10/30/12
to
marți, 30 octombrie 2012, 20:00:49 UTC+2, Greg Martin a scris:
> On 12-10-30 10:33 AM, Adrian Sch wrote:
>
> I made a couple of changes to your program. Some to make my compiler
>
> happy, some for readability and one to make the program work. The
>
> pre-increment will return the value after incrementing where as the
>
> post-increment returns it then increments the value. In your version you
>
> begin the to write after the NIL, in mine I overwrite it.
>
>
>
> #include <stdio.h>
>
>
>
> char* strcpy(char* s, char* t) {
>
> while(*++s);
>
>
>
> while((*s++=*t++) != '\0');
>
>
>
> return s;
>
> }
>
>
>
> int main(void) {
>
> char message[100]="string1";
>
> char* message2="string2";
>
>
>
> strcpy(message, message2);
>
> printf("%s\n", message);
>
>
>
> return 0;
>
> }

First, thanks to all of you for the replies.

Second, you are right, this a not-working strcat version.

Third, this is a very subtle problem and quite hard to identify considering my limited knowledge. Because my real question was why the "while(*s++);" gets skiped in the code. Because I was using a debuger and I was expecting some additional action. But because there was no block of code to execute and go back again to the while test, the while test was executed at once (very fast). Am I right or wrong? Because that seemed stupid to me, how the while get skipped while *s was pointing to s.
Now for the real question, that I didn't ask. I figured that the subtlety of the postfix increment is causing my trouble. Because once the *s is pointing at null, it is incremented one more time and the future copies of *t value go after the null. So when I am printing message, I have concatenated the string, but unfortunately after the null, and that makes it just (not random) garbage as far as printf is concerned.
I used a quick hack to fix it: using the prefix operator. That worked! But I think that this is wrong because if the first string's first character was null,then I'd repeat the whole affair from before and have concatenated the string after the null.

Forth, here's the fix(as sugested by some of you):
char *strcpy(char * s, char * t)
{

while(*s)
s++;

while((*s++=*t++))
;

return ;
}

Fifth, Is there any other way to keep simple and elegant, but on just one line of code? Or maybe a messier way, but also to fit in the while testing block? I am thinking and if else shortcut notation, but I don't master the idiom that well to do this apparently.

Keith Thompson

unread,
Oct 30, 2012, 4:18:45 PM10/30/12
to
Adrian Sch <gloh...@gmail.com> writes:
> Hello! I am doing some exercises from K&R 2nd edition and I seem to be
> stuck: Here's a working copy of a custom strcpy:
>
> char *strcpy(char * s, char * t)
> {
>
> char x;
> while(x=*s)
> s++;
>
> while((*s++=*t++))
> ;
>
> return ;
> }
> int main(void)
> {
> char message[100]="string1";
> char * message2="string2";
> strcpy(message, message2);
> printf("%s", message);
> return 0;
>
> }
[...]

As several other people have pointed out, that's an implementation of
strcat, not of strcpy.

Here's a reformatted version:

char *strcpy(char *s, char *t)
{
char x;
while(x = *s)
s++;

while((*s++ = *t++))
;
return;
}

int main(void)
{
char message[100] = "string1";
char *message2 = "string2";
strcpy(message, message2);
puts(message);
return 0;
}

I've fixed the indentation and added consistent whitespace in
some places. I've also changed your printf() call to puts(),
so that it prints a newline after the message.

Some remarks, in addition to what others have said:

You need to add "#include <stdio.h>" if you're going to call either
printf or puts.

The names "strcpy" and "strcat" are reserved, as are all identifiers
starting with "str" (or "mem", or "wcs") and a lowercase letter.
Which means that, unless you're actually implementing the C standard
library, you shouldn't call your own function "strcpy" or "strcat".
You could call it "str_cat", for example, or "my_strcat".

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Eric Sosman

unread,
Oct 30, 2012, 5:32:09 PM10/30/12
to
On 10/30/2012 3:50 PM, Adrian Sch wrote:
> [...]
> Forth, here's the fix(as sugested by some of you):
> char *strcpy(char * s, char * t)
> {
>
> while(*s)
> s++;
>
> while((*s++=*t++))
> ;
>
> return ;

Looks like you didn't read Ben Bacarisse's response.

> }
>
> Fifth, Is there any other way to keep simple and elegant, but on just one line of code? Or maybe a messier way, but also to fit in the while testing block? I am thinking and if else shortcut notation, but I don't master the idiom that well to do this apparently.

Yes! The one line you need is

#include <string.h>

That is, in real programs you should use the library's functions
instead of writing your own replacements. But this isn't a "real
program;" it's a learning exercise. So, as a learning challenge:
Can you get the whole thing on one line?

If your screen is wide enough, the answer, again, is Yes!

char*mystrcpy(char*s,char*t){while(*s)s++;while(*s++=*t++);return/*wrong!*/;}

Okay, okay, so how about "one loop" instead of "one line?"

Once again, Yes!

char *mystrcpy(char *s, char *t) {
for (s += strlen(s); *s++ = *t++; );
return /*wrong*/ ;
}

But if you don't want to use existing library functions then I
think the answer is No. You need to do two distinct things: Find
the end of the `s' string, and copy the `t' characters to it. It's
*possible* to combine those two operations into one loop, but I can
think of no way that could be called "elegant" or "simple."

--
Eric Sosman
eso...@comcast-dot-net.invalid

Adrian Sch

unread,
Oct 30, 2012, 6:34:33 PM10/30/12
to
> On 10/30/2012 3:50 PM, Adrian Sch wrote:
>
>> [...]
>> Forth, here's the fix(as sugested by some of you):
>> char *strcpy(char * s, char * t)
>> {
>> while(*s)
>> s++;
>> while((*s++=*t++))
>> ;
>> return ;
>>
> Looks like you didn't read Ben Bacarisse's response.
>
I did read it. If you are referring to code formatting then know that I am formatting my code, but it just how it looks when I paste it here.

Adrian Sch

unread,
Oct 30, 2012, 6:45:37 PM10/30/12
to

> Can you get the whole thing on one line?
>
>
>
> If your screen is wide enough, the answer, again, is Yes!
>
>
>
> char*mystrcpy(char*s,char*t){while(*s)s++;while(*s++=*t++);return/*wrong!*/;}
>
>
This is kind of obvious, and is not what I was having in mind, but thanks for the tip anyway:D



>
> Okay, okay, so how about "one loop" instead of "one line?"
>
>
>
> Once again, Yes!
>
>
>
> char *mystrcpy(char *s, char *t) {
>
> for (s += strlen(s); *s++ = *t++; );
>
> return /*wrong*/ ;
>
> }
>
This is interesting!

>
>
> But if you don't want to use existing library functions then I
>
> think the answer is No. You need to do two distinct things: Find
>
> the end of the `s' string, and copy the `t' characters to it. It's
>
> *possible* to combine those two operations into one loop, but I can
>
> think of no way that could be called "elegant" or "simple."
>
>
>
> --
>
> Eric Sosman
>
> eso...@comcast-dot-net.invalid

So there's no way to translate
while(*s)
s++;

into something like (pseudocode)
while(*s?s++:break)
the code above doesn't compile. Why is not possible to cram a if statement into a while testing block? Stupid question probably, but that's my way of learning.

Ben Bacarisse

unread,
Oct 30, 2012, 6:55:34 PM10/30/12
to
Eric is referring to the missing expression in the return statement and,
possibly, my associated comments about getting your compiler to tell you
as much as you can get it to. You shouldn't pass up the chance to get a
helping hand from the compiler. By default, I compile with as many
warnings turned on as I can.

Some warnings you will then ignore, but you'll learn from finding out
why you can ignore a particular warning in a particular situation.

--
Ben.

Ben Bacarisse

unread,
Oct 30, 2012, 7:20:21 PM10/30/12
to
Adrian Sch <gloh...@gmail.com> writes:
<snip>
> So there's no way to translate
> while(*s)
> s++;

(I've fixed the indentation -- it's so important in fragments like this)

> into something like (pseudocode)
> while(*s?s++:break)

Yes. Borrowing your conditional expression, you can write

while (*s ? s++ : 0);

but

while (*s && s++);

is simpler. However, the original is simpler still so there is nothing
at all to be gained.

You could also stick with while (*s++); and compensate for being one
place ahead of yourself later, but that works out (in that case) to be
even worse.

> the code above doesn't compile. Why is not possible to cram a if
> statement into a while testing block? Stupid question probably, but
> that's my way of learning.

It's fun to try these things out but they are often worse than you
imagine. For one thing, simpler can often be harder to read, and most
code is read far more often that it's written (and frequently by people
who have no idea what on earth you were thinking when you wrote it).

But, as I said, it can be fun. I just wasted a few minutes writing both
loops as one, but I didn't post it because the results were, as
expected, awful. I came up with:

while (*s || *t)
if (*s) s++;
else *s++ = *t++, *s = 0;

and

while (*s || *t)
if (!*s++) s[-1] = *t++, *s = 0;

and even (with it all in the condition of the loop):

char *r = 0;
while (*s++ || !r && (r = s - 1) || (*r++ = *t++));

(In my defence, I never though it would be worth the attempt -- the
desire to tinker simply got the better of me.)

--
Ben.

James Kuyper

unread,
Oct 30, 2012, 7:23:42 PM10/30/12
to
On 10/30/2012 06:45 PM, Adrian Sch wrote:
>
>> Can you get the whole thing on one line?
...
>> But if you don't want to use existing library functions then I
>> think the answer is No. You need to do two distinct things: Find
>> the end of the `s' string, and copy the `t' characters to it. It's
>> *possible* to combine those two operations into one loop, but I can
>> think of no way that could be called "elegant" or "simple."
...
> So there's no way to translate
> while(*s)
> s++;

When you talked about "just one line of code", Eric apparently assumed
(as did I) that you were referring to the entire function. It's not
possible to make that code any simpler or more elegant, as you
requested, but it is trivial to turn it into a one-liner:

while(*s) s++;

> into something like (pseudocode)
> while(*s?s++:break)
> the code above doesn't compile. Why is not possible to cram a if statement into a while testing block? Stupid question probably, but that's my way of learning.

The problem with that piece of code is that while statements can contain
expressions, expressions can never contain statements.
The third operand of the ?: operator is required to be a conditional
expression. The "break" keyword isn't any kind of an expression, it's
only permitted use is as part of a break statement. Break statements are
only allowed inside the body of a while() loop or a switch statement.
You're trying to use it in the controlling condition, which is required
to be an expression; it's not part of the loop body.

The expression 0 would be perfectly acceptable in this context, and
would have the same effect you're trying to achieve by using "break":

while(*s?s++:0);

You could also use

while(*s && s++);

but both of those are unnecessarily "clever", and I don't mean that as a
compliment. If I made a mistake in writing them down (which is quite
possible), that would demonstrate why such "cleverness" is a bad idea. I
don't think that either of those statements is any kind of improvement over

while(*s) s++;
--
James Kuyper

Greg Martin

unread,
Oct 30, 2012, 8:32:16 PM10/30/12
to
On 12-10-30 04:20 PM, Ben Bacarisse wrote:

>
> while (*s || *t)
> if (*s) s++;
> else *s++ = *t++, *s = 0;
>
> and
>
> while (*s || *t)
> if (!*s++) s[-1] = *t++, *s = 0;
>
> and even (with it all in the condition of the loop):
>
> char *r = 0;
> while (*s++ || !r && (r = s - 1) || (*r++ = *t++));
>
> (In my defence, I never though it would be worth the attempt -- the
> desire to tinker simply got the better of me.)
>

That kind of thinking can lead to no good. :)
http://www.ioccc.org/2012/blakely/blakely.c

Eric Sosman

unread,
Oct 30, 2012, 8:33:12 PM10/30/12
to
Perhaps you should pay attention to other things Ben wrote,
referring to places I highlighted with /*wrong*/ comments...

(Kudos to Ben, by the way: On first reading, I completely
missed the problem he pointed out.)

--
Eric Sosman
eso...@comcast-dot-net.invalid

S R

unread,
Nov 2, 2012, 6:03:13 AM11/2/12
to
On Oct 31, 4:20 am, Ben Bacarisse <ben.use...@bsb.me.uk> wrote:

[snip]

>
>      char *r = 0;
>      while (*s++ || !r && (r = s - 1) || (*r++ = *t++));
^^^^^
I think there is a problem with the above. Once we reach the end of
string s, s is not guaranteed to point to a location containing 0 as
its value, assuming s as being sent in by the caller of the function
(in your earlier examples you made ",*s =0" which was the correct).
The concatenation happens through r but the check *s++ is problematic,
isn't it?

Cheers,

--
SR



Ben Bacarisse

unread,
Nov 2, 2012, 8:29:30 AM11/2/12
to
Yes, you're right. I don't know how that got through the test cases
because I had one to cover that situation -- I had the same bug in the
previous version until I added this test case.

There's another bug as well. Because the middle condition is true but
copies no characters, s is incremented once more it needs to be. We have
to assume that s points somewhere big enough for the concatenated string
so incrementing s along with r is safe provided we don't go too far. If
the target is only just big enough, s will be incremented beyond the
specially permitted "one past the end" position.

Oh well... I don't think I'll offer a fix since the "challenge" of
cramming it all in the condition seems a bit old now.

--
Ben.
Message has been deleted

Prathamesh Kulkarni

unread,
Nov 2, 2012, 11:31:42 AM11/2/12
to
A one-liner attempt for strcat.
http://pastebin.com/LUYcE46F

Regards,
Prathamesh

pete

unread,
Nov 3, 2012, 12:16:49 AM11/3/12
to
Prathamesh Kulkarni wrote:
>
> An attempt for one-line condition for strcat.
>
> char *mystrcat(char *s, char *t)
> {
> char *r = 0, *temp = s;
>
> while ((*s && s++) || (r == 0 && (r = s) || (*r++ = *t++)))
> ;
> return temp;
> }

It looks good.

You could have eliminated two pairs of parentheses,
if that kind of thing appeals to you.

--
pete
0 new messages