LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 02-10-2010, 12:00 PM   #1
neioo
LQ Newbie
 
Registered: Jan 2008
Location: Sant Quinti de Mediona
Distribution: Gentoo
Posts: 25

Rep: Reputation: 3
variadic strcat in C


I think that it is a pity to manage string programming in C. So, I though to create a variadic function (like printf) but it will not print the result on screen but will malloc an string containing all the string passed in parameters.

But because it is be habitual to concatenate more than two strings, I think that someone has already develop this function. Does somebody know if the function I'm looking for already exists in a library?
 
Old 02-10-2010, 12:48 PM   #2
smeezekitty
Senior Member
 
Registered: Sep 2009
Location: Washington U.S.
Distribution: M$ Windows / Debian / Ubuntu / DSL / many others
Posts: 2,339

Rep: Reputation: 231Reputation: 231Reputation: 231
sprintf()?
sprintf(output, "%s%s", "hello", " world");
 
Old 02-10-2010, 03:49 PM   #3
ForzaItalia2006
Member
 
Registered: Dec 2009
Location: Walldorf, Germany
Distribution: (X)Ubuntu, Arch, Gentoo
Posts: 205

Rep: Reputation: 67
Quote:
Originally Posted by smeezekitty View Post
sprintf()?
sprintf(output, "%s%s", "hello", " world");
Yes, that's possible, but you need to allocate a sufficient amount of memory before calling sprintf. Or you use snprintf to limit the output to n characters.

Alternatively, you could use the function asprintf which is - as far as I know - not part of the C99 standard, but a GNU extension. asprintf allocates the memory. You don't need to care about memory sizes of your final string. All you need to do after asprintf to call free when you're finished.

- Andi -
 
Old 02-10-2010, 04:59 PM   #4
neioo
LQ Newbie
 
Registered: Jan 2008
Location: Sant Quinti de Mediona
Distribution: Gentoo
Posts: 25

Original Poster
Rep: Reputation: 3
Hello,

Thanks for your reply, I have test sprintf, and I have programmed that:
Quote:
#include <stdio.h>
#include <stdlib.h>

int
main ()
{
char * result;
char a[] = "hello";
char b[] = "world";

result = malloc (100);
sprintf (result, "%s %s", a, b);

puts (result);
return 0;
}
But there are some points that I dislike: you must allocate the memory space of the resulted string, I don't want to count how many bytes I need to malloc.
And if you concatenate a NULL pointer it prints "(null)", I would like to print nothing when a NULL pointer is found.

So, I programmed this other code, which solve my disliked points:

Quote:
#include <stdarg.h>
#include <stdio.h>

string
my_variadic_strcat (int num, ...)
{
va_list ap;
int i, size;
char * result;
char * list[num];
va_start (ap, num); // Initialize the argument list.

size = 0;
for (i=0; i<num; i++)
{
list[i] = va_arg (ap, string); // Get the next argument value.
if (list[i] != NULL)
{
size = size + strlen (list[i]);
}
}
//memory allocate required space:
result = malloc (size+1);
result[0] = '\0';

for (i=0; i<num; i++)
{
if (list[i] != NULL)
{
result = strcat (result, list[i]);
}
}
va_end (ap); //* Clean up.
return result;
}

int
main()
{
char * result;
char a[] = "hello";
char b[] = "world";

result = my_variadic_strcat (3, a, " ", b);

puts(result);
}
But again, I have a problem, I don't want to count how many strings I put on the parameters... Because when you are changing your code, you can easily forgot to update the number of strings. So, I would like to enter the parameters in that way:
my_variadic_strcat2 (a, " ", b, NULL);
But because I want to accept NULL pointer (I said that if I concatenate a null pointer no action is needed), I need a null pointer different of NULL (0), so my question is reduced to this one:

Which memory address except NULL is sure to get a segmentation fault when accessed? I though with "-1" (UINT_MAX) or just "1". But I'm not sure if there is any guarantee that this values can become correct memory positions. I'm not sure because some computers take address of 32 bit and some other of 64 bit and I would like to make it independent of the architecture (I ask so much I know u_u).
 
Old 02-10-2010, 08:05 PM   #5
graemef
Senior Member
 
Registered: Nov 2005
Location: Hanoi
Distribution: Fedora 13, Ubuntu 10.04
Posts: 2,379

