473,396 Members | 1,966 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Is this str_rev() ok?

I was messing around with an exercise I found.
This seems to work, except in debugging, some VERY odd stuff happens,
so I thought I'd chuck the code on here to see how it stands up.

I had to make char *y global in order to free it. Disgusting.

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

char *rev_str(char *);
char *y;
int main(void)
{
char *stToRev = "Dontchajustwishthisstringwouldgetreversed";
printf("%s\n", rev_str(stToRev));
free(y);
return 0;
}

char *rev_str(char *x)
{
size_t arrLength = strlen(x)+1;
y = malloc(arrLength);
memcpy(y,x,arrLength);
y = &y[arrLength-1];
while (*--y = *x++);
*y++;
return y;
}

Jun 27 '06 #1
18 1490
ballpointpenthief said:
I was messing around with an exercise I found.
This seems to work, except in debugging, some VERY odd stuff happens,
so I thought I'd chuck the code on here to see how it stands up.

I had to make char *y global in order to free it. Disgusting.
No, you could have done this:

char *ptr = rev_str(stToRev);
if(ptr != NULL)
{
printf("%s\n", ptr);
free(ptr);
}

No need for file scope.

<snip>
char *rev_str(char *x)
Since you don't plan to change x, why not make it const char *?
{
size_t arrLength = strlen(x)+1;
y = malloc(arrLength);
memcpy(y,x,arrLength);
That's fine (except that you probably want to use a local for y, rather than
a file scope object), provided malloc succeeds. But if it fails, you really
really really don't want to be memcpying to the result.

So check for NULL first.
y = &y[arrLength-1];
Don't mod that pointer. Use a temp. Note that, if arrLength is 1 (which it
will be if x is ""), you are at the beginning of the array...
while (*--y = *x++);


....which means this bit will break.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Jun 27 '06 #2

Richard Heathfield wrote:
No, you could have done this:


What this?

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

char *rev_str(char *);
int main(void)
{
char *stToRev = "A man, a plan, a canal; Panama.";
char *ptr = rev_str(stToRev);
if (ptr)
{
printf("%s\n", ptr);
free(ptr);
}
return 0;
}

char *rev_str(char *x)
{
char *tmp, *y;
size_t arrLength = strlen(x)+1;

y = malloc(arrLength);
if (y == NULL)
return NULL;
if (!memcpy(y,x,arrLength))
return NULL;
tmp = &y[arrLength-1];
while (*--tmp = *x++);
return y;
}

Is the debugger I'm using broken then? It's doing some ODD stuff.
There is also a complaint about the while (*--tmp = *x++); when showing
all warnings:

Wedit output window build: Tue Jun 27 19:40:31 2006
Warning c:\lcc\projects\str_rev.c: 29 Assignment within conditional
expression
Compilation + link time:0.1 sec, Return code: 0

Jun 27 '06 #3
On 27 Jun 2006 11:00:34 -0700, "ballpointpenthief"
<Ma*************@gmail.com> wrote in comp.lang.c:
I was messing around with an exercise I found.
This seems to work, except in debugging, some VERY odd stuff happens,
so I thought I'd chuck the code on here to see how it stands up.
Very odd stuff indeed.
I had to make char *y global in order to free it. Disgusting.
No, you didn't. See the lines I added to your main().

I'm going to pretend you used a much smaller original string for
illustrative purposes, namely "123".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *rev_str(char *);
Make the prototype:

char *rev_str(const char *);

....since you don't modify the input string and the function is safe
for string literals and constant strings.
char *y;
int main(void)
{
char *stToRev = "Dontchajustwishthisstringwouldgetreversed";
char *rev = rev_str(stTdRev);
printf("%s\n", rev_str(stToRev));
free(y);
Now replace the two lines above with:

printf("%s\n", rev);
free(rev);
return 0;
}

char *rev_str(char *x)
Again:

char *rev_str(const char *x)
{
size_t arrLength = strlen(x)+1;
Good, adding the +1. For the string "123", arrLength is 4.
y = malloc(arrLength);
Bad, not checking for a NULL return from malloc().
memcpy(y,x,arrLength);
y = &y[arrLength-1];
Both lines above invokes undefined behavior if malloc() was
unsuccessful and returned a null pointer. Also it is completely
unnecessary, what do you gain by copying the contents in the original
order if you are only going to write over them with the reverse order?
If all you wanted to do was '\0' terminate the new string, it would
surely be more efficient, and much clearer, to do:

y += arrLength - 1;
*y = '\0';
while (*--y = *x++);
For the moment, let's assume your memcpy() code is still in place, so
as this loop starts, here is what the two memory areas and the two
pointers look like on each pass through the loop:

Before pass 1: orig = 1 2 3 \0 rev = 1 2 3 \0
x y

Before pass 2: orig = 1 2 3 \0 rev = 1 2 1 \0
x y

Before pass 3: orig = 1 2 3 \0 rev = 1 2 1 \0
x y

Before pass 3: orig = 1 2 3 \0 rev = 3 2 1 \0
x y

Before pass 4: orig = 1 2 3 \0 rev = 3 2 1 \0
x y

During pass 4, you first decrement 'y' to point to one byte before the
allocated memory, which is in itself undefined behavior. You can
always form a pointer to the object one past the end of valid memory,
whether it is dynamically allocated or not, but there is no guarantee
that you can form a pointer before the beginning of the block.

But even worse, you write, or attempt to write, a '\0' null character
from the initial string into this byte one before the allocated
memory. That's a serious error, and most likely the cause of your
"odd" behavior.
*y++;
return y;
It's too late now, because your program is already off into
never-never land, but you don't need to dereference the pointer just
to increment it, and you don't need to compute the value before the
return statement.

You have no use for this, because when you rewrite the algorithm
properly you will never point to before the beginning of the allocated
block and need to increment the pointer. But if you did need to
increment a pointer before returning its value, you could combine
these two lines into one, either:

return ++y;

....or:

return y + 1;
}


--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Jun 27 '06 #4
On 27 Jun 2006 11:37:28 -0700, "ballpointpenthief"
There is also a complaint about the while (*--tmp = *x++); when showing
all warnings:

Wedit output window build: Tue Jun 27 19:40:31 2006
Warning c:\lcc\projects\str_rev.c: 29 Assignment within conditional
expression


That is normal. It is a common error in C to use an assignment when a
comparison was intended. It doesn't mean it is wrong, it means it
could be wrong and you should double check.

That is, instead of this:

char c;
...
if ( c == 'X')
{ // do this only when c equals 'X'
}

Doing this:

if (c = 'X')
{ // assign 'X' to c and do this always.
}
Jun 27 '06 #5


--
Buy my book 12 Common Atheist Arguments (refuted)
$1.25 download or $7.20 paper, available www.lulu.com/bgy1mm

"ballpointpenthief" <Ma*************@gmail.com> wrote in message
news:11*********************@b68g2000cwa.googlegro ups.com...

Richard Heathfield wrote:
No, you could have done this:


What this?

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

char *rev_str(char *);
int main(void)
{
char *stToRev = "A man, a plan, a canal; Panama.";
char *ptr = rev_str(stToRev);
if (ptr)
{
printf("%s\n", ptr);
free(ptr);
}
return 0;
}

char *rev_str(char *x)
{
char *tmp, *y;
size_t arrLength = strlen(x)+1;

y = malloc(arrLength);
if (y == NULL)
return NULL;
if (!memcpy(y,x,arrLength))
return NULL;
tmp = &y[arrLength-1];
while (*--tmp = *x++);
return y;
}

Is the debugger I'm using broken then? It's doing some ODD stuff.
There is also a complaint about the while (*--tmp = *x++); when showing
all warnings:

That's a lot better.

Your main problem is the line

while(*--tmp = *x++);

you will often see experienced programmers write C code like that. However
it is hard to read, and it is asking for a logical error.
In your case, you are putting the terminating null at the start rather than
the end of the string. You wnat to reverse all the characters of the string,
except the terminating null.
If you rewrite this line to take three or four lines, and follow the logic,
the function will probably work.
Jun 27 '06 #6
Malcolm posted:
Your main problem is the line

while(*--tmp = *x++);

you will often see experienced programmers write C code like that.
However it is hard to read, and it is asking for a logical error.

I'm not with you on this one -- I frequently write code just like it.
Ever read TC++PL by Bjarne? He demonstrates a way of copying a string:

while ( *dest++ = *source++ );

Sure it might baffle a novice... but it's a-okay to me!
Looking at the following line:

while(*--tmp = *x++);
It's obvious that there's four simple steps:

(1) Decrement "tmp".
(2) Store "*x" in "*tmp".
(3) Increment "x".
(4) Repeat until 0 is encountered.
--

Frederick Gotham
Jun 27 '06 #7


Malcolm wrote On 06/27/06 14:52,:
[Something that's a little hard to quote, because the entire
message appeared after the "-- \n" line and thus became part
of the signature. It's all well and good to avoid top-posting,
but this is taking things too far!]


