malloc’ing strcat

Alexandre François Garreau galex-713@galex-713.eu
Tue Feb 25 12:48:00 GMT 2020


Le mardi 25 février 2020, 10:41:30 CET Florian Weimer a écrit :
> * Alexandre François Garreau:
> >> I think I wouldn't use the NULL sentinel for the reasons indicated
> >> (silent truncation after memory allocation or other failure, even
> >> under
> >> valgrind), and use
> >> 
> >>   sizeof ((const char *[]) { __VA_ARGS__ }) / sizeof (const char *)
> >> 
> >> to compute the number of arguments, and pass that separately from the
> >> pointer.
> > 
> > you mean like that?
> > #define anstrcat(...) \
> > astrncat(sizeof((char *[]) {__VA_ARGS__}), \
> > 
> >                 (char *[]) {__VA_ARGS__})
> > 
> > char * anstrcat (size_t n; char *strarray[])
> > {
> > 
> >    char * result = malloc(1);
> >    size_t len = 1;
> >    result[0] = '\0';
> >    for (off_t i = 0; i < n; ++i)
> >    result = strcat(realloc(result, len += strlen(strarray[i])),
> > 
> > strarray[i]);
> > 
> >    return result;
> > 
> > }
> 
> Sort of, except with overflow checking

I’m curious: how would it overflow if strlen already checks the end (the 
\0) of each string (so that it knows how much strcat will copy)?

> and without the quadratic
> behavior.

Why is it quadratic? with a single loop… are you talking about calling 
several times realloc, or strlen?

Or simply about using strcpy or stpcpy (or memcpy or mempcpy so not to 
check for \0-position a second time) instead because you could get the 
NULL-char*position and hence the end of a string only once?

char * anstrcat (size_t n; char *strarray[])
{
  char *result = NULL, *pos = result;
  size_t total_len = 1;
  for (off_t i = 0; i < n; ++i)
    {
      size_t len = strlen (strarray[i]);
      result = realloc (result, total_len += len);
      pos = mempcpy (pos, strarray[i], len);
    }
  result[total_len-1] = '\0';
  return result;
}

I don’t see how to do less than n strlen (except by asking to provide an 
array of lengths but to me it’s overkill and not really C-ish to me… we 
need each string’s size…) , nor how to call malloc only once without first 
doing twice as much strlens… except by storing them in an VLA array (or 
mallocated, for compatibility sake) but isn’t also that overkill? well, 
“memory’s almost free”… Is calling realloc n times a bad thing?

> > Yet this only works with a macro, and is it really unreasonable to
> > expect not to give NULL args?
> 
> I think so.

For the sake of not doing every single time the same malloc’s oneself, I 
believe a such function would be too useful for a such requirement to 
hinder its benefits…

Also I like simple interfaces, and an interface that doesn’t require to 
count the arguments (the same way you don’t have to always provide the 
size of a string) looks cool to me… because I wonder about the cases 
people will have to use it without the macro… 

I mean such usage looks so basic and likely what everybody would do since 
for much time that it ought to get a general interface so to factorize a 
lot…

> But there is another complication: The NULL sentinel is not portable
> because NULL can be defined as 0, which does not have the correct type
> for use in an argument list of character pointers.

And would (char*)NULL work as a replacement? otherwise how one is to 
specify a null pointer, a null object, etc. in a portable manner?



More information about the Libc-help mailing list