strdup()

G

Grumble

As far as I can tell, strdup() is neither in C89 nor in C99.

Is that correct?

<OT>Is it in POSIX perhaps?</OT>
 
C

Chris Croughton

As far as I can tell, strdup() is neither in C89 nor in C99.

Is that correct?

Correct. It's trivial to implement, though:

#include <stdlib.h>
char *strdup(const char *str)
{
char *cpy = NULL;
if (str)
{
cpy = malloc(strlen(str)+1);
if (cpy)
strcpy(cpy, str);
}
return cpy;
}

(Actually, POSIX doesn't specify what happens if a null pointer is
passed in, but I prefer the trivial test so that strdup(NULL) == NULL.)
<OT>Is it in POSIX perhaps?</OT>

It is. Some useful URLs for POSIX:

http://www.opengroup.org/onlinepubs/009695399/nfindex.html
http://www.opengroup.org/onlinepubs/007908799/toc.htm
http://www.everything2.com/?node=Posix

Chris C
 
R

Robert Bachmann

Grumble said:
As far as I can tell, strdup() is neither in C89 nor in C99.

Is that correct?

<OT>Is it in POSIX perhaps?</OT>

Here are my replacements for strdup (specified in POSIX) and
strndup (an extension from GNU libc).

/*
dupstr() and dupstrn(): Replacements for strdup() and strndup()

Written in 2004 by Robert Bachmann <rbach [AT] rbach.priv.at>
Public domain.
*/

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

/*
char* dupstr (const char *s)

Returns a pointer to a new string
which is a duplicate of the string s,
or NULL if malloc() failed.
*/
char* dupstr (const char *s)
{
char *p;
size_t n;

assert(s != NULL);

n=strlen(s) + 1;
p = malloc(n);

if (p)
p=memcpy(p,s,n);

return p;
}

/*
char* dupnstr (const char *s,size_t n)

Works like dupstr() but it only copies the
first n characters.
A terminating '\0' is *always* added.
*/
char* dupnstr (const char *s,size_t n)
{
char *p;

assert(s != NULL);

p = malloc(n+1);

if (p) {
p=strncpy(p,s,n);
p[n]='\0';
}
return p;
}
 
T

Tor Rustad

char *p = dupstr(str);
if (p == NULL) {
perror("dupstr failed");
/* handle error */
}

If you really wish to keep the null pointer check, it is more useful to
set errno accordingly;

if (s == NULL) {
errno = EINVAL;
return NULL;
} else if ((p = malloc(n = strlen(s) + 1)) == NULL) {
return NULL;
}


Normally, a "mean and lean" attitude is sufficient. Why keep
on going -- if there isn't going to be done any recovery of
malloc failures anyway?

/* Warning: untested code follows ...*/
/* xmalloc: exit on failure */
void *xmalloc(size_t size)
{
void *mem;

/* pre-condition catch implementation defined behavoir */
assert (size > 0);

mem = malloc(size);
if (mem == NULL)
{
perror("xmalloc: Out of memory");
exit(EXIT_FAILURE);
}

return mem;
}

then our "strdup" can simply be written

/* xstrdup: exit on failure, never return NULL */
char *xstrdup(const char *str)
{
char *mem;

/* pre-condition catch UB */
assert(str != NULL);

mem = xmalloc(strlen(str) + 1);

/* post-condition */
assert(mem != NULL);

return strcpy(mem, str);
}

This simplify coding, since a lot if tests for NULL can be removed.
However, for a non-stop server, more advanced mem handling
is wanted, perhaps even using a GC.
 
G

Guillaume

Tor said:
Normally, a "mean and lean" attitude is sufficient. Why keep
on going -- if there isn't going to be done any recovery of
malloc failures anyway?

Oh my.
 
G

Gordon Burditt

Normally, a "mean and lean" attitude is sufficient. Why keep
on going -- if there isn't going to be done any recovery of
malloc failures anyway?

I hope you don't design airplanes, nuclear power plants,
weapons systems, or firmware for automobile engines :)

Gordon L. Burditt
 
N

Nils Weller

Normally, a "mean and lean" attitude is sufficient. Why keep
on going -- if there isn't going to be done any recovery of
malloc failures anyway?

The original poster explicitly mentioned that the code is intended to
act as a replacement for the UNIX function strdup(), and this function
is defined to return a null pointer if it failed to allocate any memory.

For the record: I agree that simply exiting is usually simpler than
trying to recover from an out-of-memory condition, and in fact I usually
use wrapper functions for malloc() and realloc() much like those you
posted.
 
T

Tor Rustad

Gordon Burditt said:
I hope you don't design airplanes, nuclear power plants,
weapons systems, or firmware for automobile engines :)

Have done safety-critical engineering for many years, still do. :)

