Help with Variable-Length Argument Lists

J

Jonathan Burd

Greetings everyone,

I wrote a function to learn about variable-length argument lists. I
wonder if there is a better way to detect the end of the argument list
than using a sentinel value like the one I am using (NULL in this
example) or an argument count parameter (ugh).

The following function concatenates a series of C-style strings. I am
aware of the fact that not allocating enough memory for `dst` results
in UB (gave me a segmentation fault).

#include <stdarg.h>

char *
append (
char * dst, // destination string buffer
const char * base, // first string
const char * append, // second string
/* const char * appN */ ... // n strings
/* , NULL */ // sentinel null indicating list end
)
{
va_list ap;
char * eosdst = dst;
const char * cp = NULL;

/* copy base to dst */
while ((*eosdst = *base))
++eosdst, ++base;

/* copy append to dst */
while ((*eosdst = *append))
++eosdst, ++append;

/* copy remaining strings if they exist. NULL -- terminate */
va_start (ap, append);
while (NULL != (cp = va_arg (ap, const char*)))
while ((*eosdst = *cp))
++eosdst, ++cp;
va_end (ap);

return (dst);
}

Sample Usage:
#include <stdio.h>

/* ... */

int
main (int argc, char ** pp_argv)
{
char buf[200];
char * tst = "test";
char * teststring2 = "This is some more text.\nSee if this works
well.";

append (buf, "--d--", teststring2, teststring2, tst, "\n", NULL);
printf (buf);

return (0);
}

Any help will be appreciated. If you have suggestions, please feel free
to comment so I can improve. Thank you.

Regards,
JB
 
E

Eric Sosman

Jonathan said:
Greetings everyone,

I wrote a function to learn about variable-length argument lists. I
wonder if there is a better way to detect the end of the argument list
than using a sentinel value like the one I am using (NULL in this
example) or an argument count parameter (ugh).

The fundamental requirement is that you need to know
whether a "next argument" exists (and what type it is)
before attempting to retrieve it with va_arg(). You can
use any imaginable computation to come up with this yes/no
result. However, the <stdarg.h> mechanism itself doesn't
provide any information you can use as an input for the
computation; you must compute yes/no from other sources.

Two popular mechanisms are to use a sentinel value or
to use an argument count (which could, of course, actually
count pairs of arguments, or triples, or some other grouping).
A less popular mechanism is to imitate the printf() family,
where one of the "fixed" arguments provides information
about the number and types of the variable arguments. But
these three are by no means an exhaustive catalog; anything
at all will work if it can produce the answer you need. The
trade-offs are usually the mechanism's complexity (simpler
is better, other things being equal) and its vulnerability
to misuse (the compiler can't do a lot of checking on the
"..." arguments).
The following function concatenates a series of C-style strings. I am
aware of the fact that not allocating enough memory for `dst` results
in UB (gave me a segmentation fault).

char *
append (
char * dst, // destination string buffer
const char * base, // first string
const char * append, // second string
/* const char * appN */ ... // n strings
/* , NULL */ // sentinel null indicating list end
)
[...]

Sample Usage:
#include <stdio.h>

/* ... */

append (buf, "--d--", teststring2, teststring2, tst, "\n", NULL);

Here's an example of the "vulnerability to misuse" mentioned
above: write `(char*)NULL' instead of just `NULL'. Question 5.2
in the comp.lang.c Frequently Asked Questions (FAQ) list

http://www.eskimo.com/~scs/C-faq/top.html

explains why.
 
C

CBFalconer

Jonathan said:
I wrote a function to learn about variable-length argument lists.
I wonder if there is a better way to detect the end of the argument
list than using a sentinel value like the one I am using (NULL in
this example) or an argument count parameter (ugh).

The following function concatenates a series of C-style strings. I
am aware of the fact that not allocating enough memory for `dst`
results in UB (gave me a segmentation fault).

#include <stdarg.h>

char *
append (
char * dst, // destination string buffer
const char * base, // first string
const char * append, // second string
/* const char * appN */ ... // n strings
/* , NULL */ // sentinel null indicating list end
)
{
va_list ap;
char * eosdst = dst;
const char * cp = NULL;

/* copy base to dst */
while ((*eosdst = *base))
++eosdst, ++base;

/* copy append to dst */
while ((*eosdst = *append))
++eosdst, ++append;

/* copy remaining strings if they exist. NULL -- terminate */
va_start (ap, append);
while (NULL != (cp = va_arg (ap, const char*)))
while ((*eosdst = *cp))
++eosdst, ++cp;
va_end (ap);

return (dst);
}

Sample Usage:
#include <stdio.h>

/* ... */

int
main (int argc, char ** pp_argv)
{
char buf[200];
char * tst = "test";
char * teststring2 = "This is some more text.\nSee if this works
well.";

append (buf, "--d--", teststring2, teststring2, tst, "\n", NULL);
printf (buf);

return (0);
}

Any help will be appreciated. If you have suggestions, please feel free
to comment so I can improve. Thank you.

You should properly indent your code, using spaces, not tabs. At
any rate variadic functions are invitations to misuse and are error
prone. You should avoid them. Especially in this case, since the
standard sprintf() routine is already available to perform the same
job, is more flexible, uses familiar parameters, and is already
available. With C99 I believe snprintf() is much safer.
 
J

Jonathan Burd

Actually, it's Google's posting mechanism. It formatted the code
automatically. I use the 2-space indentation style.

Regards,
JB
 
K

Kenny McCormack

Especially in this case, since the
standard sprintf() routine is already available to perform the same
job, is more flexible, uses familiar parameters, and is already
available. With C99 I believe snprintf() is much safer.

I wonder. Is it ever possible in this NG to post something like "I am
trying to solve <newProblemThatHasNeverBeenSolvedBefore>, using technique
<A>, but since newProblemThatHasNeverBeenSolvedBefore is complicated and
possibly proprietary, I'd prefer not to explain
newProblemThatHasNeverBeenSolvedBefore. So, I'm going to discuss the
problems I am having with technique <A>, but I'm going to use this made up
example", and not get a response that says "'made-up-example' has already
been solved; why are you trying to reinvent the wheel?" ?
 
K

Keith Thompson

I wonder. Is it ever possible in this NG to post something like "I am
trying to solve <newProblemThatHasNeverBeenSolvedBefore>, using technique
<A>, but since newProblemThatHasNeverBeenSolvedBefore is complicated and
possibly proprietary, I'd prefer not to explain
newProblemThatHasNeverBeenSolvedBefore. So, I'm going to discuss the
problems I am having with technique <A>, but I'm going to use this made up
example", and not get a response that says "'made-up-example' has already
been solved; why are you trying to reinvent the wheel?" ?

Probably, but I see no sign of any
newProblemThatHasNeverBeenSolvedBefore in this thread.
 

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,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top