[comp.sys.ibm.pc] bug in Turbo C 1.5

tim@cit-vax.Caltech.Edu (Timothy L. Kay) (01/24/88)

To the best of my recollection, I don't recall having seen any bug
reports so far for Turbo C 1.5.  Am I the first person to report a
bug in Turbo C 1.5?

This program

	----------------------------------
	#include <string.h>

	void main() {
	  char *s = "this is a test";
	  printf("%s\n", s);
	  memcpy(s + 1, s, strlen(s) - 1);
	  printf("%s\n", s);
	}
	----------------------------------

displays

	--------------
	this is a test
	tthis is a tes
	--------------

under Turbo C 1.0, and

	--------------
	this is a test
	tthhssii    ee
	--------------

under Turbo C 1.5.

The Turbo C 1.0 memcpy() detects if the source and destination
overlap and switches the direction of the copying.  Borland must have
decided that the memcpy() routine should not be so smart.  The Turbo
C 1.5 memcpy() generates the above (rather peculiar) pattern by doing
word-at-a-time copying, always in the positive direction.

To see how the pattern was generated, we should look at the copy one
word at a time.  The ^^ marks the word to be moved.  The destination
of the move is marked by the second ^.

	--------------
	this is a test
	^^
	tths is a test
	  ^^
	tthhsis a test
	    ^^
	tthhssi a test
	      ^^
	tthhssii  test
	        ^^
	tthhssii   est
	          ^^
	tthhssii    et
	            ^^
	tthhssii    ee
	--------------

I have worked around this bug by extracting the old version out of
the old library.

	tlib /C \tc10\lib\ch.lib *memcpy.obj

Then I added "memcpy.obj" to my project, and it works.

Tim

crh@hpcvmb.HP (Ron Henderson) (01/26/88)

>reports so far for Turbo C 1.5.  Am I the first person to report a
>bug in Turbo C 1.5?
 ^^^
A bug?, I don't think so.

>
>This program
>
>	----------------------------------
>	#include <string.h>
>
>	void main() {
>	  char *s = "this is a test";
>	  printf("%s\n", s);
>	  memcpy(s + 1, s, strlen(s) - 1);
>	  printf("%s\n", s);
>	}
>	----------------------------------
>

According to the README file, line 189, the memcpy function now conforms
to the ANSI standard. If you want the 'smart' move, you should use
memmove, not memcpy.

Ron ...!hplabs!hp-pcd!crh

skl@sklpc.vnet.van-bc.UUCP (Samuel Lam) (01/26/88)

> To the best of my recollection, I don't recall having seen any 
> bug reports so far for Turbo C 1.5.  Am I the first person to 
> report a bug in Turbo C 1.5?  

No, since what you have observed is not a bug, but a *very* 
intended feature put in by Borland in version 1.5.  For more 
details, edit the README file that comes in disk #1 of your 
Turbo-C 1.5 distribution and scan for the string "memcpy", the 
information is in the errata section of the file.

> The Turbo C 1.0 memcpy() detects if the source and destination 
> overlap and switches the direction of the copying.  Borland 
> must have decided that the memcpy() routine should not be so 
> smart.  

Borland had changed memcpy() in order to make its behaviour 
comply with the ANSI C draft standard.  

> I have worked around this bug by extracting the old version out 
> of the old library.  

The above README file pointed out that the library function 
memmove() will handle overlapping region properly.

...Sam
-- 
Samuel Lam   {ihnp4!alberta,watmath,uw-beaver}!ubc-vision!van-bc!sklpc!skl

robf2@pyuxf.UUCP (robert fair) (01/27/88)

In article <5298@cit-vax.Caltech.Edu>, tim@cit-vax.UUCP writes:
# To the best of my recollection, I don't recall having seen any bug
# reports so far for Turbo C 1.5.  Am I the first person to report a
# bug in Turbo C 1.5?
# 
# This program
# 
# 	----------------------------------
# 	#include <string.h>
# 
# 	void main() {
# 	  char *s = "this is a test";
# 	  printf("%s\n", s);
# 	  memcpy(s + 1, s, strlen(s) - 1);
# 	  printf("%s\n", s);
# 	}
# 	----------------------------------
# 
# displays
# 
# 	--------------
# 	this is a test
# 	tthis is a tes
# 	--------------
# 
# under Turbo C 1.0, and
# 
# 	--------------
# 	this is a test
# 	tthhssii    ee
# 	--------------
# 
# under Turbo C 1.5.
This is precisely correct behaviour - the action of memcpy() on overlapping
regions is undefined in the draft ANSI standard. It looks like Borland took
advantage of this to do memcpy() faster, but it only works on non-overlapping 
areas.