Rule 118 (required):
"Dynamic heap memory allocation shall not be used"
Reference: MISRA C, April 1998

My point was, without any error-recovery logic elsewhere,
then it's simpler to die at the point-of-failure.
 
D

Default User

Tor said:
My point was, without any error-recovery logic elsewhere,
then it's simpler to die at the point-of-failure.


I disagree. This is intended to be a library function, so it should not
be killing the application. The fact that the code as currently written
doesn't have error-checking does not justify removing any possibility
of doing so.




Brian
 
M

Michael Wojcik

/*
char* dupnstr (const char *s,size_t n)

Works like dupstr() but it only copies the
first n characters.
A terminating '\0' is *always* added.
*/
char* dupnstr (const char *s,size_t n)
{
char *p;

assert(s != NULL);

p = malloc(n+1);

if (p) {
p=strncpy(p,s,n);
p[n]='\0';
}
return p;
}

IMO, this is infelicitous. If n > strlen(s), you're wasting memory
and cycles and possibly hurting locality of reference as well, due
to the unfortunate semantics of strncpy. If the intent of the
function is to always allocate n+1 bytes, fine, but that's not what
the description says.

If I were writing such a function I'd have it allocate the smaller
of n and strlen(s), plus 1.

Of course, this also falls afoul of Richard Heathfield's objection to
strncpy / strncat: it's only useful if you don't mind silently
truncating the copy. If you need to know whether truncation
occurred, you might as well just check the source length first. And
most well-behaved programs will need to know whether truncation
occurred (or would occur).

(On a similar note, I dislike assert; my functions do parameter
sanity checking, but they return errors if they fail. They don't
take the whole program down. And yes, I know this is a religious
issue for some and we've been over it more than once here.)

--
Michael Wojcik (e-mail address removed)

Pseudoscientific Nonsense Quote o' the Day:
From the scientific standpoint, until these energies are directly
sensed by the evolving perceptions of the individual, via the right
brain, inner-conscious, intuitive faculties, scientists will never
grasp the true workings of the universe's ubiquitous computer system.
-- Noel Huntley
 
C

CBFalconer

Nils said:
CBFalconer wrote:
.... snip ...

I would argue that your version is inferior because it masks the bug of
passing a null pointer to dupstr(), and there is no convenient way to
distinguish between this bug and failure to allocate memory. How is the
caller supposed to put your check to good use? Although the standard
does not say anything about errno being set to an appropriate error code
when malloc() fails, the ``Quality of Implementation'' factor is usually
such that malloc() does this. Therefore, callers of this function may
reasonably wish to rely on a meaningful errno value in order to diagnose
the error as ``dupstr() failed becaue of ...'' rather than ``dupstr()
failed for an unknown reason'':

Ah, but I document my dupstr/strdup as accepting NULL and returning
NULL for NULL. :)

If the user really wants to guard against original NULLs he should
be writing (regardless of the dupstr version):

if (!s1) {
/* We have an original NULL case */
}
else if (!(s2 = dupstr(s1))) {
/* we have detected mem failure */;
}
else {
/* carry on Jack */
}

My attitude means that one can make a deep copy of a string as long
as the pointer content is valid. I can see more uses for that than
for crashing on NULL. For example:

char *table[SOMESZ];
char *auxtbl[SOMESZ];
size_t i;

....
/* code filling table with pointers or NULLs everywhere */
....
/* deep copy of table needed, high confidence in malloc */
for (i = 0; i < SOMESZ; i++) auxtbl = dupstr(table);
 
T

Tor Rustad

Default User said:
I disagree. This is intended to be a library function, so it should not
be killing the application. The fact that the code as currently written
doesn't have error-checking does not justify removing any possibility
of doing so.

Well, nothing is stopping you from calling malloc(), I argue only that
xmalloc() is a better alternative in many cases, but not for _all_.

However, a better design for a library error handling, is to provide
a mechanism for a callback function, than to return NULL pointers
and to have if-checks all over the place.
 
R

Robert Bachmann

Michael said:
IMO, this is infelicitous. If n > strlen(s), you're wasting memory
and cycles [...]
If the intent of the
function is to always allocate n+1 bytes, fine, but that's not what
the description says.

I agree my dupnstr could be a big memory waster if the strings passed
to it are shorter than n, but my description of dupnstr never said that
dupnstr will only allocate n+1 bytes.

However, here are two alternative implementations of dupnstr.

char* alt1_dupnstr (const char *s, size_t n)
{
char *p;
const char *org;
size_t len=0;

assert(s != NULL);

if (n==0)
return calloc(1,1);

org=s;
while(n-- && *s++)
++len;

p=malloc(len+1);

if (p) {
(void) memcpy(p,org,len);
p[len]='\0';
}

return p;
}

char* alt2_dupnstr (const char *s,size_t n)
{
char *p;
size_t len=0;

assert(s != NULL);

if (n==0)
return calloc(1,1);

p=malloc(n+1);

if (p)
{
char *op=p;

while(n && (*p++ = *s++) )
{
++len;
--n;
}

if (n==0)
{
*p='\0';
p=op;
}
else
{
p = realloc(op,len+1);
if (p == NULL)
free(op);
}
}

return p;
}
Of course, this also falls afoul of Richard Heathfield's objection to
strncpy / strncat: it's only useful if you don't mind silently
truncating the copy. If you need to know whether truncation
occurred, you might as well just check the source length first. And
most well-behaved programs will need to know whether truncation
occurred (or would occur).
I'm not the inventor of strndup, I only tried to provide a portable
PD alternative.
 
R

Richard Bos

Tor Rustad said:
Have done safety-critical engineering for many years, still do. :)

Rule 118 (required):
"Dynamic heap memory allocation shall not be used"
Reference: MISRA C, April 1998

Whoever wrote this rule does not understand the perversity of human
nature. What's to stop me from declaring a humungous static array, and
getting my dynamic memory from it? Well-checked dynamic allocation is
perfectly safe; this rule should read more like "All dynamic memory
allocation shall be checked, and appropriate measures taken on failure".
Note also the absence of the word "heap".
My point was, without any error-recovery logic elsewhere,
then it's simpler to die at the point-of-failure.

That's not an argument for dying in a malloc() wrapper, that's an
argument for more error recovery elsewhere.

Richard
 
R

Richard Harter

Whoever wrote this rule does not understand the perversity of human
nature. What's to stop me from declaring a humungous static array, and
getting my dynamic memory from it? Well-checked dynamic allocation is
perfectly safe; this rule should read more like "All dynamic memory
allocation shall be checked, and appropriate measures taken on failure".
Note also the absence of the word "heap".

Your point about perversity is well taken, but your replacement is
not. To preserve the intent the text should be "Dynamic memory
allocation shall not be used." Putting in the word "heap" allows a
bit of weaseling. The general issue is that in safety-critical
software resource requirements should be determinable in advance.
That's not an argument for dying in a malloc() wrapper, that's an
argument for more error recovery elsewhere.

Richard

Richard Harter, (e-mail address removed)
http://home.tiac.net/~cri, http://www.varinoma.com
All my life I wanted to be someone;
I guess I should have been more specific.
 
C

Chris Croughton

Whoever wrote this rule does not understand the perversity of human
nature. What's to stop me from declaring a humungous static array, and
getting my dynamic memory from it?

Nothing, whatever you do with your memory is your problem. If you
choose to write buggy code you may well get fired, of course...

The point of that rule -- and although I've never worked under MISRA
rules I've worked on a number of projects with something similar -- is
to ensure that nothing your code does with memory will affect any other
code inadvertently (for instance by causing it to run out of memory).
The total memory requirements of the system are known at build time, and
include things like tasks/threads created statically and stacks with
maximum sizes.
Well-checked dynamic allocation is
perfectly safe; this rule should read more like "All dynamic memory
allocation shall be checked, and appropriate measures taken on failure".
Note also the absence of the word "heap".

If it does run out unexpectedly then very often in a real-time system
nothing can be done except die (and in a safety-critical system that
'die' may well be literal on the part of the user), it can't wait around
to see if memory gets freed later. Hence "no dynamic memory", every
task knows exactly how much memory is available to it.
That's not an argument for dying in a malloc() wrapper, that's an
argument for more error recovery elsewhere.

Like what? If I've allocated the memory I need, and then need some more
and it isn't there, what am I supposed to do? I can't assume that some
more might become available if I wait, that could lock it up for ever.
Dying in a wrapper (which can of course call cleanup to close files
etc., although that could fail as well if we're out of memory, and can
display or log the information) is often the only sensible thing to do.
Why do the same 'recovery' all over the place when it can be done in one
place, and that place can be easily altered to do things like logging
the fault, or providing a place to put a debugging breakpoint.

Chris C
 
J

Joe Wright

Peter said:
Groovy hepcat Chris Croughton was jivin' on Tue, 22 Mar 2005 11:55:03
+0000 in comp.lang.c.
Re: strdup()'s a cool scene! Dig it!




Yes, if you want to have nasal demons for tea. Identifiers with
external linkage beginning with str and a lower case letter are
reserved.

Oh Peter, you're such an old woman. Surely defining strdup() will get
you in trouble only if it already exists, in which case you don't need
to define it yourself. :)
 
M

Mac

Oh Peter, you're such an old woman. Surely defining strdup() will get
you in trouble only if it already exists, in which case you don't need
to define it yourself. :)

Unless the already existing version uses a different interface, which it
could, since it is not a standard function.

--Mac
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top