Rep: Reputation: 148Reputation: 148
You could add a stop argument, which is a string passed in as the first argument and last argument. That means that you will then loop through until you find an argument with the same stop value. Obviously, you only need to do this on the first loop; for the second loop you will know how many arguments you have and so you can just do a simple count.
[code]result1 = your_variadic_strcat("#",a," ",b,NULL,"#");
result2 = your_variadic_strcat("#stop",a,"#",b,NULL,"#stop");
[code]

As an aside you want to check that you call to malloc() worked.
 
Old 02-10-2010, 08:27 PM   #6
jlinkels
LQ Guru
 
Registered: Oct 2003
Location: Bonaire, Leeuwarden
Distribution: Debian /Jessie/Stretch/Sid, Linux Mint DE
Posts: 5,195

Rep: Reputation: 1043Reputation: 1043Reputation: 1043Reputation: 1043Reputation: 1043Reputation: 1043Reputation: 1043Reputation: 1043
There is a function which allocates memory as needed, but I forgot which one. fsprintf() maybe? If not, check the libc manual.

jlinkels
 
Old 02-10-2010, 09:07 PM   #7
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
Quote:
Originally Posted by jlinkels View Post
There is a function which allocates memory as needed, but I forgot which one. fsprintf() maybe? If not, check the libc manual.

jlinkels
man asprintf
.
 
Old 02-11-2010, 05:41 AM   #8
ForzaItalia2006
Member
 
Registered: Dec 2009
Location: Walldorf, Germany
Distribution: (X)Ubuntu, Arch, Gentoo
Posts: 205

Rep: Reputation: 67
Quote:
Originally Posted by neioo View Post
Hello,
But there are some points that I dislike: you must allocate the memory space of the resulted string, I don't want to count how many bytes I need to malloc.
You should have read my post about asprintf ...
 
Old 02-11-2010, 09:29 AM   #9
neioo
LQ Newbie
 
Registered: Jan 2008
Location: Sant Quinti de Mediona
Distribution: Gentoo
Posts: 25

Original Poster
Rep: Reputation: 3
Hi again,

Thanks so much for your replying, I have develop a modified graemef's description:
Quote:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

char *
variadic_strcat2 (char * delimiter, ...)
{
va_list ap;
int i, size, counter;
char * result;
char * list[10];

va_start (ap, delimiter); // Initialize the argument list.

//result size
size = 0;
counter = 0;

list[counter] = va_arg (ap, char *); // Get the next argument value.
while (list[counter] != delimiter)
{
if (list[counter] != NULL)
{
size = size + strlen(list[counter]);
}
counter ++;
list[counter] = va_arg (ap, char *); // Get the next argument value.
}

result = malloc (size+1);
result[0] = '\0';
for (i=0; i<counter; i++)
{
if (list[i] != NULL)
{
result = strcat (result, list[i]);
}
}
va_end (ap); //* Clean up.
return result;
}

int
main ()
{
char * a;
char b[] = "Hello";
char c[] = "World";
char d[] = "!";

char * delimiter = (char *) -1;

a = variadic_strcat2 (delimiter, b, " ", c, d, delimiter);
puts (a);

return 0;
}
Please note that graemef suggests to put an string as delimiter, but now I have make that to pass a pointer, so you cannot call:
a = variadic_strcat2("#", a, " ", b, NULL, "#");
But, easily you can call:
a = variadic_strcat2(27, a, " ", b, NULL, 27);
And it will work (only one warning will be showed if -Wall flag is set when compiling).

But (allways with "but"!), it has a problem: it's limited by the size of the array "list" (10 in the example). To solve that I could use a linked-list to insert all returned strings by "va_arg", but I though it could be more eficient if I increase the size on the stack (sub esp, 4) every time "va_arg" give me an string, and then point the list to the first string returned. To do that I though I could re-declare a variable in a while an in each iteration the variable should be declared (losing the access of old declared variable with the same name):
Quote:
...
while (strcmp (iter, delimiter) != 0)
{
char * iter;
iter = va_arg(ap, char *); // Get the next argument value.
size = size + strlen(list[counter]);
counter ++;
}
...
Where iter is always a new declared variable, and not the same (put consecutively on the stack (esp register)). But it does not work like I hoped and variable is only declared the first time. Is there a way to do that?