The crucial bits are here reconstituted:
char *tmp, *y;
size_t arrLength = strlen(x)+1;

y = malloc(arrLength);
[...]
tmp = &y[arrLength-1];
while (*--tmp = *x++);


Your main problem is the line

while(*--tmp = *x++);

you will often see experienced programmers write C code like
that. However it is hard to read, and it is asking for a
logical error. In your case, you are putting the terminating
null at the start rather than the end of the string. [...]


In fact, it puts the terminating null not *at* the
beginning of the string, but *before* the beginning of
the string. Or, rather, it tries to; trying to store
something before the start of the allocated area yields
Undefined Behavior, and there's no telling what happens.

The O.P. complains of "VERY odd stuff" when he tries
to run this code under a debugger, and I have a hunch this
off-by-one error is the cause of at least some of the oddity.

--
Er*********@sun.com

Jun 27 '06 #8
"Frederick Gotham" <fg*******@SPAM.com> wrote
Malcolm posted:
Your main problem is the line

while(*--tmp = *x++);

you will often see experienced programmers write C code like that.
However it is hard to read, and it is asking for a logical error.

I'm not with you on this one -- I frequently write code just like it.
Ever read TC++PL by Bjarne? He demonstrates a way of copying a string:

while ( *dest++ = *source++ );

Sure it might baffle a novice... but it's a-okay to me!

You are a clever person.
Others are baffled.

--
Buy my book 12 Common Atheist Arguments (refuted)
$1.25 download or $7.20 paper, available www.lulu.com/bgy1mm

Jun 27 '06 #9
Malcolm posted:
"Frederick Gotham" <fg*******@SPAM.com> wrote
Malcolm posted:
Your main problem is the line

while(*--tmp = *x++);

you will often see experienced programmers write C code like that.
However it is hard to read, and it is asking for a logical error.

I'm not with you on this one -- I frequently write code just like it.
Ever read TC++PL by Bjarne? He demonstrates a way of copying a string:

while ( *dest++ = *source++ );

Sure it might baffle a novice... but it's a-okay to me!

You are a clever person.
Others are baffled.

While intelligence may equate to potential to be a good programmer, it
all comes down to experience. Practise makes perfect.

I don't throw around *p++ because I'm intelligent, but because I'm
familiar with it and know exactly what it does.

Also, on a lighter note, I'd sooner drink bleach than dumb-down my code
in order for a novice to understand it. : )
--

Frederick Gotham
Jun 27 '06 #10
ballpointpenthief wrote:

Richard Heathfield wrote:
No, you could have done this:


What this?

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

char *rev_str(char *);
int main(void)
{
char *stToRev = "A man, a plan, a canal; Panama.";
char *ptr = rev_str(stToRev);
if (ptr)
{
printf("%s\n", ptr);
free(ptr);
}
return 0;
}

char *rev_str(char *x)
{
char *tmp, *y;
size_t arrLength = strlen(x)+1;

y = malloc(arrLength);
if (y == NULL)
return NULL;
if (!memcpy(y,x,arrLength))
return NULL;
tmp = &y[arrLength-1];
while (*--tmp = *x++);
return y;
}


I tried not to change it too much:

/* BEGIN new.c */

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

char *rev_str(char *);

int main(void)
{
char *stToRev = "A man, a plan, a canal; Panama.";
char *ptr = rev_str(stToRev);

if (ptr != NULL) {
puts(ptr);
free(ptr);
} else {
puts("ptr == NULL");
}
return 0;
}

char *rev_str(char *x)
{
char *tmp_y, *y;
size_t arrLength = strlen(x);

y = malloc(arrLength + 1);
if (y != NULL) {
tmp_y = y + arrLength;
*tmp_y = '\0';
while (tmp_y-- > y) {
*tmp_y = *x++;
}
}
return y;
}

/* END new.c */
--
pete
Jun 27 '06 #11
Frederick Gotham wrote:
Malcolm posted:
Your main problem is the line

while(*--tmp = *x++);

you will often see experienced programmers write C code like that.
However it is hard to read, and it is asking for a logical error.

I'm not with you on this one -- I frequently write code just like it.


Then you're doing people a disservice.
Ever read TC++PL by Bjarne? He demonstrates a way of copying a string:

while ( *dest++ = *source++ );

Sure it might baffle a novice... but it's a-okay to me!
Well, to each their own. I would probably use a call to strcpy(); much faster.

Unless I'm implementing strcpy(), of course. Then I'd probably use assembler.

In the unlikely case I have to implement strcpy() in standard C, I'd
probably use

