Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #151069 > unrolled thread
| Started by | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| First post | 2020-04-05 06:47 -0700 |
| Last post | 2020-04-08 01:32 +0200 |
| Articles | 20 on this page of 29 — 13 participants |
Back to article view | Back to comp.lang.c
memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-05 06:47 -0700
Re: memcpy can't take null. Öö Tiib <ootiib@hot.ee> - 2020-04-05 07:30 -0700
Re: memcpy can't take null. Richard Damon <Richard@Damon-Family.org> - 2020-04-05 14:39 -0400
Re: memcpy can't take null. Philipp Klaus Krause <pkk@spth.de> - 2020-04-06 18:46 +0200
Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-06 12:45 -0700
Re: memcpy can't take null. Philipp Klaus Krause <pkk@spth.de> - 2020-04-07 07:28 +0200
Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-05 16:00 -0700
Re: memcpy can't take null. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2020-04-05 18:29 -0600
Re: memcpy can't take null. scott@slp53.sl.home (Scott Lurndal) - 2020-04-06 18:08 +0000
Re: memcpy can't take null. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2020-04-06 15:44 -0600
Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-06 13:54 -0700
Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-06 14:30 -0700
Re: memcpy can't take null. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2020-04-06 01:20 +0100
Re: memcpy can't take null. Siri Cruise <chine.bleu@yahoo.com> - 2020-04-05 19:09 -0700
Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-07 16:50 +0200
Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-07 09:19 -0700
Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-07 09:21 -0700
Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-08 01:41 +0200
Re: memcpy can't take null. scott@slp53.sl.home (Scott Lurndal) - 2020-04-08 00:30 +0000
Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-08 02:06 -0700
Re: memcpy can't take null. Richard Damon <Richard@Damon-Family.org> - 2020-04-08 09:17 -0400
Re: memcpy can't take null. Tim Rentsch <tr.17687@z991.linuxsc.com> - 2020-04-09 07:16 -0700
Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-08 13:33 -0700
Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-09 09:05 -0700
Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-09 13:28 -0700
Re: memcpy can't take null. richard@cogsci.ed.ac.uk (Richard Tobin) - 2020-04-09 23:41 +0000
Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-08 16:17 +0200
Re: memcpy can't take null. Ike Naar <ike@sdf.lonestar.org> - 2020-04-07 22:01 +0000
Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-08 01:32 +0200
Page 1 of 2 [1] 2 Next page →
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2020-04-05 06:47 -0700 |
| Subject | memcpy can't take null. |
| Message-ID | <6c3de07e-15c8-4cc0-a805-636a5976d086@googlegroups.com> |
memcpy() cannot take null as a pointer parametrer, even if the size argument is zero. So what are the implications for passing empty buffers about? For example, how would people write /* struct employee has no embedded pointers */ Employee *duplicateEmployees(Employee *employees, int Nemployees)
[toc] | [next] | [standalone]
| From | Öö Tiib <ootiib@hot.ee> |
|---|---|
| Date | 2020-04-05 07:30 -0700 |
| Message-ID | <dbb91496-3624-477c-92b7-f1d8ff434193@googlegroups.com> |
| In reply to | #151069 |
On Sunday, 5 April 2020 16:47:31 UTC+3, Malcolm McLean wrote:
> memcpy() cannot take null as a pointer parametrer, even if the size argument
> is zero.
>
> So what are the implications for passing empty buffers about? For example,
> how would people write
>
> /* struct employee has no embedded pointers */
>
> Employee *duplicateEmployees(Employee *employees, int Nemployees)
{
if (!employees || Nemployees < 1) return NULL;
Employee *ret = malloc(Nemployees * sizeof(Employee));
if (ret) memcpy(ret, employees, Nemployees * sizeof(Employee));
return ret;
}
[toc] | [prev] | [next] | [standalone]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2020-04-05 14:39 -0400 |
| Message-ID | <1CpiG.149251$Pj7.50779@fx14.iad> |
| In reply to | #151072 |
On 4/5/20 10:30 AM, Öö Tiib wrote:
> On Sunday, 5 April 2020 16:47:31 UTC+3, Malcolm McLean wrote:
>> memcpy() cannot take null as a pointer parametrer, even if the size argument
>> is zero.
>>
>> So what are the implications for passing empty buffers about? For example,
>> how would people write
>>
>> /* struct employee has no embedded pointers */
>>
>> Employee *duplicateEmployees(Employee *employees, int Nemployees)
>
> {
> if (!employees || Nemployees < 1) return NULL;
>
> Employee *ret = malloc(Nemployees * sizeof(Employee));
> if (ret) memcpy(ret, employees, Nemployees * sizeof(Employee));
>
> return ret;
> }
>
Since malloc can return either a null pointer or a unique pointer for an
allocation of size 0, if I wanted to be able to use a null pointer for
an error return in functions like this, I would be tempted to create a
wrapper for malloc() if the size requested was 0, would allocate a
buffer of 1 byte, so null returns are always errors. This wrapper could
perhaps use knowledge of the implementation, if it KNOWS that malloc(0)
will return unique pointers and not nulls (except for out of memory),
then it could still call malloc(0).
This lack of standardization of behavior is a historical artifact that
K&R didn't define what to do here, and when the first Standard was being
written, both options were is common usage, so rather than break
existing implementations, it was left undefined. It is of course fairly
trivial to make a wrapper to provide the alternate behavior if you
really need it as described above.
[toc] | [prev] | [next] | [standalone]
| From | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| Date | 2020-04-06 18:46 +0200 |
| Message-ID | <r6fmcn$f4r$1@solani.org> |
| In reply to | #151092 |
Am 05.04.20 um 20:39 schrieb Richard Damon: > This lack of standardization of behavior is a historical artifact that > K&R didn't define what to do here, and when the first Standard was being > written, both options were is common usage, so rather than break > existing implementations, it was left undefined. It is of course fairly > trivial to make a wrapper to provide the alternate behavior if you > really need it as described above. The next C standard will even make realloc for zero size undefined behaviour. Too many implementations didn't conform with the old C11 standard for zero size realloc. C17 thus allowed more implementation-defined behaviour, but at that point it wasn't very useable for portable programs anymore. So it will just be undefined behaviour in C2X.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2020-04-06 12:45 -0700 |
| Message-ID | <87eet0o0tb.fsf@nosuchdomain.example.com> |
| In reply to | #151145 |
Philipp Klaus Krause <pkk@spth.de> writes:
[...]
> The next C standard will even make realloc for zero size undefined
> behaviour.
> Too many implementations didn't conform with the old C11 standard for
> zero size realloc. C17 thus allowed more implementation-defined
> behaviour, but at that point it wasn't very useable for portable
> programs anymore. So it will just be undefined behaviour in C2X.
Do you have a citation for that?
The latest draft I have (n2455.pdf, November 18, 2019) does have this
new wording relative to n1570.pdf, but is identical to the wording in
C17. There is no new undefined behavior as far as I can tell.
If size is nonzero and memory for the new object is not
allocated, the old object is not deallocated. If size is
zero and memory for the new object is not allocated, it is
implementation-defined whether the old object is deallocated.
If the old object is not deallocated, its value shall be
unchanged.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Philipp Klaus Krause <pkk@spth.de> |
|---|---|
| Date | 2020-04-07 07:28 +0200 |
| Message-ID | <r6h32q$h81$1@solani.org> |
| In reply to | #151154 |
Am 06.04.20 um 21:45 schrieb Keith Thompson: > Philipp Klaus Krause <pkk@spth.de> writes: > [...] >> The next C standard will even make realloc for zero size undefined >> behaviour. >> Too many implementations didn't conform with the old C11 standard for >> zero size realloc. C17 thus allowed more implementation-defined >> behaviour, but at that point it wasn't very useable for portable >> programs anymore. So it will just be undefined behaviour in C2X. > > Do you have a citation for that? > Not really. There is N2464, but you'll have to wait a a bit for the draft meeting minutes for the meeting last week to see the votes on it (or until August for the decision on the final meeting minutes). There is item 6.35 in the 2019 Ithaka meeting minutes regarding an earlier version of the realloc paper. > The latest draft I have (n2455.pdf, November 18, 2019) N2478 / N2479, February 2020 is the latest draft (but naturally doesn't have changes voted on last week). Philipp
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2020-04-05 16:00 -0700 |
| Message-ID | <87a73ppmgj.fsf@nosuchdomain.example.com> |
| In reply to | #151069 |
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
> memcpy() cannot take null as a pointer parametrer, even if the size argument
> is zero.
>
> So what are the implications for passing empty buffers about? For example,
> how would people write
>
> /* struct employee has no embedded pointers */
>
> Employee *duplicateEmployees(Employee *employees, int Nemployees)
C doesn't directly support empty arrays, but since arrays are usually
manipulated via pointers to the initial element, it's easy enough
to treat a pointer as pointing to an empty array.
A null pointer doesn't point to an empty array. It doesn't point
to *anything*. Similarly, a null pointer doesn't point to an empty
string (there have been problems in the past with code that assumed
it did, and that happened to "work" on some systems).
Just keep that consistently in mind, and write your code in
whatever way is necessary to maintain that. This might require
a little special case code for handling the result of malloc(0).
So write that code.
Employee *duplicateEmployees(Employee *employees, size_t Nemployees) {
const size_t requestedCount = Nemployees + (Nemployees == 0);
Employee *const new = malloc(requestedCount * sizeof *new);
if (new != NULL) {
memcpy(new, employees, Nemployees * sizeof *new);
}
return new;
}
An alternative would be to treat a null pointer as a pointer to an
empty array, but then you'd probably need more special case code on
top of that to keep things consistent (to avoid passing null pointers
to memcpy, for example). By *not* treating a null pointer as a
pointer to an empty array, you can (probably) isolate the special
case code to allocation functions. You'd also lose the ability to
use a null pointer as a special value that doesn't point to anything.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Joe Pfeiffer <pfeiffer@cs.nmsu.edu> |
|---|---|
| Date | 2020-04-05 18:29 -0600 |
| Message-ID | <1bzhbpmp7j.fsf@pfeifferfamily.net> |
| In reply to | #151104 |
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes: > > A null pointer doesn't point to an empty array. It doesn't point > to *anything*. Similarly, a null pointer doesn't point to an empty > string (there have been problems in the past with code that assumed > it did, and that happened to "work" on some systems). In particular, VAXen had page 0 user readable, and byte 0 contained a 0 by pure luck. I thought for years a null pointer could be used to represent an empty string -- I learned my mistake when I moved my code over to a Sun workstation.
[toc] | [prev] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2020-04-06 18:08 +0000 |
| Message-ID | <ceKiG.895917$5o5.652843@fx41.iad> |
| In reply to | #151108 |
Joe Pfeiffer <pfeiffer@cs.nmsu.edu> writes: >Keith Thompson <Keith.S.Thompson+u@gmail.com> writes: >> >> A null pointer doesn't point to an empty array. It doesn't point >> to *anything*. Similarly, a null pointer doesn't point to an empty >> string (there have been problems in the past with code that assumed >> it did, and that happened to "work" on some systems). > >In particular, VAXen had page 0 user readable, and byte 0 contained a 0 >by pure luck. I thought for years a null pointer could be used to >represent an empty string -- I learned my mistake when I moved my code >over to a Sun workstation. BSD unix supported a readonly page of zeros at virtual address zero (on the VAX), which caused difficulties in porting BSD utilities to SVR4 when they were merged in. I don't remember that VMS did the same, but it's been forty years since.
[toc] | [prev] | [next] | [standalone]
| From | Joe Pfeiffer <pfeiffer@cs.nmsu.edu> |
|---|---|
| Date | 2020-04-06 15:44 -0600 |
| Message-ID | <1bpnckmgr7.fsf@pfeifferfamily.net> |
| In reply to | #151149 |
scott@slp53.sl.home (Scott Lurndal) writes: > Joe Pfeiffer <pfeiffer@cs.nmsu.edu> writes: >>Keith Thompson <Keith.S.Thompson+u@gmail.com> writes: >>> >>> A null pointer doesn't point to an empty array. It doesn't point >>> to *anything*. Similarly, a null pointer doesn't point to an empty >>> string (there have been problems in the past with code that assumed >>> it did, and that happened to "work" on some systems). >> >>In particular, VAXen had page 0 user readable, and byte 0 contained a 0 >>by pure luck. I thought for years a null pointer could be used to >>represent an empty string -- I learned my mistake when I moved my code >>over to a Sun workstation. > > BSD unix supported a readonly page of zeros at virtual address zero (on the VAX), > which caused difficulties in porting BSD utilities to SVR4 when they > were merged in. > > I don't remember that VMS did the same, but it's been forty years since. Yes, I was referring to BSD. I didn't remember (if I ever knew) that the whole first page was 0; the first byte was sufficient to create the misunderstanding.
[toc] | [prev] | [next] | [standalone]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2020-04-06 13:54 -0700 |
| Message-ID | <07c3ec1e-d2d5-41a3-bbc7-6e37b6464c01@googlegroups.com> |
| In reply to | #151104 |
On Monday, 6 April 2020 00:00:41 UTC+1, Keith Thompson wrote:
> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
> > memcpy() cannot take null as a pointer parametrer, even if the size argument
> > is zero.
> >
> > So what are the implications for passing empty buffers about? For example,
> > how would people write
> >
> > /* struct employee has no embedded pointers */
> >
> > Employee *duplicateEmployees(Employee *employees, int Nemployees)
>
> C doesn't directly support empty arrays, but since arrays are usually
> manipulated via pointers to the initial element, it's easy enough
> to treat a pointer as pointing to an empty array.
>
> A null pointer doesn't point to an empty array. It doesn't point
> to *anything*. Similarly, a null pointer doesn't point to an empty
> string (there have been problems in the past with code that assumed
> it did, and that happened to "work" on some systems).
>
> Just keep that consistently in mind, and write your code in
> whatever way is necessary to maintain that. This might require
> a little special case code for handling the result of malloc(0).
> So write that code.
>
> Employee *duplicateEmployees(Employee *employees, size_t Nemployees) {
> const size_t requestedCount = Nemployees + (Nemployees == 0);
> Employee *const new = malloc(requestedCount * sizeof *new);
> if (new != NULL) {
> memcpy(new, employees, Nemployees * sizeof *new);
> }
> return new;
> }
>
> An alternative would be to treat a null pointer as a pointer to an
> empty array, but then you'd probably need more special case code on
> top of that to keep things consistent (to avoid passing null pointers
> to memcpy, for example). By *not* treating a null pointer as a
> pointer to an empty array, you can (probably) isolate the special
> case code to allocation functions. You'd also lose the ability to
> use a null pointer as a special value that doesn't point to anything.
>
>
I think this is the best answer.
The standard library doesn't accept NULL as a valid pointer to the
empty array, so it's reasonable to say that user-defined functions
don't accept it either. That means that if you are passed a non-
null pointer (presumably valid) with a size of zero, you should pass
back a valid pointer. Otherwise caller suddenly finds NULLs infesting
what he thought were valid empty arrays.
if you are passed NULL and non-zero for the size, it is clearly
an error, and it's best to pass it onto memcpy in the hope that
memcpy will crash out. Since we don't know what we are running on,
the implementation has a better idea how to crash the program
than we do. You shouldn't suppress the error and pass back NULL -
that's making it harder for caller to find where the bug lies.
But if you are passed NULL and zero for the size, should you still
pass it on to memcpy() ?
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2020-04-06 14:30 -0700 |
| Message-ID | <87y2r8mhei.fsf@nosuchdomain.example.com> |
| In reply to | #151158 |
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
> On Monday, 6 April 2020 00:00:41 UTC+1, Keith Thompson wrote:
>> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
>> > memcpy() cannot take null as a pointer parametrer, even if the size argument
>> > is zero.
>> >
>> > So what are the implications for passing empty buffers about? For example,
>> > how would people write
>> >
>> > /* struct employee has no embedded pointers */
>> >
>> > Employee *duplicateEmployees(Employee *employees, int Nemployees)
>>
>> C doesn't directly support empty arrays, but since arrays are usually
>> manipulated via pointers to the initial element, it's easy enough
>> to treat a pointer as pointing to an empty array.
>>
>> A null pointer doesn't point to an empty array. It doesn't point
>> to *anything*. Similarly, a null pointer doesn't point to an empty
>> string (there have been problems in the past with code that assumed
>> it did, and that happened to "work" on some systems).
>>
>> Just keep that consistently in mind, and write your code in
>> whatever way is necessary to maintain that. This might require
>> a little special case code for handling the result of malloc(0).
>> So write that code.
>>
>> Employee *duplicateEmployees(Employee *employees, size_t Nemployees) {
>> const size_t requestedCount = Nemployees + (Nemployees == 0);
>> Employee *const new = malloc(requestedCount * sizeof *new);
>> if (new != NULL) {
>> memcpy(new, employees, Nemployees * sizeof *new);
>> }
>> return new;
>> }
>>
>> An alternative would be to treat a null pointer as a pointer to an
>> empty array, but then you'd probably need more special case code on
>> top of that to keep things consistent (to avoid passing null pointers
>> to memcpy, for example). By *not* treating a null pointer as a
>> pointer to an empty array, you can (probably) isolate the special
>> case code to allocation functions. You'd also lose the ability to
>> use a null pointer as a special value that doesn't point to anything.
>>
>>
> I think this is the best answer.
>
> The standard library doesn't accept NULL as a valid pointer to the
> empty array, so it's reasonable to say that user-defined functions
> don't accept it either. That means that if you are passed a non-
> null pointer (presumably valid) with a size of zero, you should pass
> back a valid pointer. Otherwise caller suddenly finds NULLs infesting
> what he thought were valid empty arrays.
>
> if you are passed NULL and non-zero for the size, it is clearly
> an error, and it's best to pass it onto memcpy in the hope that
> memcpy will crash out. Since we don't know what we are running on,
> the implementation has a better idea how to crash the program
> than we do. You shouldn't suppress the error and pass back NULL -
> that's making it harder for caller to find where the bug lies.
>
> But if you are passed NULL and zero for the size, should you still
> pass it on to memcpy() ?
You should decide *and document* what argument values are valid, and
what values are invalid. If a function receives invalid values, either
it should behave in some documented manner, or the documentation should
say that it's the caller's responsibility to pass only valid arguments
(otherwise the behavior is undefined).
For some functions, a null pointer might be valid, indicating that there
is no array (distinct from an empty array). For others, a null pointer
might be invalid. Decide and document.
You might consider wrapping the address and count in a structure.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2020-04-06 01:20 +0100 |
| Message-ID | <871rp1cvmd.fsf@bsb.me.uk> |
| In reply to | #151069 |
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
> memcpy() cannot take null as a pointer parametrer, even if the size argument
> is zero.
>
> So what are the implications for passing empty buffers about? For example,
> how would people write
>
> /* struct employee has no embedded pointers */
>
> Employee *duplicateEmployees(Employee *employees, int Nemployees)
Employee *duplicateEmployees(Employee *employees, int Nemployees)
{
Employee *copy = 0;
if (Nemployees > 0 && (copy = malloc(Nemployees * sizeof *copy)))
memcpy(copy, employees, Nemployees * sizeof *copy);
return copy;
}
though I'd rather it were
Employee *duplicateEmployees(const Employee *employees, size_t Nemployees);
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | Siri Cruise <chine.bleu@yahoo.com> |
|---|---|
| Date | 2020-04-05 19:09 -0700 |
| Message-ID | <chine.bleu-82C6F2.19090105042020@reader.eternal-september.org> |
| In reply to | #151069 |
In article
<6c3de07e-15c8-4cc0-a805-636a5976d086@googlegroups.com>,
Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
> memcpy() cannot take null as a pointer parametrer, even if the size argument
> is zero.
rmemcpy does.
#define rmemcpy(dd, ss, len) ({ \
void *_dd = (dd), *_ss = (ss); size_t _len = (len); \
if (_dd==0 || _len<=0) ; \
else if (_ss==0) memset(_dd, 0, _len); \
else memcpy(_dd, _ss, _len); \
_dd; \
})
Ma deese! Do you mean I am allowed to wrap other code to even out
interfaces to what I want? Epouvantable!
#define tmemcpy(Type, dd, ss, len) \
((Type*)rmemcpy(dd, ss, (len)*sizeof(Type)))
How is this allowed?
#define rstrcmp(a, b) ({char *_a=(a), *_b=(b); \
_a==_b ? 0 : !_a ? -1 : !_b ? 1 : strcmp(_a, _b) ; \
})
#define rstreq(a, b) (rstrcmp(a, b)==0)
#define rstrne(a, b) (rstrcmp(a, b)!=0)
#define rstrlt(a, b) (rstrcmp(a, b)<0)
#define rstrle(a, b) (rstrcmp(a, b)<=0)
#define rstrgt(a, b) (rstrcmp(a, b)>0)
#define rstrge(a, b) (rstrcmp(a, b)>=0)
--
:-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. @
'I desire mercy, not sacrifice.' /|\
The first law of discordiamism: The more energy This post / \
to make order is nore energy made into entropy. insults Islam. Mohammed
[toc] | [prev] | [next] | [standalone]
| From | Guillaume <message@bottle.org> |
|---|---|
| Date | 2020-04-07 16:50 +0200 |
| Message-ID | <r6i3vk$14n3$1@gioia.aioe.org> |
| In reply to | #151069 |
Le 05/04/2020 à 15:47, Malcolm McLean a écrit :
> memcpy() cannot take null as a pointer parametrer, even if the size argument
> is zero.
Most of the string/memory functions of the C std lib don't do any kind
of error checking. You may consider that passing memcpy a null pointer
AND a size of 0 is not an error per se, but I wouldn't necessarily agree
(along with probably many others.)
The reason for no error checking is likely historical. Possibly for
performance reasons. Not quite sure, but the idea is pervasive in most
of the std lib.
BTW, what would you expect memcpy to do in such a case? I guess just do
nothing and return NULL?
In which case you'd just write:
void * mymemcpy(void *destination, const void *source, size_t size)
{
if (destination == NULL)
return NULL;
memcpy(destination, source, size);
}
Note that I can't see how you could usefully distinguish the case where
destination is NULL and size isn't zero, from the case where destination
is NULL and size is zero. In both cases, there's nothing to do, and the
function could return nothing else than NULL IMO, so the above
definition should be OK.
> So what are the implications for passing empty buffers about?
You implement error checking yourself, which many find tedious in C.
But maybe you should tell us a bit more about what you call an "empty
buffer".
Typically, to be consistent IMO a NULL pointer would mean an unallocated
buffer, whereas an "empty" buffer may be more like an allocated buffer
with no data. Really depends on your requirements/implementation. In the
later case, you'd need to define your buffers as structs with a bit more
info than just a pointer to the buffer memory.
[toc] | [prev] | [next] | [standalone]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2020-04-07 09:19 -0700 |
| Message-ID | <3ff768db-1f76-4bc5-b1ad-b7c80d8e27e0@googlegroups.com> |
| In reply to | #151186 |
On Tuesday, 7 April 2020 15:50:43 UTC+1, Guillaume wrote: > Le 05/04/2020 à 15:47, Malcolm McLean a écrit : > > memcpy() cannot take null as a pointer parametrer, even if the size argument > > is zero. > > Most of the string/memory functions of the C std lib don't do any kind > of error checking. You may consider that passing memcpy a null pointer > AND a size of 0 is not an error per se, but I wouldn't necessarily agree > (along with probably many others.) > It's not inherently a logical error to regard NULL as a pointer to no bytes, and it's not enforced by the C language as such. However the convention that the standard library uses is that NULL with a size of zero is an error, rather than a valid call for the null case. > > > So what are the implications for passing empty buffers about? > You implement error checking yourself, which many find tedious in C. > > But maybe you should tell us a bit more about what you call an "empty > buffer". > "Empty buffer" is maybe the wrong term. I mean a buffer with no data in it, and of zero capacity. C doesn't allow zero-sized arrays, but it does allow malloc() to return a non-null, freeable pointer for a request of zero bytes.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2020-04-07 09:21 -0700 |
| Message-ID | <87zhbnl10s.fsf@nosuchdomain.example.com> |
| In reply to | #151186 |
Guillaume <message@bottle.org> writes:
> Le 05/04/2020 à 15:47, Malcolm McLean a écrit :
>> memcpy() cannot take null as a pointer parametrer, even if the size argument
>> is zero.
>
> Most of the string/memory functions of the C std lib don't do any kind
> of error checking. You may consider that passing memcpy a null pointer
> AND a size of 0 is not an error per se, but I wouldn't necessarily
> agree (along with probably many others.)
It's not as ambiguous as you make it sound. Passing a null pointer to
memcpy has undefined behavior.
The memcpy function copies n characters from the object pointed to
by s2 into the object pointed to by s1.
If s1 or s2 doesn't point to an object, then that argument is invalid as
described in 7.1.4.
[...]
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Guillaume <message@bottle.org> |
|---|---|
| Date | 2020-04-08 01:41 +0200 |
| Message-ID | <r6j33u$15ev$1@gioia.aioe.org> |
| In reply to | #151193 |
Le 07/04/2020 à 18:21, Keith Thompson a écrit : > Guillaume <message@bottle.org> writes: >> Le 05/04/2020 à 15:47, Malcolm McLean a écrit : >>> memcpy() cannot take null as a pointer parametrer, even if the size argument >>> is zero. >> >> Most of the string/memory functions of the C std lib don't do any kind >> of error checking. You may consider that passing memcpy a null pointer >> AND a size of 0 is not an error per se, but I wouldn't necessarily >> agree (along with probably many others.) > > It's not as ambiguous as you make it sound. Passing a null pointer to > memcpy has undefined behavior. None of what I said makes it ambiguous. I just said most of those functions don't do any error checking for invalid parameters. Nothing ambiguous about it. > > The memcpy function copies n characters from the object pointed to > by s2 into the object pointed to by s1. > > If s1 or s2 doesn't point to an object, then that argument is invalid as > described in 7.1.4. Exactly. To me an invalid argument is an error. Then the underlying function is implemented with error checking (so the behavior is defined) or with no error checking (so the behavior is obviously undefined.) Error checking here meant "parameter validity checking". Of course, note that the case exposed by Malcolm is just a particular case that *could* have been handled by memcpy(). But the standard is more general, as it generally talks about "pointers not pointing to an object". Which of course means NULL, but also any other value that would happen to point to a random location that would not have been allocated properly, or that would have been free'd. In that regard, the usefulness of checking for just NULL when any other value could also be not pointing to an object is admittedly questionable, and is probably the main reason null pointers are not checked explicitely in those functions.
[toc] | [prev] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2020-04-08 00:30 +0000 |
| Message-ID | <dW8jG.1373649$hD4.309580@fx47.iad> |
| In reply to | #151216 |
Guillaume <message@bottle.org> writes: >Le 07/04/2020 à 18:21, Keith Thompson a écrit : >> Guillaume <message@bottle.org> writes: >>> Le 05/04/2020 à 15:47, Malcolm McLean a écrit : >>>> memcpy() cannot take null as a pointer parametrer, even if the size argument >>>> is zero. >>> >>> Most of the string/memory functions of the C std lib don't do any kind >>> of error checking. You may consider that passing memcpy a null pointer >>> AND a size of 0 is not an error per se, but I wouldn't necessarily >>> agree (along with probably many others.) >> >> It's not as ambiguous as you make it sound. Passing a null pointer to >> memcpy has undefined behavior. > >None of what I said makes it ambiguous. I just said most of those >functions don't do any error checking for invalid parameters. Nothing >ambiguous about it. > >> >> The memcpy function copies n characters from the object pointed to >> by s2 into the object pointed to by s1. >> >> If s1 or s2 doesn't point to an object, then that argument is invalid as >> described in 7.1.4. > >Exactly. To me an invalid argument is an error. Then the underlying >function is implemented with error checking (so the behavior is defined) >or with no error checking (so the behavior is obviously undefined.) > >Error checking here meant "parameter validity checking". How would you validate any of the parameters to memcpy? Particularly considering that from a hardware point of view, zero is a legal virtual address. Back a quarter century ago, one of the standards committees I sat on discussed this very topic (with options like an OS system call to validate that the virtual addresses were valid for the process - huge overhead). C is, after all, used in many contexts, often as bare metal code. The final consensus was that there was no effective way to check the parameters to mem* or str* functions without a serious performance drop.
[toc] | [prev] | [next] | [standalone]
| From | Malcolm McLean <malcolm.arthur.mclean@gmail.com> |
|---|---|
| Date | 2020-04-08 02:06 -0700 |
| Message-ID | <8338dd09-dfed-447b-adf0-356695b1b1d8@googlegroups.com> |
| In reply to | #151221 |
On Wednesday, 8 April 2020 01:30:08 UTC+1, Scott Lurndal wrote: > Guillaume <message@bottle.org> writes: > >Le 07/04/2020 à 18:21, Keith Thompson a écrit : > >> Guillaume <message@bottle.org> writes: > >>> Le 05/04/2020 à 15:47, Malcolm McLean a écrit : > >>>> memcpy() cannot take null as a pointer parametrer, even if the size argument > >>>> is zero. > >>> > >>> Most of the string/memory functions of the C std lib don't do any kind > >>> of error checking. You may consider that passing memcpy a null pointer > >>> AND a size of 0 is not an error per se, but I wouldn't necessarily > >>> agree (along with probably many others.) > >> > >> It's not as ambiguous as you make it sound. Passing a null pointer to > >> memcpy has undefined behavior. > > > >None of what I said makes it ambiguous. I just said most of those > >functions don't do any error checking for invalid parameters. Nothing > >ambiguous about it. > > > >> > >> The memcpy function copies n characters from the object pointed to > >> by s2 into the object pointed to by s1. > >> > >> If s1 or s2 doesn't point to an object, then that argument is invalid as > >> described in 7.1.4. > > > >Exactly. To me an invalid argument is an error. Then the underlying > >function is implemented with error checking (so the behavior is defined) > >or with no error checking (so the behavior is obviously undefined.) > > > >Error checking here meant "parameter validity checking". > > How would you validate any of the parameters to memcpy? Particularly > considering that from a hardware point of view, zero is a legal virtual > address. > > Back a quarter century ago, one of the standards committees I sat on > discussed this very topic (with options like an OS system call to validate > that the virtual addresses were valid for the process - huge overhead). > > C is, after all, used in many contexts, often as bare metal code. > > The final consensus was that there was no effective way to check > the parameters to mem* or str* functions without a serious performance > drop. > If Nemployees is non-zero whilst the pointer employees points to invalid memory, it's likely that the bug will be discovered soon enough. But if Nemployees is zero and employees is NULL, then the loops through the employees array will all be no-ops and have defined, correct behaviour. It's only if you call a library function like memcpy that the code becomes incorrect.
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.c
csiph-web