Well... I know it seems over-elaborate, but you must understand that I would like to use this function everywhere I concatenate strings, so I need to verify there is no restriction on that function.

Last edited by neioo; 02-11-2010 at 10:37 AM.
 
Old 02-11-2010, 10:35 AM   #10
neioo
LQ Newbie
 
Registered: Jan 2008
Location: Sant Quinti de Mediona
Distribution: Gentoo
Posts: 25

Original Poster
Rep: Reputation: 3
Quote:
Originally Posted by ForzaItalia2006 View Post
You should have read my post about asprintf ...
Yes, I have read your post, thank you, but I read it after publishing my second post, and because asprintf prints (null) when a NULL pointer is passed I said nothing.

I think asprintf is a good function to be used as itoa. And if my concatenate function is getting complex or slow I will use asprintf.
 
Old 02-11-2010, 01:14 PM   #11
smeezekitty
Senior Member
 
Registered: Sep 2009
Location: Washington U.S.
Distribution: M$ Windows / Debian / Ubuntu / DSL / many others
Posts: 2,339

Rep: Reputation: 231Reputation: 231Reputation: 231
Be very afraid of asprintf()...you are going out of standard.
 
Old 02-11-2010, 04:48 PM   #12
neioo
LQ Newbie
 
Registered: Jan 2008
Location: Sant Quinti de Mediona
Distribution: Gentoo
Posts: 25

Original Poster
Rep: Reputation: 3
Quote:
Originally Posted by smeezekitty View Post
Be very afraid of asprintf()...you are going out of standard.
You are right, if someone wants his code to be portable, must trust with standard. I will keep it in mind.

I have decided to solve the limited array with a linked list.

Thanks to everyone to clarify my ideas
 
Old 02-11-2010, 07:18 PM   #13
graemef
Senior Member
 
Registered: Nov 2005
Location: Hanoi
Distribution: Fedora 13, Ubuntu 10.04
Posts: 2,379

Rep: Reputation: 148Reputation: 148
I don't understand why you are putting the arguments into an array (or linked list). You should be able to parse the parameter list twice, just call va_end() followed by a va_start() to initialise the list for a second run through.
 
Old 02-12-2010, 03:20 AM   #14
neioo
LQ Newbie
 
Registered: Jan 2008
Location: Sant Quinti de Mediona
Distribution: Gentoo
Posts: 25

Original Poster
Rep: Reputation: 3
Quote:
Originally Posted by graemef View Post
I don't understand why you are putting the arguments into an array (or linked list). You should be able to parse the parameter list twice, just call va_end() followed by a va_start() to initialise the list for a second run through.
Oh! I didn't notice that! Really, thank you so much!

My final code:
Quote:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

/*
PRE:
Input parameters must finish with delimiter.
All input parameters must be char *.
POST:
returned string is a concatenation of all strings between delimiter and the next occurrence of delimiter.
Null strings between delimiters will be ignored.
If all strings between delimiters are Null pointers an empty string will be returned.
*/
char *
variadic_strcat (char * delimiter, ...)
{
va_list ap;
int size, count, i;
char * result, * iter;

va_start (ap, delimiter); // Initialize the argument list.

//number of strings to concatenate
count = 0;
//result length
size = 0;

iter = va_arg (ap, char *); // Get the next argument value.
while (iter != delimiter)
{
if (iter != NULL)
{
size = size + strlen(iter);
}
count ++;
iter = va_arg (ap, char *); // Get the next argument value.
}

va_end(ap);
va_start (ap, delimiter);

result = malloc (size+1);
result[0] = '\0';
for (i=0; i<count; i++)
{
iter = va_arg (ap, char *); // Get the next argument value.
if (iter != NULL)
{
result = strcat (result, iter);
}
}

va_end (ap); //* Clean up.
return result;
}
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
warning: ISO C does not permit named variadic macros knobby67 Programming 3 12-16-2009 03:17 AM
How to strcat ? baig Linux - Newbie 4 12-01-2008 04:42 AM
Question regarding variadic functions rajesh_b Programming 7 07-08-2007 09:46 PM
Variadic functions Config Programming 6 03-03-2004 11:05 AM
RH 8.0 strcat problem shibdas Programming 1 07-02-2003 01:00 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:31 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration