Hi
Reading comp.std.c I noticed that there was a message like this:
The WG14 Post Portland mailing is now available from the WG14 web site
at http://www.open-std.org/jtc1/sc22/wg14/
Best regards
Keld Simonsen
I went there and found that there is a report called
ISO/IEC JTC1 SC22 WG14 WG14/N1193
Specification for Safer C Library Functions �
Part II: Dynamic Allocation Functions
It proposes really interesting functions, among others getline and
getdelim, that I introduced into lcc-win32 (by coincidence) a few weeks
ago.
It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
but strndup was missing. Here is a proposed implementation. I would like
to see if your sharp eyes see any bug or serious problem with it.
Thanks in advance
jacob
---------------------------------------------------------cut here
#include <string.h>
#include <stdlib.h>
/*
The strndup function copies not more than n characters (characters that
follow a null character are not copied) from string to a dynamically
allocated buffer. The copied string shall always be null terminated.
*/
char *strndup(const char *string,size_t s)
{
char *p,*r;
if (string == NULL)
return NULL;
p = string;
while (s > 0) {
if (*p == 0)
break;
p++;
s--;
}
s = (p - string);
r = malloc(1+s);
if (r) {
strncpy(r,string,s);
r[s] = 0;
}
return r;
}
#ifdef TEST
#include <stdio.h>
#define MAXTEST 60
int main(void)
{
char *table[MAXTEST];
char *str = "The quick brown fox jumps over the lazy dog";
for (int i=0; i<MAXTEST;i++) {
table[i] = strndup(str,i);
}
for (int i=0; i<MAXTEST;i++) {
printf("[%4d] %s\n",i,table[i]);
}
return 0;
}
#endif
|
|
0
|
|
|
|
Reply
|
jacob
|
12/2/2006 10:53:26 PM |
|
jacob navia said:
<snip>
> I would like
> to see if your sharp eyes see any bug or serious problem with it.
>
I don't see any serious problem with the code in terms of meeting its
specification, although I would consider replacing
> strncpy(r,string,s);
with:
memcpy(r, string, s);
since you've already detected the terminator and know precisely where it is.
The thing that does concern me is the spec itself, which seems to me to
suffer from the same flaw as strncpy - i.e. it gives no indication of
whether truncation occurred. But of course that's a design issue, not a C
issue.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/2/2006 11:54:04 PM
|
|
Richard Heathfield a �crit :
> jacob navia said:
>
> <snip>
>
>>I would like
>>to see if your sharp eyes see any bug or serious problem with it.
>>
>
>
> I don't see any serious problem with the code in terms of meeting its
> specification, although I would consider replacing
>
>
>> strncpy(r,string,s);
>
>
> with:
>
> memcpy(r, string, s);
>
> since you've already detected the terminator and know precisely where it is.
>
> The thing that does concern me is the spec itself, which seems to me to
> suffer from the same flaw as strncpy - i.e. it gives no indication of
> whether truncation occurred. But of course that's a design issue, not a C
> issue.
>
Interesting. I did not think about that.
You know of a version that gives that information back to the user?
It is sometimes important to know.
< off topic>
Since lcc-win32 supports optional arguments I could do:
char *strndup(char *str,size_t siz,bool *pTruncated=NULL);
strndup(str,30) would be strndup(str,30,NULL)...
< / off topic >
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 12:04:45 AM
|
|
In article <cKSdnVXyuLJNjO_YnZ2dnUVZ8q-dnZ2d@bt.com>,
Richard Heathfield <rjh@see.sig.invalid> wrote:
>The thing that does concern me is the spec itself, which seems to me to
>suffer from the same flaw as strncpy - i.e. it gives no indication of
>whether truncation occurred. But of course that's a design issue, not a C
>issue.
When I've used my own version of strndup, it's always been make an
ordinary string from a "counted" string, so there is no question of
truncation. I suspect this is the more common use of it, rather than
copying a string to a buffer that might not be big enough.
-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
|
|
0
|
|
|
|
Reply
|
richard
|
12/3/2006 1:42:05 AM
|
|
jacob navia wrote:
> It proposes really interesting functions, among others getline and
> getdelim, that I introduced into lcc-win32 (by coincidence) a few weeks
> ago.
>
> It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
> but strndup was missing. Here is a proposed implementation. I would like
> to see if your sharp eyes see any bug or serious problem with it.
What would the point of strndup be? It allocates the memory so there
really isn't a problem of an overflow. If you're low on memory ... why
are you duplicating a string?
Me thinks this is a solution looking for a problem [not directed at you
specifically Jacob, just the whole question of whether we should care
about strndup at all].
Tom
|
|
0
|
|
|
|
Reply
|
Tom
|
12/3/2006 3:49:42 AM
|
|
Richard Tobin said:
> In article <cKSdnVXyuLJNjO_YnZ2dnUVZ8q-dnZ2d@bt.com>,
> Richard Heathfield <rjh@see.sig.invalid> wrote:
>
>>The thing that does concern me is the spec itself, which seems to me to
>>suffer from the same flaw as strncpy - i.e. it gives no indication of
>>whether truncation occurred. But of course that's a design issue, not a C
>>issue.
>
> When I've used my own version of strndup, it's always been make an
> ordinary string from a "counted" string, so there is no question of
> truncation. I suspect this is the more common use of it, rather than
> copying a string to a buffer that might not be big enough.
No, that's not more common - it's just a more *intelligent* use of strncpy.
By far the most common usage of strncpy is from the cargo cult bunch: "I'm
smart, I know about buffer overruns, I know I should use strncpy instead of
strcpy, oops, oh look, I just threw away data, ohdearhowsadnevermind."
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/3/2006 7:16:05 AM
|
|
jacob navia said:
> You know of a version that gives that information back to the user?
No, but that doesn't mean much because I know of no version of strndup
whatsoever, apart from the one you just posted. I don't see any particular
use for it, so I've never gone looking for it. (That is not the same as
saying it's useless - one man's useless is another man's essential.)
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/3/2006 7:18:49 AM
|
|
Tom St Denis wrote:
> jacob navia wrote:
>
>> It proposes really interesting functions, among others getline and
>> getdelim, that I introduced into lcc-win32 (by coincidence) a few
>> weeks ago.
>>
>> It proposes strdup and strndup too. Lcc-win32 proposes already
>> strdup, but strndup was missing. Here is a proposed implementation.
>> I would like to see if your sharp eyes see any bug or serious
>> problem with it.
>
> What would the point of strndup be? It allocates the memory so
> there really isn't a problem of an overflow. If you're low on
> memory ... why are you duplicating a string?
>
> Me thinks this is a solution looking for a problem [not directed at
> you specifically Jacob, just the whole question of whether we should
> care about strndup at all].
I can see a possible use for it - to duplicate the initial portion
of a string only, i.e. to truncate it on the right.
#include <stdlib.h>
#include <stddef.h>
/* The strndup function copies not more than n characters
(characters that follow a null character are not copied)
from string to a dynamically allocated buffer. The copied
string shall always be null terminated.
*/
char *strndup(const char *_string, size_t _len) {
char *s, *p;
if ((p = s = malloc(_len + 1))) {
if (_string) /* else interpret NULL as empty string */
while (_len-- && (*p++ = *_string++)) continue;
*p = '\0';
}
return s;
} /* strndup, untested */
However N1193 is, in general, a Microsoft proposal, not a
standard. It is their lame attempt to catch their own foolish
clueless programmers. It is ugly, ugly, ugly.
BTW, Jacobs code carelessly fails to define size_t. It also return
NULL for other reasons than lack of memory, which can only cause
confusion.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/3/2006 7:35:32 AM
|
|
jacob navia wrote:
> It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
> but strndup was missing. Here is a proposed implementation. I would like
> to see if your sharp eyes see any bug or serious problem with it.
....
> ---------------------------------------------------------cut here
> #include <string.h>
> #include <stdlib.h>
> /*
> The strndup function copies not more than n characters (characters that
> follow a null character are not copied) from string to a dynamically
> allocated buffer. The copied string shall always be null terminated.
> */
> char *strndup(const char *string,size_t s)
I haven't looked at the code, but I am a big believer in accurate
documentation.
Your comment references "n characters", but defines a parameter s. Is
that n? The comment doesn't say how many characters are copied, only
that it is not more than n, so I can't rely on the function to copy the
full strlen(string), even if s exceeds this. I suggest calling the
result a dynamically allocated char array, rather than a buffer, since
it isn't necessarily buffering anything. As a matter of fact, since it
is presumably sized to the input string, there probably isn't room for
additional characters to be added later, thus not used as a buffer.
The description doesn't say what the results are if insufficient memory
exists for the new array.
--
Thad
|
|
0
|
|
|
|
Reply
|
Thad
|
12/3/2006 9:40:38 AM
|
|
CBFalconer a �crit :
> BTW, Jacobs code carelessly fails to define size_t.
???
I have
#include <stdlib.h>
and that file includes stddef.h that defines size_t
> It also return
> NULL for other reasons than lack of memory, which can only cause
> confusion.
>
??? Why confusion?
Null means failure
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 9:50:35 AM
|
|
Thad Smith a �crit :
> jacob navia wrote:
>
>> It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
>> but strndup was missing. Here is a proposed implementation. I would like
>> to see if your sharp eyes see any bug or serious problem with it.
>
> ...
>
>> ---------------------------------------------------------cut here
>> #include <string.h>
>> #include <stdlib.h>
>> /*
>> The strndup function copies not more than n characters (characters that
>> follow a null character are not copied) from string to a dynamically
>> allocated buffer. The copied string shall always be null terminated.
>> */
>> char *strndup(const char *string,size_t s)
>
>
> I haven't looked at the code, but I am a big believer in accurate
> documentation.
>
> Your comment references "n characters", but defines a parameter s. Is
> that n? The comment doesn't say how many characters are copied, only
> that it is not more than n, so I can't rely on the function to copy the
> full strlen(string), even if s exceeds this. I suggest calling the
> result a dynamically allocated char array, rather than a buffer, since
> it isn't necessarily buffering anything. As a matter of fact, since it
> is presumably sized to the input string, there probably isn't room for
> additional characters to be added later, thus not used as a buffer.
>
> The description doesn't say what the results are if insufficient memory
> exists for the new array.
>
Excuse me I just cutted and pasted the specification from the
standards document. It is not my documentation.
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 9:55:16 AM
|
|
CBFalconer a �crit :
> Tom St Denis wrote:
> char *strndup(const char *_string, size_t _len) {
> char *s, *p;
>
> if ((p = s = malloc(_len + 1)))
Here you allocate _len characters even if the string could be
considerably shorter... This wastes space.
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 10:01:59 AM
|
|
On Sat, 02 Dec 2006 23:53:26 +0100, jacob navia wrote:
>Reading comp.std.c
....
>It proposes really interesting functions, among others getline and
>getdelim, that I introduced into lcc-win32 (by coincidence) a few weeks
>ago.
>It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
>but strndup was missing.
strdup, getline and other functions require deallocation by the user.
They were deliberately excluded from the C Standards. Not by
oversight, not because they were difficult to implement, not because
they wouldn't have been 'useful'.
Best regards,
Roland Pibinger
|
|
0
|
|
|
|
Reply
|
rpbg123
|
12/3/2006 11:35:38 AM
|
|
Roland Pibinger a �crit :
> On Sat, 02 Dec 2006 23:53:26 +0100, jacob navia wrote:
>
>>Reading comp.std.c
>
> ...
>
>>It proposes really interesting functions, among others getline and
>>getdelim, that I introduced into lcc-win32 (by coincidence) a few weeks
>>ago.
>>It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
>>but strndup was missing.
>
>
> strdup, getline and other functions require deallocation by the user.
> They were deliberately excluded from the C Standards. Not by
> oversight, not because they were difficult to implement, not because
> they wouldn't have been 'useful'.
>
> Best regards,
> Roland Pibinger
1) What's wrong with the user deallocating?
2) Maybe this view is changing since that technical report is there...
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 11:49:37 AM
|
|
jacob navia said:
> CBFalconer a �crit :
>> BTW, Jacobs code carelessly fails to define size_t.
(Wrong, Chuck!)
>
> ???
> I have
> #include <stdlib.h>
> and that file includes stddef.h that defines size_t
On your particular implementation, possibly, but the Standard doesn't
require this as far as I know. What it *does* require is that size_t is
available as a type to those translation units that have included
<stdlib.h>. So yes, your code was correct in that regard.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/3/2006 2:05:45 PM
|
|
Richard Heathfield a �crit :
> jacob navia said:
>
>
>>CBFalconer a �crit :
>>
>>>BTW, Jacobs code carelessly fails to define size_t.
>
>
> (Wrong, Chuck!)
>
>
>>???
>>I have
>>#include <stdlib.h>
>>and that file includes stddef.h that defines size_t
>
>
> On your particular implementation, possibly, but the Standard doesn't
> require this as far as I know. What it *does* require is that size_t is
> available as a type to those translation units that have included
> <stdlib.h>. So yes, your code was correct in that regard.
>
stdlib.h defines
calloc(size_t,size_t)
malloc(size_t)
qsort(void *,size_t,size_t,int (*)(...etc));
realloc(size_t);
and many others, so I do not see how size_t could be unknown after
including stdlib.h...
Obviously in other implementation they could have defined size_t
several times in several files.
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 2:13:59 PM
|
|
jacob navia said:
<snip>
>
> stdlib.h defines
>
> calloc(size_t,size_t)
> malloc(size_t)
> qsort(void *,size_t,size_t,int (*)(...etc));
> realloc(size_t);
>
> and many others, so I do not see how size_t could be unknown after
> including stdlib.h...
True enough - or, better still, we can simply quote chapter and verse at
Chuck:
"4.10 GENERAL UTILITIES <stdlib.h>
The header <stdlib.h> declares four types and several functions of
general utility, and defines several macros./113/
The types declared are size_t and wchar_t (both described in $4.1.5),..."
<snip>
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/3/2006 2:19:02 PM
|
|
jacob navia wrote:
> CBFalconer a �crit :
>
>> char *strndup(const char *_string, size_t _len) {
>> char *s, *p;
>>
>> if ((p = s = malloc(_len + 1)))
>
> Here you allocate _len characters even if the string could be
> considerably shorter... This wastes space.
The only possible use for the function is to truncate input
strings, as I pointed out. In that case there is no space wasted.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/3/2006 2:55:09 PM
|
|
Roland Pibinger wrote:
> On Sat, 02 Dec 2006 23:53:26 +0100, jacob navia wrote:
>
>> Reading comp.std.c
> ...
>> It proposes really interesting functions, among others getline and
>> getdelim, that I introduced into lcc-win32 (by coincidence) a few
>> weeks ago.
>> It proposes strdup and strndup too. Lcc-win32 proposes already
>> strdup, but strndup was missing.
>
> strdup, getline and other functions require deallocation by the user.
> They were deliberately excluded from the C Standards. Not by
> oversight, not because they were difficult to implement, not because
> they wouldn't have been 'useful'.
By that reasoning malloc, calloc, and realloc should also be
omitted. Not to mention fopen.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/3/2006 3:01:01 PM
|
|
jacob navia wrote:
> CBFalconer a �crit :
>
>> BTW, Jacobs code carelessly fails to define size_t.
>
> ???
> I have
> #include <stdlib.h>
> and that file includes stddef.h that defines size_t
Sorry, you are right there. However there is no requirement that
stdlib includes stddef, only that it define size_t (among other
things).
>> It also return NULL for other reasons than lack of memory, which
>> can only cause confusion.
>>
> ??? Why confusion?
>
> Null means failure
Because the caller can't tell whether or not the system is out of
memory. If you are going to define the 'undefined behaviour' from
calling with an invalid parameter, you might as well make it
something innocuous. Otherwise the only way the user can tell the
cause of the error is to save the input parameter, and test it for
NULL himself after the routine returns a NULL. If he does that he
might as well test first, and not call the routine.
The other basic choice is to let a bad parameter blow up in the
function. Both will work properly if the programmer tests and
avoids passing that bad parameter in the first place. If he
doesn't the 'innocuous behavior' has a much better chance of
producing a user friendly end application. The unsophisticated end
user has little use for a segfault message.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/3/2006 3:25:28 PM
|
|
Richard Heathfield wrote:
>
> jacob navia said:
>
> <snip>
> >
> > stdlib.h defines
> >
> > calloc(size_t,size_t)
> > malloc(size_t)
> > qsort(void *,size_t,size_t,int (*)(...etc));
> > realloc(size_t);
> >
> > and many others, so I do not see how size_t could be unknown after
> > including stdlib.h...
>
> True enough - or, better still,
> we can simply quote chapter and verse at Chuck:
>
> "4.10 GENERAL UTILITIES <stdlib.h>
>
> The header <stdlib.h> declares four types and several functions of
> general utility, and defines several macros./113/
>
> The types declared are size_t and wchar_t
> (both described in $4.1.5),..."
>
> <snip>
We can, but I also use jacob navia's logic
to deduce what's defined in a header,
except for stddef.h which is small enough to learn.
I tend to be more familiar with standard function descriptions
from looking them up repeatedly,
than I am with the standard header descriptions in their entirety.
I know that the ctype functions are described as being
able to take the value of EOF as an argument,
so I know that EOF is defined in ctype.h.
I know that putchar is described as being
able to return the value of EOF,
so I know that EOF is defined in stdio.h.
--
pete
|
|
0
|
|
|
|
Reply
|
pete
|
12/3/2006 4:04:18 PM
|
|
jacob navia wrote:
[ snip ]
> stdlib.h defines
>
> calloc(size_t,size_t)
> malloc(size_t)
> qsort(void *,size_t,size_t,int (*)(...etc));
> realloc(size_t);
>
> and many others, so I do not see how size_t could be unknown after
> including stdlib.h...
>
> Obviously in other implementation they could have defined size_t
> several times in several files.
Lazy? Headers tend to declare rather than define. All four of your
examples fail as prototypes.
realloc's prototype..
void *realloc(void *_ptr, size_t _size);
--
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
|
|
0
|
|
|
|
Reply
|
Joe
|
12/3/2006 5:14:38 PM
|
|
Joe Wright a �crit :
> jacob navia wrote:
> [ snip ]
>
>> stdlib.h defines
>>
>> calloc(size_t,size_t)
>> malloc(size_t)
>> qsort(void *,size_t,size_t,int (*)(...etc));
>> realloc(size_t);
>>
>> and many others, so I do not see how size_t could be unknown after
>> including stdlib.h...
>>
>> Obviously in other implementation they could have defined size_t
>> several times in several files.
>
>
> Lazy? Headers tend to declare rather than define. All four of your
> examples fail as prototypes.
>
> realloc's prototype..
>
> void *realloc(void *_ptr, size_t _size);
>
Please Joe do not use my mail messages as header files :-)
I was just copying, and I wrote (etc...) sometimes to
speed it up. Maybe is lazyness but I just wanted to make a point,
not to write a header file.
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 5:18:51 PM
|
|
[comp.compilers.lcc snipped as there is nothing lcc-specific here]
In article <4572EC68.AF9DE589@yahoo.com>
CBFalconer <cbfalconer@maineline.net> wrote:
>... However there is no requirement that
>stdlib includes stddef, only that it define size_t (among other
>things).
Indeed, in fact including <stdlib.h> must *not* include <stddef.h>,
because stddef.h will (e.g.) define "offsetof", and it must not be
defined if you have included only stdlib.h:
% cat x.c
#include <stdlib.h>
#ifdef offsetof
# error offsetof defined when it should not be
#endif
void f(void){}
%
This x.c translation unit must translate without error, i.e., the
#error must not fire.
(Now that it has been officially announced I can mention this: One
rather painful aspect of obtaining POSIX PSE52 certification for
vxWorks 6.4 was cleaning up our header-file organization so that
"inappropriate" symbols were not defined by including various POSIX
headers. The certification process includes code that reads the
actual headers, searching for all things that look like identifiers,
and emits a test module to make sure they are not "#define"d when
compiling in "POSIX mode". Only names reserved to the implementor
-- things like __users_must_not_use_this_identifier -- are omitted
from this test.
Making header files "do the right thing" when writing them from
scratch is not that bad, but retroactively enforcing such rules in
a system with many years of history is more difficult.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40�39.22'N, 111�50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
|
|
0
|
|
|
|
Reply
|
nospam252 (1722)
|
12/3/2006 6:27:01 PM
|
|
On Sun, 03 Dec 2006 12:49:37 +0100, jacob navia wrote:
>1) What's wrong with the user deallocating?
It's bad style. The responsibility for deallocation becomes unclear.
In your program you have some functions that return a char* that must
be freed and other functions retruning char* without that requirement:
a perfect receipt for a leaking program.
Moreover, functions like getline foster an inefficient style. They
dynamically allocate memory for each line even when most of the lines
would fit into a char[80] buffer and only exceptional cases needed
dynamic allocation.
>2) Maybe this view is changing since that technical report is there...
Why should they abandon good style?
Best regards,
Roland Pibinger
|
|
0
|
|
|
|
Reply
|
rpbg123
|
12/3/2006 7:04:06 PM
|
|
On Sun, 03 Dec 2006 10:01:01 -0500, CBFalconer wrote:
>By that reasoning malloc, calloc, and realloc should also be
>omitted.
Why? Do those functions force you to free something you haven't
allocated?
>Not to mention fopen.
Wrong analogy again. Would you write a function like the following
(probably not):
/* user must call fclose() on the returned FILE* */
FILE *do_something (int i);
Best regards,
Roland Pibinger
|
|
0
|
|
|
|
Reply
|
rpbg123
|
12/3/2006 7:15:11 PM
|
|
Roland Pibinger a �crit :
> On Sun, 03 Dec 2006 12:49:37 +0100, jacob navia wrote:
>
>>1) What's wrong with the user deallocating?
>
>
> It's bad style. The responsibility for deallocation becomes unclear.
> In your program you have some functions that return a char* that must
> be freed and other functions retruning char* without that requirement:
> a perfect receipt for a leaking program.
> Moreover, functions like getline foster an inefficient style. They
> dynamically allocate memory for each line even when most of the lines
> would fit into a char[80] buffer and only exceptional cases needed
> dynamic allocation.
>
>
>>2) Maybe this view is changing since that technical report is there...
>
>
> Why should they abandon good style?
>
> Best regards,
> Roland Pibinger
The responsability of the deallocation is perfectly clear: the specs
specify that the user should deallocate the new space.
And to the argument that this is inefficient, just pass a buffer
(allocated with malloc) to this function. It will NOT touch the
passed buffer unless IT NEEEDS TO!!!
If all your lines are less than 80 characters and you pass it
a buffer of 80, the buffer will be reused!
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 7:47:03 PM
|
|
"Roland Pibinger" <rpbg123@yahoo.com> wrote in message
news:45731fb1.4847710@news.utanet.at...
> On Sun, 03 Dec 2006 10:01:01 -0500, CBFalconer wrote:
>>Not to mention fopen.
>
> Wrong analogy again. Would you write a function like the following
> (probably not):
>
> /* user must call fclose() on the returned FILE* */
> FILE *do_something (int i);
I've written code like that, yes, though more often with <OT>open() /
close()</OT> than fopen() / fclose(). It's also pretty standard when
working with <OT>sockets</OT>; because they require so much f'ing work
to create, people tend to put all that in another function to avoid
clutter.
This whole "who deallocates the returned string" argument is one of the
largest problems I have with C; yes, you can always find the correct
answer if you look at the function specs (assuming they exist), but it's
not obvious and is therefore prone to errors. <OT> I'm often tempted to
use the C-like subset of C++ just so I'll have string objects that
deallocate (er, destruct) themselves when appropriate rather than having
to read function specs to figure things out. The hassle of requiring a
working C++ environment isn't yet worth the gain, though it's getting
closer. </OT>
S
--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking
--
Posted via a free Usenet account from http://www.teranews.com
|
|
0
|
|
|
|
Reply
|
Stephen
|
12/3/2006 8:02:54 PM
|
|
jacob navia wrote:
>
.... snip ...
>
> And to the argument that this is inefficient, just pass a buffer
> (allocated with malloc) to this function. It will NOT touch the
> passed buffer unless IT NEEEDS TO!!!
>
> If all your lines are less than 80 characters and you pass it
> a buffer of 80, the buffer will be reused!
Oh? What if you pass it a locally (automatic storage) buffer. How
is the function going to expand that buffer? What is there to
prevent the noob user (or the one who doesn't read the fine print)
from passing such a buffer? The only way the routine can tell is
by trying to realloc, and if the program blows up the buffer was in
automatic storage. Very efficient. Highly user friendly.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/3/2006 8:41:04 PM
|
|
Roland Pibinger wrote:
> On Sun, 03 Dec 2006 10:01:01 -0500, CBFalconer wrote:
>
>> By that reasoning malloc, calloc, and realloc should also be
>> omitted.
>
> Why? Do those functions force you to free something you haven't
> allocated?
Yes. Which function does the allocating? If you answer 'all
three' then what is wrong with making it 'all four'.
>> Not to mention fopen.
>
> Wrong analogy again. Would you write a function like the following
> (probably not):
>
> /* user must call fclose() on the returned FILE* */
> FILE *do_something (int i);
Certainly would. Although the passed parameters would probably
have a bit more to say. For example, you want to open a file with
a specified name, or prefix, in some system dependent directory,
subject to some condition or other. Or you want to conditionally
override a default file name. Consider logging files.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/3/2006 8:53:38 PM
|
|
Joe Wright <joewwright@comcast.net> writes:
> jacob navia wrote:
> [ snip ]
>> stdlib.h defines
>> calloc(size_t,size_t)
>> malloc(size_t)
>> qsort(void *,size_t,size_t,int (*)(...etc));
>> realloc(size_t);
>> and many others, so I do not see how size_t could be unknown after
>> including stdlib.h...
>> Obviously in other implementation they could have defined size_t
>> several times in several files.
>
> Lazy? Headers tend to declare rather than define. All four of your
> examples fail as prototypes.
How so? A prototype is a function declaration that declares the types
(not necessarily the names) of its parameters. Only the definition
needs the parameter names.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
|
|
0
|
|
|
|
Reply
|
Keith
|
12/3/2006 9:55:00 PM
|
|
Chris Torek <nospam@torek.net> writes:
> [comp.compilers.lcc snipped as there is nothing lcc-specific here]
>
> In article <4572EC68.AF9DE589@yahoo.com>
> CBFalconer <cbfalconer@maineline.net> wrote:
>>... However there is no requirement that
>>stdlib includes stddef, only that it define size_t (among other
>>things).
>
> Indeed, in fact including <stdlib.h> must *not* include <stddef.h>,
> because stddef.h will (e.g.) define "offsetof", and it must not be
> defined if you have included only stdlib.h:
>
> % cat x.c
> #include <stdlib.h>
> #ifdef offsetof
> # error offsetof defined when it should not be
> #endif
> void f(void){}
> %
>
> This x.c translation unit must translate without error, i.e., the
> #error must not fire.
[...]
<stdlib.h> and <stddef.h> could have some kind of logic that causes
<stddef.h> to define offsetof normally, but not to define it if it's
#include'd from <stdlib.h>. The restriction isn't that <stdlib.h> may
not include <stddef.h>; it's that, as your test program shows,
including just <stdlib.h> may not define offsetof.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
|
|
0
|
|
|
|
Reply
|
kst-u (21474)
|
12/3/2006 10:00:49 PM
|
|
CBFalconer a �crit :
> jacob navia wrote:
>
> ... snip ...
>
>>And to the argument that this is inefficient, just pass a buffer
>>(allocated with malloc) to this function. It will NOT touch the
>>passed buffer unless IT NEEEDS TO!!!
>>
>>If all your lines are less than 80 characters and you pass it
>>a buffer of 80, the buffer will be reused!
>
>
> Oh? What if you pass it a locally (automatic storage) buffer. How
> is the function going to expand that buffer? What is there to
> prevent the noob user (or the one who doesn't read the fine print)
> from passing such a buffer? The only way the routine can tell is
> by trying to realloc, and if the program blows up the buffer was in
> automatic storage. Very efficient. Highly user friendly.
>
You have a good point here.
The specs are:
< quote >
4 The application shall ensure that *lineptr is a valid argument that
could be passed to the free function. If *n is nonzero, the application
shall ensure that *lineptr points to an object containing at least *n
characters.
5 The size of the object pointed to by *lineptr shall be increased to
fit the incoming line, if it isn�t already large enough. The characters
read shall be stored in the string pointed to by the argument lineptr
< end quote >
That error would absolutely fatal since it would mean that the
function would pass a wrong pointer to realloc, with disastrous
consequences!
Since there isn't in C a portable way to determine if a memory
block is the result of malloc() we are screwed...
jacob
|
|
0
|
|
|
|
Reply
|
jacob
|
12/3/2006 10:26:02 PM
|
|
CBFalconer <cbfalconer@yahoo.com> writes:
> jacob navia wrote:
>>
> ... snip ...
>>
>> And to the argument that this is inefficient, just pass a buffer
>> (allocated with malloc) to this function. It will NOT touch the
>> passed buffer unless IT NEEEDS TO!!!
>>
>> If all your lines are less than 80 characters and you pass it
>> a buffer of 80, the buffer will be reused!
>
> Oh? What if you pass it a locally (automatic storage) buffer. How
> is the function going to expand that buffer? What is there to
> prevent the noob user (or the one who doesn't read the fine print)
> from passing such a buffer? The only way the routine can tell is
> by trying to realloc, and if the program blows up the buffer was in
> automatic storage. Very efficient. Highly user friendly.
It's worse than that. Attempting to realloc() something that wasn't
allocated by one of the *alloc() functions invokes undefined behavior.
You're as likely to silently corrupt the heap (if the implementation
uses a "heap" for dynamic memory allocation) as to make the program
"blow up".
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
|
|
0
|
|
|
|
Reply
|
Keith
|
12/3/2006 10:40:08 PM
|
|
jacob navia wrote:
> Thad Smith a �crit :
>> jacob navia wrote:
>>
>>> Here is a proposed implementation. I would like
>>> to see if your sharp eyes see any bug or serious problem with it.
....
>>> /*
>>> The strndup function copies not more than n characters (characters that
>>> follow a null character are not copied) from string to a dynamically
>>> allocated buffer. The copied string shall always be null terminated.
>>> */
>>> char *strndup(const char *string,size_t s)
>>
>> I haven't looked at the code, but I am a big believer in accurate
>> documentation.
[suggestions to improve function description deleted]
>
> Excuse me I just cutted and pasted the specification from the
> standards document. It is not my documentation.
Feel free to forward my comments to the author. Still, if you
distribute such a function, I recommend you use a better description.
--
Thad
|
|
0
|
|
|
|
Reply
|
Thad
|
12/3/2006 11:07:00 PM
|
|
In article <0dOdncPFb7H65O_YRVnyjQ@bt.com>,
Richard Heathfield <rjh@see.sig.invalid> wrote:
>>>The thing that does concern me is the spec itself, which seems to me to
>>>suffer from the same flaw as strncpy - i.e. it gives no indication of
>>>whether truncation occurred. But of course that's a design issue, not a C
>>>issue.
>> When I've used my own version of strndup, it's always been make an
^^^^^^^
>> ordinary string from a "counted" string, so there is no question of
>> truncation. I suspect this is the more common use of it, rather than
>> copying a string to a buffer that might not be big enough.
>No, that's not more common - it's just a more *intelligent* use of strncpy.
^^^^^^^
>By far the most common usage of strncpy is from the cargo cult bunch: "I'm
>smart, I know about buffer overruns, I know I should use strncpy instead of
>strcpy, oops, oh look, I just threw away data, ohdearhowsadnevermind."
Um, you seem to have mixed up strndup and strncpy here. But looking
at my own posting, I seem to have done the same thing in the last
sentence. If you know the string is null-terminated but don't know
how big it is, just use strdup. I can't imagine why you'd use strndup
unless you had a counted string or wanted to just copy a prefix of the
string.
-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
|
|
0
|
|
|
|
Reply
|
richard
|
12/4/2006 12:28:41 AM
|
|
In article <1165117782.183141.55200@79g2000cws.googlegroups.com>,
Tom St Denis <tomstdenis@gmail.com> wrote:
>What would the point of strndup be?
As I said in another article, it's useful for converting a counted
string to a null-terminated one.
-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
|
|
0
|
|
|
|
Reply
|
richard
|
12/4/2006 12:30:16 AM
|
|
"CBFalconer" <cbfalconer@yahoo.com> wrote in message
news:45727E44.1EF02917@yahoo.com...
> Tom St Denis wrote:
>> jacob navia wrote:
>>> It proposes strdup and strndup too. Lcc-win32 proposes already
>>> strdup, but strndup was missing. Here is a proposed implementation.
>>> I would like to see if your sharp eyes see any bug or serious
>>> problem with it.
>>
>> What would the point of strndup be? It allocates the memory so
>> there really isn't a problem of an overflow. If you're low on
>> memory ... why are you duplicating a string?
....
> I can see a possible use for it - to duplicate the initial portion
> of a string only, i.e. to truncate it on the right.
Ah, but it's in a "Specification for Safer C Library Functions"; that
implies that strndup() is somehow a safer version of strdup(), like
strncat() and strncpy() are safer versions of strcat() and strcpy().
There is nothing unsafe about strdup() per se, though. The problem with
strdup() is how it's normally used, which is to copy strings that are
returned from functions as pointers to static buffers. This is not
thread-safe/re-entrant, and strndup() doesn't solve that problem at all,
because the problem is needing strdup() in the first place.
If one needs to truncate a string on the right, it's easy enough to do
that with strdup() and some extra code, with malloc() and strncpy(), or
by creating a new function which truncates any string (such as one
returned by strdup()). But such a function is not a "safer" version of
strdup() -- it's entirely new functionality.
S
--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking
--
Posted via a free Usenet account from http://www.teranews.com
|
|
0
|
|
|
|
Reply
|
Stephen
|
12/4/2006 12:49:04 AM
|
|
CBFalconer wrote:
> jacob navia wrote:
>> CBFalconer a �crit :
>>
>>> BTW, Jacobs code carelessly fails to define size_t.
>> ???
>> I have
>> #include <stdlib.h>
>> and that file includes stddef.h that defines size_t
>
> Sorry, you are right there. However there is no requirement that
> stdlib includes stddef, only that it define size_t (among other
> things).
>
>>> It also return NULL for other reasons than lack of memory, which
>>> can only cause confusion.
>>>
>> ??? Why confusion?
>>
>> Null means failure
>
> Because the caller can't tell whether or not the system is out of
> memory. If you are going to define the 'undefined behaviour' from
> calling with an invalid parameter, you might as well make it
> something innocuous. Otherwise the only way the user can tell the
> cause of the error is to save the input parameter, and test it for
> NULL himself after the routine returns a NULL. If he does that he
> might as well test first, and not call the routine.
>
> The other basic choice is to let a bad parameter blow up in the
> function. Both will work properly if the programmer tests and
> avoids passing that bad parameter in the first place. If he
> doesn't the 'innocuous behavior' has a much better chance of
> producing a user friendly end application. The unsophisticated end
> user has little use for a segfault message.
>
Why can't you test the arguments for valid conditions and set errno to
EINVAL when one of the condition fails, and return NULL. Now, if the
validations are good and the memory allocation fails errno will be set
to ENOMEM. Now you know why you failed and can get the error message
with strerror().
#include <errno.h>
#include <stdlib.h>
char *
dupnstr( const char *source, size_t size )
{
size_t length;
char *wrk, *rv = NULL;
if ( source == NULL || *source == 0 || size == 0 )
errno = EINVAL;
else {
for ( length = 0, wrk = (char *)source; *wrk; length++ ) wrk++;
if ( length < size ) size = length;
if ( rv = malloc( size + 1 ) ) {
for( wrk = rv; size; size-- ) *wrk++ = *source++;
*wrk = 0;
}
}
return rv;
}
Regards,
Stan Milam.
|
|
0
|
|
|
|
Reply
|
Stan
|
12/4/2006 3:51:24 AM
|
|
Stan Milam said:
<snip>
>
> Why can't you test the arguments for valid conditions and set errno to
> EINVAL when one of the condition fails, and return NULL. Now, if the
> validations are good and the memory allocation fails errno will be set
> to ENOMEM. Now you know why you failed and can get the error message
> with strerror().
Except that neither EINVAL nor ENOMEM is defined by the Standard.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/4/2006 4:43:37 AM
|
|
jacob navia said:
<snip>
>
> Since there isn't in C a portable way to determine if a memory
> block is the result of malloc() we are screwed...
There is actually such a way. It's called "programming". When we call
malloc, obviously we know we're calling malloc, so we're in an ideal
position to record the fact that this particular pointer was returned by
malloc. The fact that there isn't some magical "is_malloc" function in ISO
C doesn't mean that there is no portable way to achieve what we want.
It's the same as with block sizes - "if you need to know this, well, at one
point in the program you *do* know it, so the answer is simply: DON'T
FORGET".
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/4/2006 4:48:06 AM
|
|
Richard Tobin said:
<snip>
>
> Um, you seem to have mixed up strndup and strncpy here.
Mea culpa.
> But looking
> at my own posting, I seem to have done the same thing in the last
> sentence.
Youa culpa!
> If you know the string is null-terminated but don't know
> how big it is, just use strdup.
If you don't know how big it is, you might want to find out before calling
strdup. (I'm thinking of possible Denial of Memory attacks.)
> I can't imagine why you'd use strndup
> unless you had a counted string or wanted to just copy a prefix of the
> string.
Yes - splitting up a CSV line into tokens, perhaps (where you've answered
the "how long" question with something like strchr and ptr arithmetic).
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/4/2006 4:53:15 AM
|
|
On Sun, 03 Dec 2006 21:55:00 GMT, Keith Thompson <kst-u@mib.org>
wrote:
>Joe Wright <joewwright@comcast.net> writes:
>> jacob navia wrote:
>> [ snip ]
>>> stdlib.h defines
>>> calloc(size_t,size_t)
>>> malloc(size_t)
>>> qsort(void *,size_t,size_t,int (*)(...etc));
>>> realloc(size_t);
>>> and many others, so I do not see how size_t could be unknown after
>>> including stdlib.h...
>>> Obviously in other implementation they could have defined size_t
>>> several times in several files.
>>
>> Lazy? Headers tend to declare rather than define. All four of your
>> examples fail as prototypes.
>
>How so? A prototype is a function declaration that declares the types
>(not necessarily the names) of its parameters. Only the definition
>needs the parameter names.
All the samples seem to be missing the return type.
Remove del for email
|
|
0
|
|
|
|
Reply
|
Barry
|
12/4/2006 6:53:06 AM
|
|
Richard Heathfield wrote:
> Richard Tobin said:
>
> <snip>
>>
>> Um, you seem to have mixed up strndup and strncpy here.
>
> Mea culpa.
>
>> But looking at my own posting, I seem to have done the same thing
>> in the last sentence.
>
> Youa culpa!
We'alla culpa!
.... snip ...
>
>> I can't imagine why you'd use strndup unless you had a counted
>> string or wanted to just copy a prefix of the string.
>
> Yes - splitting up a CSV line into tokens, perhaps (where you've
> answered the "how long" question with something like strchr and
> ptr arithmetic).
Which is basically my function 'toksplit', which can be thought of
as a combination of strchr and strncpy, plus fol-de-rol:
const char *toksplit(const char *src, /* Source of tokens */
char tokchar, /* token delimiting char */
char *token, /* receiver of parsed token */
size_t lgh) /* length token can receive */
/* not including final '\0' */
{
if (src) {
while (' ' == *src) src++;
while (*src && (tokchar != *src)) {
if (lgh) {
*token++ = *src;
--lgh;
}
src++;
}
if (*src && (tokchar == *src)) src++;
}
*token = '\0';
return src;
} /* toksplit */
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/4/2006 6:56:11 AM
|
|
Stan Milam wrote:
> CBFalconer wrote:
>> jacob navia wrote:
>>> CBFalconer a �crit :
>>>
.... snip ...
>>>>
>>>> It also return NULL for other reasons than lack of memory, which
>>>> can only cause confusion.
>>>>
>>> ??? Why confusion?
>>>
>>> Null means failure
>>
>> Because the caller can't tell whether or not the system is out of
>> memory. If you are going to define the 'undefined behaviour' from
>> calling with an invalid parameter, you might as well make it
>> something innocuous. Otherwise the only way the user can tell the
>> cause of the error is to save the input parameter, and test it for
>> NULL himself after the routine returns a NULL. If he does that he
>> might as well test first, and not call the routine.
>>
>> The other basic choice is to let a bad parameter blow up in the
>> function. Both will work properly if the programmer tests and
>> avoids passing that bad parameter in the first place. If he
>> doesn't the 'innocuous behavior' has a much better chance of
>> producing a user friendly end application. The unsophisticated end
>> user has little use for a segfault message.
>
> Why can't you test the arguments for valid conditions and set errno
> to EINVAL when one of the condition fails, and return NULL. Now,
> if the validations are good and the memory allocation fails errno
> will be set to ENOMEM. Now you know why you failed and can get the
> error message with strerror().
In part because I disapprove of global variables.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/4/2006 7:00:16 AM
|
|
Barry Schwarz said:
> On Sun, 03 Dec 2006 21:55:00 GMT, Keith Thompson <kst-u@mib.org>
> wrote:
>
>>Joe Wright <joewwright@comcast.net> writes:
>>> jacob navia wrote:
>>> [ snip ]
>>>> stdlib.h defines
>>>> calloc(size_t,size_t)
>>>> malloc(size_t)
>>>> qsort(void *,size_t,size_t,int (*)(...etc));
>>>> realloc(size_t);
>>>> and many others, so I do not see how size_t could be unknown after
>>>> including stdlib.h...
>>>> Obviously in other implementation they could have defined size_t
>>>> several times in several files.
>>>
>>> Lazy? Headers tend to declare rather than define. All four of your
>>> examples fail as prototypes.
>>
>>How so? A prototype is a function declaration that declares the types
>>(not necessarily the names) of its parameters. Only the definition
>>needs the parameter names.
>
> All the samples seem to be missing the return type.
Guys, guys, what's the matter with you? Mr Navia was just showing how often
size_t crops up in <stdlib.h>, for heaven's sake! He has a perfectly valid
point, which is borne out by the Standard. If you want <stdlib.h> you know
where to find it.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/4/2006 7:04:36 AM
|
|
In article <u7qdnRIzJbLyNO7YnZ2dnUVZ8tSdnZ2d@bt.com>,
Richard Heathfield <rjh@see.sig.invalid> wrote:
>> If you know the string is null-terminated but don't know
>> how big it is, just use strdup.
>If you don't know how big it is, you might want to find out before calling
>strdup. (I'm thinking of possible Denial of Memory attacks.)
It wouldn't be a very *good* denial of memory attack. strdup only
produces a string as big as one you've already got. You've already
made the mistake by reading in or creating the existing string.
It's not impossible for it to be a real consideration I suppose. You
might not want to call strdup inside your operating system kernel on a
string passed in by the user. But strndup is probably not the place
to fix the problem in that case either.
-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
|
|
0
|
|
|
|
Reply
|
richard
|
12/4/2006 11:33:25 AM
|
|
On Sun, 3 Dec 2006 14:02:54 -0600, "Stephen Sprunk" wrote:
>"Roland Pibinger" <rpbg123@yahoo.com> wrote
>> Would you write a function like the following
>> (probably not):
>>
>> /* user must call fclose() on the returned FILE* */
>> FILE *do_something (int i);
>
>I've written code like that, yes, though more often with <OT>open() /
>close()</OT> than fopen() / fclose(). It's also pretty standard when
>working with <OT>sockets</OT>; because they require so much f'ing work
>to create, people tend to put all that in another function to avoid
>clutter.
IMO, there is no problem when you write your own symmetric open
(create, connect, ...) and close (cleanup, disconnect, ...) functions,
e.g.
Handle h = my_open (...);
// ...
my_close (h);
>This whole "who deallocates the returned string" argument is one of the
>largest problems I have with C; yes, you can always find the correct
>answer if you look at the function specs (assuming they exist), but it's
>not obvious and is therefore prone to errors.
So just don't do it, i.e. don't return a string that has to be freed
by the caller. Even the Windows API uses that convention, AFAIK.
Best wishes,
Roland Pibinger
|
|
0
|
|
|
|
Reply
|
rpbg123
|
12/4/2006 11:34:47 AM
|
|
Richard Heathfield wrote:
> Stan Milam said:
>
> <snip>
> >
> > Why can't you test the arguments for valid conditions and set errno to
> > EINVAL when one of the condition fails, and return NULL. Now, if the
> > validations are good and the memory allocation fails errno will be set
> > to ENOMEM. Now you know why you failed and can get the error message
> > with strerror().
>
> Except that neither EINVAL nor ENOMEM is defined by the Standard.
>
Ah, another glaring deficiency of the standard. Every implementation I
have used for over 20 years has had EINVAL and ENOMEM defined.
Regards,
Stan Milam.
|
|
0
|
|
|
|
Reply
|
stmilam
|
12/4/2006 1:26:25 PM
|
|
stmilam@yahoo.com said:
>
> Richard Heathfield wrote:
>> Stan Milam said:
>>
>> <snip>
>> >
>> > Why can't you test the arguments for valid conditions and set errno to
>> > EINVAL when one of the condition fails, and return NULL. Now, if the
>> > validations are good and the memory allocation fails errno will be set
>> > to ENOMEM. Now you know why you failed and can get the error message
>> > with strerror().
>>
>> Except that neither EINVAL nor ENOMEM is defined by the Standard.
>>
>
> Ah, another glaring deficiency of the standard. Every implementation I
> have used for over 20 years has had EINVAL and ENOMEM defined.
I don't see why that means the Standard is deficient. By the same reasoning,
someone who has only ever used Turbo C 2.0 could claim that the Standard is
glaringly deficient in its omission of initgraph() from the library section
despite its being present in every implementation that person has used for
over 20 years - but this would not say so much about the Standard as it
would about the person making the claim!
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/4/2006 1:35:55 PM
|
|
Richard Heathfield <rjh@see.sig.invalid> writes:
> stmilam@yahoo.com said:
>> Richard Heathfield wrote:
[...]
>>> Except that neither EINVAL nor ENOMEM is defined by the Standard.
>>
>> Ah, another glaring deficiency of the standard. Every implementation I
>> have used for over 20 years has had EINVAL and ENOMEM defined.
>
> I don't see why that means the Standard is deficient. By the same reasoning,
> someone who has only ever used Turbo C 2.0 could claim that the Standard is
> glaringly deficient in its omission of initgraph() from the library section
> despite its being present in every implementation that person has used for
> over 20 years - but this would not say so much about the Standard as it
> would about the person making the claim!
Yes, but EINVAL ("Invalid argument") and ENOMEM ("Not enough space")
are much more generic, perhaps to the point of being universal, than
something like initgraph().
The only E* macros defined by the standard in <errno.h> are EDOM,
EILSEQ, and ERANGE. I don't see the lack of EINVAL and ENOMEM as
"glaring deficiencies", but it would have been perfectly reasonable to
include them.
On the other hand, standardizing more E* macros would encourage the
use of the ugly errno mechanism. On the other other hand, it would be
nice if the standard offered something better. On the other**3 hand,
defining a decent error handling mechanism for C without turning it
into C++ would be non-trivial.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
|
|
0
|
|
|
|
Reply
|
Keith
|
12/4/2006 8:07:58 PM
|
|
Richard Heathfield wrote:
> jacob navia said:
>
> <snip>
>
>>Since there isn't in C a portable way to determine if a memory
>>block is the result of malloc() we are screwed...
>
>
> There is actually such a way. It's called "programming". When we call
> malloc, obviously we know we're calling malloc, so we're in an ideal
> position to record the fact that this particular pointer was returned by
> malloc. The fact that there isn't some magical "is_malloc" function in ISO
> C doesn't mean that there is no portable way to achieve what we want.
>
> It's the same as with block sizes - "if you need to know this, well, at one
> point in the program you *do* know it, so the answer is simply: DON'T
> FORGET".
>
Please....
You really mean that for users to use this getline()
function they should set up a complicated system of maintaining a list
of memory allocated with their sizes???
|
|
0
|
|
|
|
Reply
|
jacob
|
12/4/2006 8:38:55 PM
|
|
jacob navia said:
> Richard Heathfield wrote:
>> jacob navia said:
>>
>> <snip>
>>
>>>Since there isn't in C a portable way to determine if a memory
>>>block is the result of malloc() we are screwed...
>>
>>
>> There is actually such a way. It's called "programming". When we call
>> malloc, obviously we know we're calling malloc, so we're in an ideal
>> position to record the fact that this particular pointer was returned by
>> malloc. The fact that there isn't some magical "is_malloc" function in
>> ISO C doesn't mean that there is no portable way to achieve what we want.
>>
>> It's the same as with block sizes - "if you need to know this, well, at
>> one point in the program you *do* know it, so the answer is simply: DON'T
>> FORGET".
>>
>
> Please....
>
> You really mean that for users to use this getline()
> function they should set up a complicated system of maintaining a list
> of memory allocated with their sizes???
No, I only mean that it is possible for a program to keep track of such
things and, if it is *necessary*, then of course it should be done. If
getline() makes memory management too much of a pain for a particular task,
well, there is no shortage of alternatives.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/4/2006 10:51:13 PM
|
|
"Roland Pibinger" <rpbg123@yahoo.com> wrote in message
news:4574057c.13842344@news.utanet.at...
> On Sun, 3 Dec 2006 14:02:54 -0600, "Stephen Sprunk" wrote:
>>I've written code like that, yes, though more often with <OT>open() /
>>close()</OT> than fopen() / fclose(). It's also pretty standard when
>>working with <OT>sockets</OT>; because they require so much
>>f'ing work to create, people tend to put all that in another function
>>to
>>avoid clutter.
>
> IMO, there is no problem when you write your own symmetric open
> (create, connect, ...) and close (cleanup, disconnect, ...) functions,
> e.g.
>
> Handle h = my_open (...);
> // ...
> my_close (h);
Except that, in the case of <OT>sockets</OT>, all you need is a simple
close(fd) to get rid of them. I suppose I could create a my_close(fd)
function, but in reality all it'd do is close(fd), and people would end
up not using it because it'd add overhead.
>>This whole "who deallocates the returned string" argument is one of
>>the
>>largest problems I have with C; yes, you can always find the correct
>>answer if you look at the function specs (assuming they exist), but
>>it's
>>not obvious and is therefore prone to errors.
>
> So just don't do it, i.e. don't return a string that has to be freed
> by the caller. Even the Windows API uses that convention, AFAIK.
.... and the result is that any time you call a huge number of
<OT>Windows API</OT> functions, you have to call them twice: once with a
NULL argument to find out how big of a buffer to create, and again after
you've created the buffer to actually get the data you wanted. That's
not so great an idea when you consider the overhead of having to do all
your syscalls twice, and it leads to people taking shortcuts that end up
biting them later.
S
--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking
--
Posted via a free Usenet account from http://www.teranews.com
|
|
0
|
|
|
|
Reply
|
Stephen
|
12/5/2006 6:39:39 AM
|
|
jacob navia <jacob@jacob.remcomp.fr> wrote:
> Roland Pibinger a �crit :
> > On Sat, 02 Dec 2006 23:53:26 +0100, jacob navia wrote:
> >
> >>It proposes really interesting functions, among others getline and
> >>getdelim, that I introduced into lcc-win32 (by coincidence) a few weeks
> >>ago.
> >>It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
> >>but strndup was missing.
> >
> > strdup, getline and other functions require deallocation by the user.
> > They were deliberately excluded from the C Standards. Not by
> > oversight, not because they were difficult to implement, not because
> > they wouldn't have been 'useful'.
Actually, TTBOMK they were not included for three reasons:
- There was not enough prior art for them back then, as explained in the
Rationale;
- There would have to be an argument about whether they belonged in
<string.h>, because they deal with strings, or in <stdlib.h>, because
they deal with memory allocation, and no matter which of those were
chosen, there would always have been accusations of the Committee
getting it horribly wrong (because this is precisely the kind of anal-
retentive detail about which some people who should get a hobby Have
Opinions);
- They are remarkably simple for the user-programmer to write.
> 1) What's wrong with the user deallocating?
For a programmer with more than a year's experience, nothing.
> 2) Maybe this view is changing since that technical report is there...
That TR looks like a follow-up on that other "safer" function report.
It, too, was broken.
Richard
|
|
0
|
|
|
|
Reply
|
rlb
|
12/5/2006 11:21:52 AM
|
|
rpbg123@yahoo.com (Roland Pibinger) wrote:
> On Sun, 03 Dec 2006 10:01:01 -0500, CBFalconer wrote:
> >Not to mention fopen.
>
> Wrong analogy again. Would you write a function like the following
> (probably not):
>
> /* user must call fclose() on the returned FILE* */
> FILE *do_something (int i);
Yes, I would definitely write a function like that. It is very useful
for, say, opening a log file and writing a standardised header to it.
The calling function(s) then write log entries to it, and close it when
it's no longer needed.
Richard
|
|
0
|
|
|
|
Reply
|
rlb (4118)
|
12/5/2006 11:23:18 AM
|
|
In article <45731fb1.4847710@news.utanet.at>,
Roland Pibinger <rpbg123@yahoo.com> wrote:
>>By that reasoning malloc, calloc, and realloc should also be
>>omitted.
>Why? Do those functions force you to free something you haven't
>allocated?
Um, why do you consider that you have done the allocating in the
case of calloc() but not in the case of strdup()? Both allocate
some memory and store values in it.
>Would you write a function like the following
>(probably not):
>
>/* user must call fclose() on the returned FILE* */
>FILE *do_something (int i);
Whyever not?
-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
|
|
0
|
|
|
|
Reply
|
richard
|
12/5/2006 12:16:34 PM
|
|
rlb@hoekstra-uitgeverij.nl (Richard Bos) writes:
> rpbg123@yahoo.com (Roland Pibinger) wrote:
>> On Sun, 03 Dec 2006 10:01:01 -0500, CBFalconer wrote:
>> >Not to mention fopen.
>>
>> Wrong analogy again. Would you write a function like the following
>> (probably not):
>>
>> /* user must call fclose() on the returned FILE* */
>> FILE *do_something (int i);
>
> Yes, I would definitely write a function like that. It is very useful
> for, say, opening a log file and writing a standardised header to it.
> The calling function(s) then write log entries to it, and close it when
> it's no longer needed.
That's a good example. In effect, the function is a wrapper around
fopen(). If you absolutely insist on having strict pairings of
functions for allocation and deallocation, you could write
FILE *open_log(int i);
int close_log(FILE *log);
but close_log would do exactly the same thing as fclose().
In general, some functions allocate resources, and other functions
deallocate those same resources. Having a strict one-to-one mapping
between the two can be helpful, I suppose, but IMHO it's not strictly
necessary. Even the standard memory allocation functions don't do
this; malloc() and calloc() both allocate memory, free() deallocates
it, and realloc() potentially does both. The clean symmetry of
fopen() and fclose() is broken by freopen().
Programmers simply have to *read the documentation* to find out which
function does what. The interface described by the documentation
should be as simple as possible, but no simpler. If it makes sense to
have a strict pairing of allocators and deallocators, by all means do
that; if it doesn't, don't.
(In OO terms, there's no requirement to have exactly one constructor
and exactly one destructor for a given type.)
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
|
|
0
|
|
|
|
Reply
|
kst-u (21474)
|
12/5/2006 8:21:30 PM
|
|
Richard Heathfield wrote:
> stmilam@yahoo.com said:
>
>> Richard Heathfield wrote:
>>> Stan Milam said:
>>>
>>> <snip>
>>>> Why can't you test the arguments for valid conditions and set errno to
>>>> EINVAL when one of the condition fails, and return NULL. Now, if the
>>>> validations are good and the memory allocation fails errno will be set
>>>> to ENOMEM. Now you know why you failed and can get the error message
>>>> with strerror().
>>> Except that neither EINVAL nor ENOMEM is defined by the Standard.
>>>
>> Ah, another glaring deficiency of the standard. Every implementation I
>> have used for over 20 years has had EINVAL and ENOMEM defined.
>
> I don't see why that means the Standard is deficient. By the same reasoning,
> someone who has only ever used Turbo C 2.0 could claim that the Standard is
> glaringly deficient in its omission of initgraph() from the library section
> despite its being present in every implementation that person has used for
> over 20 years - but this would not say so much about the Standard as it
> would about the person making the claim!
>
I haven't used Turbo C since around 1992. I never cared for it either.
I've used C on several platforms, mostly UNIX these past few years,
but also DOS and some embedded stuff, using a wide variety of compilers.
EINVAL and ENOMEM have always been defined so why not standardize
them? Now as for the initgraph() analogy (this shows you know more
about Turbo C than I do), well, that is just silly. Of course a
compiler vendor's graphic library is not very likely to be portable,
ergo not a good candidate for standardization, but every C program I
know has functions using arguments, and dynamic memory allocation is a
way of life in C (and one of the truly great things about C). Why not
define values for errno when invalid argument values are used and when
no memory is available for allocation? It seems such a simple and
reasonable idea. Moreover, why isn't there a required compiler flag
that when used will cause the compiler to generate an error when
undefined behavior is encountered? Oh, yeah, it's called LINT!
Now I suppose I am in over my head discoursing such weighty issues with
an erudite C programmer such as yourself. I am by my own admission just
an old country programmer trying to make a living. But I think there is
some sensibility to what I suggest.
I am not finished yet, but I am feeling ill - like I might be having a
stroke - seriously!
Got to run.
--
Regards,
Stan Milam
=============================================================
Charter Member of The Society for Mediocre Guitar Playing on
Expensive Instruments, Ltd.
=============================================================
|
|
0
|
|
|
|
Reply
|
Stan
|
12/6/2006 4:09:53 AM
|
|
Stan Milam said:
<snip>
> Why not
> define values for errno when invalid argument values are used and when
> no memory is available for allocation? It seems such a simple and
> reasonable idea.
Implementors do not introduce extensions that they consider complicated and
unreasonable - at least, not often! Nevertheless, the mere introduction of
a simple and reasonable extension by one implementor does not oblige ISO to
adopt that extension and standardise it. They might do so, but they might
not. If you disagree with their decision, by all means take it up with
them. If you are sufficiently persuasive, maybe EINVAL and ENOMEM will
finally make it into the Standard.
> Moreover, why isn't there a required compiler flag
> that when used will cause the compiler to generate an error when
> undefined behavior is encountered?
What should the compiler do with the following code, if such a flag is in
operation?
#include <stddef.h>
void foo(int *p, int *q, size_t len)
{
while(len--)
{
*p++ = *q++;
}
}
Do we need a compiler error ("Error: undefined behaviour in foo()") here, or
not? The answer is: it all depends. Specifically, it depends on how the
function is called. And the compiler might never see this code and the
calling code in the same invocation.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/6/2006 8:23:14 AM
|
|
Roland Pibinger wrote:
> On Sat, 02 Dec 2006 23:53:26 +0100, jacob navia wrote:
> >Reading comp.std.c
> ...
> >It proposes really interesting functions, among others getline and
> >getdelim, that I introduced into lcc-win32 (by coincidence) a few weeks
> >ago.
> >It proposes strdup and strndup too. Lcc-win32 proposes already strdup,
> >but strndup was missing.
>
> strdup, getline and other functions require deallocation by the user.
So does malloc, calloc, and realloc. In a very similar sense, so does
fopen.
> They were deliberately excluded from the C Standards.
Indeed, and gets was deliberately kept in, and the C *language* is
responsible for something like 75% of all security exploits in
existence. At this point, someone should call 3 strikes and you're
out.
> [...] Not by
> oversight, not because they were difficult to implement, not because
> they wouldn't have been 'useful'.
And not for any good reason either. I would have some sympathy if they
demanded a name change to: strcpyalloc. Then they could assert a
simple naming pattern: functions ending in "alloc" need to return their
allocated storage to free(). Whatever, I wouldn't take any action by
the C standard committee to suggest that such a course of action is
even plausibly rational.
--
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/
|
|
0
|
|
|
|
Reply
|
websnarf
|
12/6/2006 8:41:56 AM
|
|
Roland Pibinger wrote:
> On Sun, 03 Dec 2006 12:49:37 +0100, jacob navia wrote:
> >1) What's wrong with the user deallocating?
>
> It's bad style.
Yes, but that would make it more consistent with C's design. I mean
the C language makes myspace.com look elegant and attractive.
> [...] The responsibility for deallocation becomes unclear.
> In your program you have some functions that return a char* that must
> be freed and other functions retruning char* without that requirement:
> a perfect receipt for a leaking program.
This sounds like an argument for removing malloc/calloc/realloc and
fopen from the standard.
> Moreover, functions like getline foster an inefficient style. They
> dynamically allocate memory for each line even when most of the lines
> would fit into a char[80] buffer and only exceptional cases needed
> dynamic allocation.
You sir, are the kind of person that virus/exploit writers have wet
dreams about. They know you exist, and they spend their time
fantasizing about you.
> >2) Maybe this view is changing since that technical report is there...
>
> Why should they abandon good style?
Doesn't that assume they had some good style that they were abandoning
in the first place?
--
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/
|
|
0
|
|
|
|
Reply
|
websnarf
|
12/6/2006 8:49:59 AM
|
|
On Tue, 05 Dec 2006 20:21:30 GMT, Keith Thompson wrote:
>rlb@hoekstra-uitgeverij.nl (Richard Bos) writes:
>> rpbg123@yahoo.com (Roland Pibinger) wrote:
>>> On Sun, 03 Dec 2006 10:01:01 -0500, CBFalconer wrote:
>>> >Not to mention fopen.
>>>
>>> Wrong analogy again. Would you write a function like the following
>>> (probably not):
>>>
>>> /* user must call fclose() on the returned FILE* */
>>> FILE *do_something (int i);
>>
>> Yes, I would definitely write a function like that. It is very useful
>> for, say, opening a log file and writing a standardised header to it.
>> The calling function(s) then write log entries to it, and close it when
>> it's no longer needed.
But it's clearer to write
FILE* log = fopen(...);
write_log_header (log);
fclose (log);
>That's a good example. In effect, the function is a wrapper around
>fopen().
Where do you find that style? Certainly not in K&R.
>If you absolutely insist on having strict pairings of
>functions for allocation and deallocation, you could write
> FILE *open_log(int i);
> int close_log(FILE *log);
>but close_log would do exactly the same thing as fclose().
The point of open_log and close_log is that they refer to a 'log
device' which needs not be a file. When you use FILE* in the interface
you cannot change the log destination without breaking user code. You
probably also use a write_log function (instead of fwrite) which
prepends the log enty with a timestamp, line number, ...
>In general, some functions allocate resources, and other functions
>deallocate those same resources. Having a strict one-to-one mapping
>between the two can be helpful, I suppose, but IMHO it's not strictly
>necessary.
It's not necessary but it helps you create large-scale applications.
It's an important idiom to keep track of the resources in a C program.
>Even the standard memory allocation functions don't do
>this; malloc() and calloc() both allocate memory, free() deallocates
>it, and realloc() potentially does both. The clean symmetry of
>fopen() and fclose() is broken by freopen().
Not by freopen() which just associates the existing stream with a new
file.
>Programmers simply have to *read the documentation* to find out which
>function does what.
Yes, always, but that's not the point here.
>The interface described by the documentation
>should be as simple as possible, but no simpler. If it makes sense to
>have a strict pairing of allocators and deallocators, by all means do
>that; if it doesn't, don't.
Programming in any language becomes manageable only when people adhere
to certain styles and idioms. If you merely knew the language syntax
you would know handly anything of a language. IMO, it's important to
propagate idioms that 'work' and warn of those that don't.
>(In OO terms, there's no requirement to have exactly one constructor
>and exactly one destructor for a given type.)
I don't see a problem in more than one open/create/init function per
'object', e.g. open_log1(), open_log2(), ...
Best regards,
Roland Pibinger
|
|
0
|
|
|
|
Reply
|
rpbg123 (870)
|
12/6/2006 9:13:01 AM
|
|
rpbg123@yahoo.com (Roland Pibinger) wrote:
> On Tue, 05 Dec 2006 20:21:30 GMT, Keith Thompson wrote:
> >rlb@hoekstra-uitgeverij.nl (Richard Bos) writes:
> >> rpbg123@yahoo.com (Roland Pibinger) wrote:
> >>> On Sun, 03 Dec 2006 10:01:01 -0500, CBFalconer wrote:
> >>> >Not to mention fopen.
> >>>
> >>> Wrong analogy again. Would you write a function like the following
> >>> (probably not):
> >>>
> >>> /* user must call fclose() on the returned FILE* */
> >>> FILE *do_something (int i);
> >>
> >> Yes, I would definitely write a function like that. It is very useful
> >> for, say, opening a log file and writing a standardised header to it.
> >> The calling function(s) then write log entries to it, and close it when
> >> it's no longer needed.
>
> But it's clearer to write
>
> FILE* log = fopen(...);
> write_log_header (log);
> fclose (log);
Says you. You're probably right if it happens once. But if you have over
a dozen places in your program where you start a log file?
> >That's a good example. In effect, the function is a wrapper around
> >fopen().
>
> Where do you find that style? Certainly not in K&R.
I don't find so many styles in K&R. I don't think you'll find sparse
arrays in K&R. Does that mean we shouldn't use them?
> >Programmers simply have to *read the documentation* to find out which
> >function does what.
>
> Yes, always, but that's not the point here.
Yes, it is. If you read the documentation for start_logfile(), you will
read that you need to call fclose() to close the log file. If you do not
read that documentation, you will indeed be completely puzzled by this
"unclear" style, but then you're a fool.
> >The interface described by the documentation
> >should be as simple as possible, but no simpler. If it makes sense to
> >have a strict pairing of allocators and deallocators, by all means do
> >that; if it doesn't, don't.
>
> Programming in any language becomes manageable only when people adhere
> to certain styles and idioms. If you merely knew the language syntax
> you would know handly anything of a language. IMO, it's important to
> propagate idioms that 'work' and warn of those that don't.
And this idiom works. IMnot very humbleO _and_ E.
Richard
|
|
0
|
|
|
|
Reply
|
rlb (4118)
|
12/6/2006 2:13:53 PM
|
|
Richard Heathfield wrote:
>
> What should the compiler do with the following code, if such a flag is in
> operation?
>
> #include <stddef.h>
> void foo(int *p, int *q, size_t len)
> {
> while(len--)
> {
> *p++ = *q++;
> }
> }
>
> Do we need a compiler error ("Error: undefined behaviour in foo()") here, or
> not? The answer is: it all depends. Specifically, it depends on how the
> function is called. And the compiler might never see this code and the
> calling code in the same invocation.
>
If that is what it takes, then yes. And what I meant was that before
any function made its way into a production library or program it would
have to be compiled with said flag. You can remove all of the ambiguity
of the above code:
for (; len; p++, q++, len-- ) *p = *q;
Which is what is happening and what you intended.
--
Regards,
Stan Milam
=============================================================
Charter Member of The Society for Mediocre Guitar Playing on
Expensive Instruments, Ltd.
=============================================================
|
|
0
|
|
|
|
Reply
|
Stan
|
12/7/2006 12:51:52 AM
|
|
CBFalconer wrote:
> Stan Milam wrote:
>> Now,
>> if the validations are good and the memory allocation fails errno
>> will be set to ENOMEM. Now you know why you failed and can get the
>> error message with strerror().
>
> In part because I disapprove of global variables.
>
Well, at least we have one thing in common.
--
Regards,
Stan Milam
=============================================================
Charter Member of The Society for Mediocre Guitar Playing on
Expensive Instruments, Ltd.
=============================================================
|
|
0
|
|
|
|
Reply
|
stmilam (106)
|
12/7/2006 12:53:56 AM
|
|
On Thu, 7 Dec 2006, Stan Milam wrote:
>
> Richard Heathfield wrote:
[re: a hypothetical compiler flag "-warn-on-UB"]
>> What should the compiler do with the following code, if such a flag is in
>> operation?
>>
>> #include <stddef.h>
>> void foo(int *p, int *q, size_t len)
>> {
>> while(len--)
>> {
>> *p++ = *q++;
>> }
>> }
>>
>> Do we need a compiler error ("Error: undefined behaviour in foo()") here,
>> or not? The answer is: it all depends. Specifically, it depends on how the
>> function is called. And the compiler might never see this code and the
>> calling code in the same invocation.
>
> If that is what it takes, then yes. And what I meant was that before any
> function made its way into a production library or program it would have to
> be compiled with said flag. You can remove all of the ambiguity of the above
> code:
>
> for (; len; p++, q++, len-- ) *p = *q;
>
> Which is what is happening and what you intended.
However, you have not removed any of the possible undefined behaviors
of that code. (IMO, your revised version is harder to read, too, so it
looks like a lose-lose!)
Consider that *p++ increments p, /then/ dereferences it; and then
consider what happens if p's value is null or indeterminate.
-Arthur
|
|
0
|
|
|
|
Reply
|
ajonospam (382)
|
12/7/2006 1:46:20 AM
|
|
Stan Milam wrote:
> Richard Heathfield wrote:
>>
>> What should the compiler do with the following code, if such a
>> flag is in operation?
>>
>> #include <stddef.h>
>> void foo(int *p, int *q, size_t len)
>> {
>> while(len--)
>> {
>> *p++ = *q++;
>> }
>> }
>>
>> Do we need a compiler error ("Error: undefined behaviour in foo()")
>> here, or not? The answer is: it all depends. Specifically, it
>> depends on how the function is called. And the compiler might never
>> see this code and the calling code in the same invocation.
>
> If that is what it takes, then yes. And what I meant was that
> before any function made its way into a production library or
> program it would have to be compiled with said flag. You can remove
> all of the ambiguity of the above code:
>
> for (; len; p++, q++, len-- ) *p = *q;
>
> Which is what is happening and what you intended.
Why obfuscate the original, and simple:
while (len--) *p++ = *q++;
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
|
|
0
|
|
|
|
Reply
|
CBFalconer
|
12/7/2006 2:29:56 AM
|
|
Stan Milam said:
<snip>
> And what I meant was that before
> any function made its way into a production library or program it would
> have to be compiled with said flag. You can remove all of the ambiguity
> of the above code:
>
> for (; len; p++, q++, len-- ) *p = *q;
If I understand you correctly, your ideal compiler would reject this code
because it might exhibit undefined behaviour.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
|
|
0
|
|
|
|
Reply
|
Richard
|
12/7/2006 9:17:39 AM
|
|
|
68 Replies
303 Views
(page loaded in 1.241 seconds)
|