Go Back   Rhinocerus > Newsgroup > Newsgroup comp.lang.c

Reply
 
Thread Tools Display Modes
  #1 (permalink)  
Old 06-08-2012, 05:16 AM
sinbad
Guest
 
Posts: n/a
Default copy size

is it ok to allow memcpy () to take size 0;
or is it a good practice to check for the size
and do memcpy only for sizes greater than 0;
Reply With Quote
Alt Today
Advertising
 
and become member of Rhinocerus
Standard Sponsored Links

  #2 (permalink)  
Old 06-08-2012, 05:40 AM
Keith Thompson
Guest
 
Posts: n/a
Default Re: copy size

sinbad <sinbad.sinbad@gmail.com> writes:
> is it ok to allow memcpy () to take size 0;
> or is it a good practice to check for the size
> and do memcpy only for sizes greater than 0;


See section 7.24.1 of the C standard
(http://www.open-std.org/jtc1/sc22/wg...docs/n1570.pdf is the latest
draft):

Where an argument declared as size_t n specifies the length of the
array for a function, n can have the value zero on a call to that
function.

So yes, it's ok to call memcpy() with a size of 0.

Note that if either s1 or s2 is a null pointer, the behavior is (I
believe) undefined.

--
Keith Thompson (The_Other_Keith) kst-u@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"
Reply With Quote
  #3 (permalink)  
Old 06-08-2012, 01:15 PM
David T. Ashley
Guest
 
Posts: n/a
Default Re: copy size

On Thu, 07 Jun 2012 22:40:52 -0700, Keith Thompson <kst-u@mib.org>
wrote:

>
>So yes, it's ok to call memcpy() with a size of 0.
>
>Note that if either s1 or s2 is a null pointer, the behavior is (I
>believe) undefined.


I quick search of the Internet has revealed that you are right.

But it seems odd for three reasons:

a)In almost all the functions I write that have parameters of type
(void *) and size_t (usually occurring together, for the obvious
reasons), I handle the size=0 case. When I handle it, the most
natural construction of the function is that the pointer(s) are not
dereferenced if size=0.

b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
to handle the size=0 case down into the function, by definition. It
could just as easily be handled by the caller. In some programs, the
case of size=0 and an invalid pointer go together. It seems useful to
make it standard that if size=0, neither pointer will be dereferenced.

c)I don't see a performance penalty in any memcpy() implementation
I've ever seen.

However:

d)I'm not a processor design whiz. I do mostly work on 8-bit through
32-bit microcontrollers. These are not really a representative set. I
can't rule out the possibility that on certain families of machines
(branch prediction, state rollback, caching, who knows what else) that
there could be a performance penalty of not requiring that the
pointers be valid even if size=0.

DTA
Reply With Quote
  #4 (permalink)  
Old 06-08-2012, 01:32 PM
Eric Sosman
Guest
 
Posts: n/a
Default Re: copy size

On 6/8/2012 9:15 AM, David T. Ashley wrote:
> On Thu, 07 Jun 2012 22:40:52 -0700, Keith Thompson<kst-u@mib.org>
> wrote:
>
>>
>> So yes, it's ok to call memcpy() with a size of 0.
>>
>> Note that if either s1 or s2 is a null pointer, the behavior is (I
>> believe) undefined.

>
> I quick search of the Internet has revealed that you are right.
>
> But it seems odd for three reasons:
>
> a)In almost all the functions I write that have parameters of type
> (void *) and size_t (usually occurring together, for the obvious
> reasons), I handle the size=0 case. When I handle it, the most
> natural construction of the function is that the pointer(s) are not
> dereferenced if size=0.


Right. But the Standard does not require the implementation
to use the "most natural" construction. Indeed, it goes out of
its way to permit "unnatural" constructions in pursuit of speed
and other efficiencies.

> b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
> to handle the size=0 case down into the function, by definition. It
> could just as easily be handled by the caller. In some programs, the
> case of size=0 and an invalid pointer go together. It seems useful to
> make it standard that if size=0, neither pointer will be dereferenced.


This seems self-contradictory. If the caller can handle a
special case "just as easily," why is it "useful" to require the
internals of memcpy() to handle it in a particular way?

>[...]
> d)I'm not a processor design whiz. I do mostly work on 8-bit through
> 32-bit microcontrollers. These are not really a representative set. I
> can't rule out the possibility that on certain families of machines
> (branch prediction, state rollback, caching, who knows what else) that
> there could be a performance penalty of not requiring that the
> pointers be valid even if size=0.