while ((*dest++ = *source++) != '\0');

This is a few more keystrokes, but won't require everyone who compiles it to
figure out that 1) I in fact know what I'm doing and 2) what the
-Wno-warn-on-that-thing-thats-nearly-always-an-error option for their
compiler is.
Looking at the following line:

while(*--tmp = *x++);
It's obvious that there's four simple steps:

(1) Decrement "tmp".
(2) Store "*x" in "*tmp".
(3) Increment "x".
(4) Repeat until 0 is encountered.


while (*x) {
*--tmp = *x++;
}

Two more lousy lines, and you have removed a warning ubiquitous to nearly
all compilers, clarified the loop logic, and corrected the error.

That is, I'm assuming you noticed that the one-liner loop didn't do quite
what the OP expected it would do.

DTRT.

S.
Jun 27 '06 #12
Skarmander wrote:

Frederick Gotham wrote:
Ever read TC++PL by Bjarne?
He demonstrates a way of copying a string:

while ( *dest++ = *source++ );

Sure it might baffle a novice... but it's a-okay to me!


In K&R, that's strcpy "pointer version 3"
Well, to each their own.
I would probably use a call to strcpy(); much faster.

Unless I'm implementing strcpy(), of course.
Then I'd probably use assembler.

In the unlikely case I have to implement strcpy() in standard C, I'd
probably use

while ((*dest++ = *source++) != '\0');


An equality expression with three side effects,
is a little too busy for my taste.

I'm more partial to:

do {
*dest = *source++;
} while (*dest++ != '\0');

--
pete
Jun 27 '06 #13
pete wrote:
Skarmander wrote:
Frederick Gotham wrote:

Ever read TC++PL by Bjarne?
He demonstrates a way of copying a string:

while ( *dest++ = *source++ );

Sure it might baffle a novice... but it's a-okay to me!


In K&R, that's strcpy "pointer version 3"
Well, to each their own.
I would probably use a call to strcpy(); much faster.

Unless I'm implementing strcpy(), of course.
Then I'd probably use assembler.

In the unlikely case I have to implement strcpy() in standard C, I'd
probably use

while ((*dest++ = *source++) != '\0');


An equality expression with three side effects,
is a little too busy for my taste.

I'm more partial to:

do {
*dest = *source++;
} while (*dest++ != '\0');

I thought about this, but in the end, the urge to keep the symmetry of the
++ operators was just too strong to ignore.

If I really wanted to split up all the side effects, of which I'm no great
fan either, I'd go all the way:

for (;;)
*dest = *source*;
if (*source == '\0') break;
++dest; ++source;
}

Loop with test in the middle, discussed in another thread. It's a small group.

S.
Jun 27 '06 #14
Skarmander wrote:
pete wrote:
Skarmander wrote:
Frederick Gotham wrote:

Ever read TC++PL by Bjarne?
He demonstrates a way of copying a string:

while ( *dest++ = *source++ );

Sure it might baffle a novice... but it's a-okay to me!


In K&R, that's strcpy "pointer version 3"
Well, to each their own.
I would probably use a call to strcpy(); much faster.

Unless I'm implementing strcpy(), of course.
Then I'd probably use assembler.

In the unlikely case I have to implement strcpy() in standard C, I'd
probably use

while ((*dest++ = *source++) != '\0');


An equality expression with three side effects,
is a little too busy for my taste.

I'm more partial to:
do {
*dest = *source++;
} while (*dest++ != '\0');

I thought about this, but in the end, the urge to keep the symmetry of
the ++ operators was just too strong to ignore.

If I really wanted to split up all the side effects, of which I'm no
great fan either, I'd go all the way:

for (;;)
*dest = *source*;


Typo, obviously. Some newsreaders will helpfully *highlight* it. :-)

S.
Jun 27 '06 #15
Skarmander wrote:

Skarmander wrote:
pete wrote:
Skarmander wrote: In the unlikely case I have to implement strcpy()
in standard C, I'd probably use

while ((*dest++ = *source++) != '\0');

An equality expression with three side effects,
is a little too busy for my taste.

I'm more partial to:
do {
*dest = *source++;
} while (*dest++ != '\0');

I thought about this, but in the end,
the urge to keep the symmetry of
the ++ operators was just too strong to ignore.

If I really wanted to split up all the side effects, of which I'm no
great fan either, I'd go all the way:

for (;;)
*dest = *source*;


Typo, obviously. Some newsreaders will helpfully *highlight* it. :-)


I avoid break statements when it's not too hard to do.

I also prefer

for (rc = getc(fp); rc != EOF; rc = getc(fp))

to

while ((rc = getc(fp)) != EOF)

I guess it's not necessarily the "three side effects"
in an equality expression that I don't like.

--
pete
Jun 27 '06 #16


pete wrote On 06/27/06 16:35,:

I also prefer

for (rc = getc(fp); rc != EOF; rc = getc(fp))

to

while ((rc = getc(fp)) != EOF)


There's no point arguing with Gus, but one reason
to prefer the latter form is that you type the expression
only once instead of twice. That's not too bad when the
expression is a simple as this one, but even so it hands
a human reader the job of verifying that the expressions
are identical -- or, if they're not, whether they're
intentionally or accidentally different:

for (rc = getc(fp); rc != EOF; rc = fgetc(fp))

for (rc = getc(conn.fp); rc != EOF; rc = getc(fp))

for (rc = getc(fp); rc != EOF; r = getc(fp))

--
Er*********@sun.com

Jun 27 '06 #17
pete wrote:
Skarmander wrote:
Skarmander wrote:
pete wrote:
Skarmander wrote: In the unlikely case I have to implement strcpy()
> in standard C, I'd probably use
>
> while ((*dest++ = *source++) != '\0');
An equality expression with three side effects,
is a little too busy for my taste.

I'm more partial to:
do {
*dest = *source++;
} while (*dest++ != '\0');

I thought about this, but in the end,
the urge to keep the symmetry of
the ++ operators was just too strong to ignore.

If I really wanted to split up all the side effects, of which I'm no
great fan either, I'd go all the way:

for (;;)
*dest = *source*; Typo, obviously. Some newsreaders will helpfully *highlight* it. :-)


I avoid break statements when it's not too hard to do.

There's just no pleasing you.
I also prefer

for (rc = getc(fp); rc != EOF; rc = getc(fp))
Oh no! A duplicated statement! *faints*

Just kidding. But I don't imagine many programmers write this.
to

while ((rc = getc(fp)) != EOF)

I guess it's not necessarily the "three side effects"
in an equality expression that I don't like.

It doesn't win any beauty contests, but it's C. It wasn't meant to win
beauty contests. C's maxim is "brevity is... wit", and in that respect it's
a very witty language.

S.
Jun 27 '06 #18
Eric Sosman wrote:

pete wrote On 06/27/06 16:35,:

I also prefer

for (rc = getc(fp); rc != EOF; rc = getc(fp))

to

while ((rc = getc(fp)) != EOF)


There's no point arguing with Gus, but one reason
to prefer the latter form is that you type the expression
only once instead of twice. That's not too bad when the
expression is a simple as this one, but even so it hands
a human reader the job of verifying that the expressions
are identical -- or, if they're not, whether they're
intentionally or accidentally different:

for (rc = getc(fp); rc != EOF; rc = fgetc(fp))

for (rc = getc(conn.fp); rc != EOF; rc = getc(fp))

for (rc = getc(fp); rc != EOF; r = getc(fp))


Good point!

--
pete
Jun 27 '06 #19

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

24
by: Sathyaish | last post by:
This one question is asked modally in most Microsoft interviews. I started to contemplate various implementations for it. This was what I got. #include <stdio.h> #include <stdlib.h> #include...
16
by: VISHNU VARDHAN REDDY UNDYALA | last post by:
Hi, Could anyone over here, write a program in C using only for loop to print the following output * *** ***** ******* ********* ***********
46
by: Albert | last post by:
Why doesn't: #include <stdio.h> void reverse(char, int); main() { char s;
22
by: Mike Polinske | last post by:
I am new to the C programming language but have been programming in Cobol for over 10 years. When I compile the following code, it compiles clean but I get an application error both under Windows...
21
by: siya | last post by:
I'm new to c and i've a problem with arrays. I want to sum two arrays into a third array like this: array a: 5 6 8 9 1 2 4 7 3 8 array b: 5 2 1 2 4 6 8 5 4 3 sum: 1 0 9 0 1 5 9 3 2 8 1 I...
47
by: sudharsan | last post by:
could any one please give me a code to reverse a string of more than 1MB .??? Thanks in advance
20
by: paolo.arimado | last post by:
Dear everyone, Can someone please help me on my HW. I'm trying to write a program that will display the following: * *** ***** ******* *********
11
by: anto_kamau | last post by:
Question write a c program that displays a diamond pattern, when a user enters an odd number between 0 and 19, and displays on the screen
20
by: Wabz | last post by:
Hello mates, Does anyone know how to write a function that tests if an integer is a palindrome in C language?
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.