Simply don't use memcpy() on overlapping regions. There should be a function
which does what you want if Turbo is a decent compiler.

Incidently MSC memcpy() changed in *exactly* the same way from 4.0 to 5.0 -
but at leat Microsoft documented the change and provided a function which 
worked in the old way for compatibility [ memmove() ]

Personally I think the entire thing sucks - the function should be
able to detect overlapping areas and take special action in such cases :(
-
-- 
Robert L. Fair
Bell Communications Research/CHC
Piscataway, NJ
{ihnp4,allegra}!pyuxww!pyuxf!robf2

tim@cit-vax.Caltech.Edu (Timothy L. Kay) (01/27/88)

>... ("error" in mem_copy)
>
>Please read your Readme file...

I'd like to thank all the people who explained to me that the change
in memcpy was noted in the readme file that came with the disks.  Somehow
I had missed this eventhough I had read the readme file.

I'd like to make two points.  First, why was it necessary for Borland
to *change* memcpy to conform to the standard?  It seems to me that the
old version of memcpy already conformed to the standard.  In changing
memcpy, they only caused people like me inconvenience.  (Please don't
suggest that some people want to use memcpy to clear memory.)

The second point is this.  After I posted my (not-a-)bug report, I received
several notes via mail that pointed out my error.  Then, six days later,
I am still seeing postings that tell me the same thing.  I think
followups are an inappropriate way to answer such questions.  In the
future, if there are questions such as mine, please mail the answer.  The
original poster should then summarize.

Tim

scjones@sdrc.UUCP (Larry Jones) (01/27/88)

In article <2490@emory.uucp>, platt@emory.uucp (Dan Platt) writes:
> 
> The version 1.5 fixes a "bug" in the 1.0 version by NOT checking for overwrites
> in the memcopy function.  The memmove function preserves the old (safe) 
> technique.  [by following a standard like ANSI a lot of inferiorities are
> introduced]

Sorry, but I get tired of reading statements like these.  For the last time,
ANSI did not REQUIRE anyone to change the way memcpy works.  ANSI does not say
that memcpy can't check for overlapping moves and do them correctly, it merely
affirms the fact that many implementations don't, quite a lot of people expect
memcpy to be blindingly fast, and thus said that not checking was OK.  For
those who need a function to handle overlapping moves correctly, ANSI added
memmove.  So quit blaming ANSI and start blaming { Microsoft, Borland, ... }
'cause THEY'RE the ones that decided to change their implementations.

chip@ateng.UUCP (Chip Salzenberg) (01/28/88)

In article <1596@imagen.UUCP> mark@imagen.UUCP (Mark Peek) writes:
}
}If you'd like a real bug, try compiling a program in huge model and do
}pointer arithmetic. Unless you specify "huge" in the pointer declaration,
}you get a "far" pointer. This can cause a lot of problems because the offset
}arithmetic does not roll over into the segment. Maybe in 1.6 ???

This is a documented feature.  :-)

"Huge model" and "huge pointers" have nothing to do with each other.
It's too bad that the same word is used to describe them.

Actually, it's good that far pointers are the default in huge model, since
huge pointers are _slow_.  And I do mean _slow_.
-- 
Chip Salzenberg                 UUCP: "{codas,uunet}!ateng!chip"
A T Engineering                 My employer's opinions are a trade secret.
       "Anything that works is better than anything that doesn't."

hsu@santra.UUCP (Heikki Suonsivu) (01/28/88)

In article <5298@cit-vax.Caltech.Edu> tim@cit-vax.UUCP (Timothy L. Kay) writes:
>The Turbo C 1.0 memcpy() detects if the source and destination
>overlap and switches the direction of the copying.  Borland must have
>decided that the memcpy() routine should not be so smart.  The Turbo