I lack any inside knowledge of the various Committees' thought
processes, but it seems unlikely that the "all arguments must be
valid" requirement arose out of concerns for specific machines.
Rather, it seems more likely that the overarching principle "do
not constrain implementations unnecessarily" was at work. In this
case, the implementation is not required to determine something
about one argument before making use of the others.

--
Eric Sosman
esosman@ieee-dot-org.invalid
Reply With Quote
  #5 (permalink)  
Old 06-08-2012, 01:40 PM
David T. Ashley
Guest
 
Posts: n/a
Default Re: copy size

On Fri, 08 Jun 2012 09:32:34 -0400, Eric Sosman
<esosman@ieee-dot-org.invalid> wrote:

>> b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
>> to handle the size=0 case down into the function, by definition. It
>> could just as easily be handled by the caller. In some programs, the
>> case of size=0 and an invalid pointer go together. It seems useful to
>> make it standard that if size=0, neither pointer will be dereferenced.

>
> This seems self-contradictory. If the caller can handle a
>special case "just as easily," why is it "useful" to require the
>internals of memcpy() to handle it in a particular way?


Please don't play Devil's Advocate because you are bored : ).

Creating a memcpy() function that doesn't handle the size=0 case is
just as silly as creating a square root function that calculates
square root for every non-negative integer except 0.

It forces the users to either a)wrap the function or b)handle the
exception case many times whereas handling it in the function would
require handling it only once.

DTA
Reply With Quote
  #6 (permalink)  
Old 06-08-2012, 02:05 PM
pete
Guest
 
Posts: n/a
Default Re: copy size

Keith Thompson wrote:
>
> sinbad <sinbad.sinbad@gmail.com> writes:
> > is it ok to allow memcpy () to take size 0;
> > or is it a good practice to check for the size
> > and do memcpy only for sizes greater than 0;

>
> See section 7.24.1 of the C standard
> (http://www.open-std.org/jtc1/sc22/wg...docs/n1570.pdf
> is the latest draft):
>
> Where an argument declared as size_t n specifies the length of the
> array for a function, n can have the value zero on a call to that
> function.
>
> So yes, it's ok to call memcpy() with a size of 0.
>
> Note that if either s1 or s2 is a null pointer, the behavior is (I
> believe) undefined.


You believe correctly.
Here is the next sentence from n1570:

Unless explicitly stated otherwise
in the description of a particular function in this subclause,
pointer arguments on such a call
shall still have valid values, as described in 7.1.4.

And here is some of 7.1.4:

7.1.4 Use of library functions
1 Each of the following statements applies
unless explicitly stated otherwise
in the detailed descriptions that follow:
If an argument to a function has an invalid value
(such as a value outside the domain of the function,
or a pointer outside the address space of the program,

or a null pointer,

or a pointer to non-modifiable storage
when the corresponding parameter is not const-qualified)
or a type (after promotion)
not expected by a function with variable number of arguments,
the behavior is undefined.

--
pete
Reply With Quote
  #7 (permalink)  
Old 06-08-2012, 02:30 PM
pete
Guest
 
Posts: n/a
Default Re: copy size

David T. Ashley wrote:
>
> On Thu, 07 Jun 2012 22:40:52 -0700, Keith Thompson <kst-u@mib.org>
> wrote:
>
> >
> >So yes, it's ok to call memcpy() with a size of 0.
> >
> >Note that if either s1 or s2 is a null pointer, the behavior is (I
> >believe) undefined.

>
> I quick search of the Internet has revealed that you are right.
>
> But it seems odd for three reasons:
>
> a)In almost all the functions I write that have parameters of type
> (void *) and size_t (usually occurring together, for the obvious
> reasons), I handle the size=0 case. When I handle it, the most
> natural construction of the function is that the pointer(s) are not
> dereferenced if size=0.


memcpy is of the <string.h> functions.
and there are some of them,
where it may make sense to derefence a pointer parameter
prior to evaluating the n parameter.

char *strncat(char *s1, const char *s2, size_t n)
{
char *const p1 = s1;

s1 += str_len(s1);
while (n-- != 0 && *s2 != '\0') {
*s1++ = *s2++;
}
*s1 = '\0';
return p1;
}

>
> b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
> to handle the size=0 case down into the function, by definition.


memcpy may or may not, (depending on how it is implemented),
require any special code to handle the n equals 0 case.

void *memcpy(void *s1, const void *s2, size_t n)
{
unsigned char *p1 = s1;
const unsigned char *p2 = s2;

while (n-- != 0) {
*p1++ = *p2++;
}
return s1;
}

--
pete
Reply With Quote
  #8 (permalink)  
Old 06-08-2012, 02:31 PM
Eric Sosman
Guest
 
Posts: n/a
Default Re: copy size

On 6/8/2012 9:40 AM, David T. Ashley wrote:
> On Fri, 08 Jun 2012 09:32:34 -0400, Eric Sosman
> <esosman@ieee-dot-org.invalid> wrote:
>
>>> b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
>>> to handle the size=0 case down into the function, by definition. It
>>> could just as easily be handled by the caller. In some programs, the
>>> case of size=0 and an invalid pointer go together. It seems useful to
>>> make it standard that if size=0, neither pointer will be dereferenced.

>>
>> This seems self-contradictory. If the caller can handle a
>> special case "just as easily," why is it "useful" to require the
>> internals of memcpy() to handle it in a particular way?

>
> Please don't play Devil's Advocate because you are bored : ).
>
> Creating a memcpy() function that doesn't handle the size=0 case is
> just as silly as creating a square root function that calculates
> square root for every non-negative integer except 0.


The Standard does in fact require memcpy() to handle the size=0
case, so what's your point?

> It forces the users to either a)wrap the function or b)handle the
> exception case many times whereas handling it in the function would
> require handling it only once.


Concerning (b), you yourself wrote "It could just as easily be
handled by the caller," end quote. If the burden on the caller has
zero weight, where's the utility in removing it?

As to (a), I have never felt the slightest need or inclination
to wrap memcpy(), and have certainly never felt forced to do so.

--
Eric Sosman
esosman@ieee-dot-org.invalid
Reply With Quote
  #9 (permalink)  
Old 06-08-2012, 06:47 PM
Tim Rentsch
Guest
 
Posts: n/a
Default Re: copy size

David T. Ashley <dashley@gmail.com> writes:

> On Thu, 07 Jun 2012 22:40:52 -0700, Keith Thompson <kst-u@mib.org>
> wrote:
>
>>
>>So yes, it's ok to call memcpy() with a size of 0.
>>
>>Note that if either s1 or s2 is a null pointer, the behavior is (I
>>believe) undefined.

>
> I quick search of the Internet has revealed that you are right.
>
> But it seems odd for three reasons:
>
> a)In almost all the functions I write that have parameters of type
> (void *) and size_t (usually occurring together, for the obvious
> reasons), I handle the size=0 case. When I handle it, the most
> natural construction of the function is that the pointer(s) are not
> dereferenced if size=0.
>
> b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
> to handle the size=0 case down into the function, by definition. It
> could just as easily be handled by the caller. In some programs, the
> case of size=0 and an invalid pointer go together. It seems useful to
> make it standard that if size=0, neither pointer will be dereferenced.
> [snip other]


Two points:

Dereferencing is not the only way a bad pointer value might cause
undesired behavior. Any use of a pointer in an "address-like"
manner might cause a malfunction if the pointer value is bad.

Implementations of library functions are allowed to make use of
non-portable, implementation-specific assumptions to do what they
do. Actions based on such assumptions might work for bona fide
pointers but fail for, eg, NULL. For example, we might do a
computation on a pointer value to prepare a pre-fetch of the
memory where the source bytes would be. Such a computation could
misbehave if operating on a bad pointer value, even though no
"dereferencing" in the sense of C semantics has taken place.
Reply With Quote
  #10 (permalink)  
Old 06-08-2012, 06:53 PM
Keith Thompson
Guest
 
Posts: n/a
Default Re: copy size

David T. Ashley <dashley@gmail.com> writes:
> On Fri, 08 Jun 2012 09:32:34 -0400, Eric Sosman
> <esosman@ieee-dot-org.invalid> wrote:
>
>>> b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
>>> to handle the size=0 case down into the function, by definition. It
>>> could just as easily be handled by the caller. In some programs, the
>>> case of size=0 and an invalid pointer go together. It seems useful to
>>> make it standard that if size=0, neither pointer will be dereferenced.

>>
>> This seems self-contradictory. If the caller can handle a
>>special case "just as easily," why is it "useful" to require the
>>internals of memcpy() to handle it in a particular way?

>
> Please don't play Devil's Advocate because you are bored : ).
>
> Creating a memcpy() function that doesn't handle the size=0 case is
> just as silly as creating a square root function that calculates
> square root for every non-negative integer except 0.


sqrt() computes square roots; sqrt(0.0) computes the square root of
0.0. It's not a special case.

memcpy() copies memory; memcpy() with size=0 doesn't copy any memory.

And as Eric points out, the standard already requires support for
memcpy() with size=0 if the s1 and s2 arguments are valid. If they're
not valid, the behavior is undefined, but I don't see that that's a
significant problem.

> It forces the users to either a)wrap the function or b)handle the
> exception case many times whereas handling it in the function would
> require handling it only once.


The only reason to wrap the function or handle exceptional cases is if
you have a call to memcpy() where it's possible to have size=0 *and* one
or more null pointer or otherwise invalid arguments. I don't see that
happening much in real life.

--
Keith Thompson (The_Other_Keith) kst-u@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"
Reply With Quote
  #11 (permalink)  
Old 06-08-2012, 07:11 PM
Tim Rentsch
Guest
 
Posts: n/a
Default Re: copy size

David T. Ashley <dashley@gmail.com> writes:

> On Fri, 08 Jun 2012 09:32:34 -0400, Eric Sosman
> <esosman@ieee-dot-org.invalid> wrote:
>
>>> b)Allowing memcpy(dptr, sptr, 0) means that you decided to push logic
>>> to handle the size=0 case down into the function, by definition. It
>>> could just as easily be handled by the caller. In some programs, the
>>> case of size=0 and an invalid pointer go together. It seems useful to
>>> make it standard that if size=0, neither pointer will be dereferenced.

>>
>> This seems self-contradictory. If the caller can handle a
>>special case "just as easily," why is it "useful" to require the
>>internals of memcpy() to handle it in a particular way?

>
> Please don't play Devil's Advocate because you are bored : ).
>
> Creating a memcpy() function that doesn't handle the size=0 case is
> just as silly as creating a square root function that calculates
> square root for every non-negative integer except 0. [snip]


I think you're confusing implementation and specification.

I expect most implementations will work just fine for any pointer
values given to memcpy() when the number of bytes to be moved is
zero.

However, the specification is written considering all possible
implementations, including some that have hardware characteristics
we don't even know about yet. In light of that, it is natural to
give as much freedom as we can to implementors of library
functions like memcpy(), provided the burden on developers using
those functions isn't too high. Here the burden seems pretty low
-- in practice I expect most calls to memcpy() either are known to
have valid pointer arguments, or are inside an if() if the pointer
arguments might not be valid (and so the calls would not be made
if the pointer arguments were null, for example), ie, the tests
would be made anyway, independent of whether memcpy() always works
for zero length calls. Since the burden seems to be low, we give
implementations more latitude, because even though _most_ won't
need it, _some_ may find it a significant benefit. It's because
we don't know the characteristics of all implementations,
including future implementations, that it makes sense to keep
the specifications as unrestrictive as we reasonably can.
Reply With Quote
  #12 (permalink)  
Old 06-08-2012, 07:32 PM
christian.bau
Guest
 
Posts: n/a
Default Re: copy size

An implementation may assume that the programmer's code does not
invoke undefined behaviour. Therefore, after a call

memcpy (p, q, n);

the implementation may assume that p != NULL and q != NULL. So in this
code fragment (assuming that p and q are non-const pointers to numeric
objects)

memcpy (p, q, n);
if (p != NULL) *p = 1;
if (q != NULL) *q = 2;

the compiler is free to remove the test whether p != NULL and whether
q != NULL.
Reply With Quote
  #13 (permalink)  
Old 06-09-2012, 03:42 AM
Tim Rentsch
Guest
 
Posts: n/a
Default Re: copy size

"christian.bau" <christian.bau@cbau.wanadoo.co.uk> writes:

> An implementation may assume that the programmer's code does not
> invoke undefined behaviour. Therefore, after a call
>
> memcpy (p, q, n);
>
> the implementation may assume that p != NULL and q != NULL. So in this
> code fragment (assuming that p and q are non-const pointers to numeric
> objects)
>
> memcpy (p, q, n);
> if (p != NULL) *p = 1;
> if (q != NULL) *q = 2;
>
> the compiler is free to remove the test whether p != NULL and whether
> q != NULL.


I believe this is technically true, but there is a subtlety. The
behavior of memcpy() operating on a null pointer is undefined
only as far as the Standard is concerned. If an implementation
chooses to document (and therefore define) the behavior of
memcpy() upon receiving a null pointer value argument, then the
behavior is defined and the implementation cannot assume that the
'!= NULL' tests are superfluous. An implementation may not act
in contradiction to its own documentation, even for situations
that the Standard labels "undefined behavior".
Reply With Quote
 
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off




All times are GMT. The time now is 09:16 AM.


Copyright ©2009

LinkBacks Enabled by vBSEO 3.3.0 RC2 © 2009, Crawlability, Inc.