'man memcpy' and other C manuals I have around, state "Because
character movement is performed differently in different
implementations, overlapping moves (memcpies) may yield unexpected
results". Using such a feature, even if it exists in some certain
compiler/library, will certainly make code unportable.

Haven't read Turbo C manuals so I have no idea whether there is
something about this, but I hope there is, otherwise there will be
lots of nice code which can be used in all machines which run turbo-C,
but rest of the world will have lots of fun porting it, specially when
bugs generated by that kind of thing are sometimes quite hard to find.

Inet: hsu@santra ................. Kuutamokatu 5 A 7 
Uucp: ...!mcvax!santra!hsu ....... 02210 Espoo .....
Fido: Heikki Suonsivu at 2:504/1 . FINLAND .........

carlj@hpcvmb.HP (Carl Johnson) (01/29/88)

>The Turbo C 1.0 memcpy() detects if the source and destination
>overlap and switches the direction of the copying.  Borland must have
>decided that the memcpy() routine should not be so smart.  The Turbo

I think Borland is right not to bother making the memcpy() so smart.
If you think about what a _COPY_ operation implies, an overlapping copy
makes no sense whatsoever.  In general it is impossible to have
overlapping strings be identical.  A move operation on the other hand
must be able to handle overlaps, so it is important to have an
intelligent move function such as memmove().

-----
Carl Johnson
...!hp-labs!hp-pcd!carlj

rbradbur@oracle.UUCP (Robert Bradbury) (02/14/88)

In article <5331@cit-vax.Caltech.Edu> tim@cit-vax.UUCP (Timothy L. Kay) writes:
>
>I'd like to make two points.  First, why was it necessary for Borland
>to *change* memcpy to conform to the standard?  It seems to me that the
>old version of memcpy already conformed to the standard.  In changing
>memcpy, they only caused people like me inconvenience.  (Please don't
>suggest that some people want to use memcpy to clear memory.)

Ah, a question I can answer finally :-).  As one of the prime motivators
of the addition of memmove() to the standard I can explain the reasons
for this.  The problem revolves around trying to serve 2 masters in the
C runtime library: efficiency and portability.  The original (to the best
of my knowledge) definition for memcpy() (Unix System V memory(3C)) is:

  "memcpy copies *n* characters from memory area *s2* to *s1*".

It is undefined what happens if the memory areas overlap.  On the VAX
memcpy() was implemented using movc3/c5 instructions which have clever
microcode which handles overlapping copies correctly.  On a variety of
other machines implementors had less smart instructions and chose to
implement memcpy() efficiently using instructions which did not handle
overlapping areas.  This gets particularly important as compiler vendors
are now providing in-line versions of memcpy() which *usually* do not
handle overlapping copies.  (Supporting overlapping copies involves
adding extra code on most machines.)  We (at Oracle) build large DBMS
which run on a variety of machines and desire that functions which move
bytes of data around should be both efficient AND portable.

In order to have efficient in-line functions we recommended the C standard
support memcpy() as not having to handle overlapping copies.  To handle
copies of overlapping areas portably (and efficiently on machines with
clever microcode) the function memmove() was added.

Borland simply changed memcpy to be the more efficient implementation
allowed by the current draft of the C standard.

I'll admit we could have gone the other way on the names (adding memecpy()
for efficient copies) but it was felt that the majority of current memcpy()
uses did not involve overlapping copies so "standardizing" the existing practice
of machines where memcpy() did not handle overlapping copies would not break
alot of code on those machines where memcpy() did handle overlapping copies.

Most people have no idea of the effort which goes into standardizing something
like C.  Suffice to say that when something is done there are usually good
reasons behind it and that in many cases the compromises required are going
to upset someone.

On another note; does everyone realize that the current standard allows
the results of the str/memcmp() function to be implementation defined
if the characters being compared have the high-bit set?  The net result
is to prevent portable comparisons of unsigned chars or European/EBCDIC
character sets.  This has been pointed out to the committee but they
didn't feel it was "significant".  So if you are writing portable code
and plan to compare anything other than standard ASCII text you should
define your own comparison functions and not rely on the runtime library.


-- 
Robert Bradbury
Oracle Corporation
(206) 784-9726                            hplabs!oracle!rbradbur