passing arrays of strings

C

Colin Doig

Hi, I need to pass an array of strings to a function, but I can't get it
to work. This is what I wrote :

#include <stdio.h>

void a_function(char *blah[]) {

printf("%s %s %s", blah[0], blah[1], blah[2]);

}


main() {

char *blah[3];

strcpy(blah[0], "This");
strcpy(blah[1], "doesn't");
strcpy(blah[2], "work");

a_function(blah);

}

Note that I do not want to use

char *blah[3] = {"This", "doesn't", "work"};

because I need to create the strings on the fly.
Thanks very much
Colin.
colin _ doig AT hotmail DOT com
 
P

pete

Colin said:
Hi, I need to pass an array of strings to a function, but I can't get it
to work. This is what I wrote :

#include <stdio.h>

void a_function(char *blah[]) {

printf("%s %s %s", blah[0], blah[1], blah[2]);

}

main() {

char *blah[3];

strcpy(blah[0], "This");
strcpy(blah[1], "doesn't");
strcpy(blah[2], "work");

a_function(blah);

}

Note that I do not want to use

char *blah[3] = {"This", "doesn't", "work"};

because I need to create the strings on the fly.

/* BEGIN blah.c */

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

void a_function(char (*blah)[sizeof "doesn't"])
{
printf("%s %s %s\n", blah[0], blah[1], blah[2]);
}

int main(void)
{
char (*blah)[sizeof "doesn't"];

blah = malloc(3 * sizeof *blah);
if (blah) {
strcpy(blah[0], "This");
strcpy(blah[1], "doesn't");
strcpy(blah[2], "work");
a_function(blah);
free(blah);
}
return 0;
}

/* END blah.c */
 
A

Artie Gold

Colin said:
Hi, I need to pass an array of strings to a function, but I can't get it
to work. This is what I wrote :

#include <stdio.h>

void a_function(char *blah[]) {

printf("%s %s %s", blah[0], blah[1], blah[2]);

}


main() {

char *blah[3];

This is an array of 3 pointers to char. None of the pointers has any
allocated memory assciated with it...
strcpy(blah[0], "This");

....so this...
strcpy(blah[1], "doesn't");

....and this...
strcpy(blah[2], "work");

...and this...invokes undefined behavior.
a_function(blah);

}

Note that I do not want to use

char *blah[3] = {"This", "doesn't", "work"};

because I need to create the strings on the fly.

It depends what you mean by "on the fly". If you are, say, choosing your
strings from a collection of possibilites, setting those pointers to a
string literal (i.e. `blah[2] = "a possibilty";') is legitimate;
otherwise you'll have to allocate enough memory to each member of `blah'
to contain whatever you want to put in it (with one extra for the
trailing NUL).

HTH,
--ag
 
J

Jack Klein

Hi, I need to pass an array of strings to a function, but I can't get it
to work. This is what I wrote :

#include <stdio.h>

void a_function(char *blah[]) {

printf("%s %s %s", blah[0], blah[1], blah[2]);

}


main() {

Implicit int is finally illegal under the current C standard. Best
make this:

int main()

....or even better:

int main(void)
char *blah[3];

This defines an array of three pointers to char. They are not
initialized and do not point to anything useful.

You could change this to:

char blah[3][25]; /* or some large enough number */
strcpy(blah[0], "This");

Calling strcpy() without having included <string.h> or otherwise
having a prototype in scope is undefined behavior. Trying to write to
memory through an uninitialized pointer is undefined behavior.
strcpy(blah[1], "doesn't");
strcpy(blah[2], "work");

a_function(blah);

Even without the implicit int return type on main(), you told the
compiler that your main() would return an int, then you lied and did
not return anything. Add:

return 0;
}

Note that I do not want to use

char *blah[3] = {"This", "doesn't", "work"};

because I need to create the strings on the fly.
Thanks very much
Colin.
colin _ doig AT hotmail DOT com

If there are a large number of strings and their length can vary
greatly, you could dynamically allocate the memory for each string
with malloc().
 
A

Arthur J. O'Dwyer

Colin said:
Hi, I need to pass an array of strings to a function, but I can't get it
to work. This is what I wrote :

void a_function(char *blah[]) {
printf("%s %s %s", blah[0], blah[1], blah[2]);
}
char *blah[3];

[and then pete wrote...]
void a_function(char (*blah)[sizeof "doesn't"])
{
printf("%s %s %s\n", blah[0], blah[1], blah[2]);
}

char (*blah)[sizeof "doesn't"];

Ye gods! Please, don't frighten the questioners! :) The OP
should note carefully that this code does *NOT* follow the same
design as the other couple of responses you've gotten, and I
think there's a reason for that. This way is bad. :)
Essentially, with two dimensions in your "array" of strings,
you have four different ways to design your data structure.
You can fix both dimensions:

char blah[3][sizeof "the longest string you'll encounter"];
strcpy(blah[0], "This");
strcpy(blah[1], "will");
strcpy(blah[2], "work");

You can fix the first dimension and let the second vary, as
others have suggested:

char *blah[3];
if ((blah[0] = malloc(sizeof "This")) != NULL);
strcpy(blah[0], "This");
if ((blah[1] = malloc(sizeof "will")) != NULL);
strcpy(blah[1], "will");
if ((blah[2] = malloc(sizeof "work")) != NULL);
strcpy(blah[2], "work");

You can vary the first dimension and fix the second, which
is what pete suggested, and will allow you to realloc() the
array as needed where the first two ways won't:

char (*blah)[sizeof "the longest string you'll encounter"];
blah = malloc(3 * sizeof *blah);
strcpy(blah[0], "This");
strcpy(blah[1], "will");
strcpy(blah[2], "work");

And finally you can vary both dimensions, which is IMHO often
the most extensible and useful solution, but does require more
bookkeeping. This is the structure with which 'argv' is defined
to work, by the way:

char **blah;
blah = malloc(3 * sizeof *blah);
if ((blah[0] = malloc(sizeof "This")) != NULL);
strcpy(blah[0], "This");
if ((blah[1] = malloc(sizeof "will")) != NULL);
strcpy(blah[1], "will");
if ((blah[2] = malloc(sizeof "work")) != NULL);
strcpy(blah[2], "work");

So there you have it. Four ways to do one simple thing, and
it's up to you to pick which one is right for this particular
problem you're solving.

HTH,
-Arthur
 
T

Tobin Fricke

Colin said:
main() {

char *blah[3];

strcpy(blah[0], "This");
strcpy(blah[1], "doesn't");
strcpy(blah[2], "work");

That doesn't work, because you haven't allocated memory in blah to hold your
strings. Your code probably results in a segmentation violation --- that's
your clue that you're writing into memory improperly! The strcpy function just
copies characters from one location in memory to another, and in this case you
haven't allocated space at the destination.

One way to fix this is to allocate the memory manually. This will allow blah[0]
to store a string up to 1023 characters long:

blah[0] = (char *)malloc(1024);
strcpy(blah[0], "This");

Something more along the lines of what you wrote originally is to use "strdup",
which allocates enough memory to hold a copy of a string, and then copies the
string:

blah[0] = strdup("This"); // five bytes are allocated

That is equivalent to this:

blah[0] = (char *)malloc(strlen("this")+1);
strcpy(blah[0],"this");

HTH

Tobin
 
T

Tom St Denis

Arthur J. O'Dwyer said:
You can fix the first dimension and let the second vary, as
others have suggested:

char *blah[3];
if ((blah[0] = malloc(sizeof "This")) != NULL);
strcpy(blah[0], "This");
if ((blah[1] = malloc(sizeof "will")) != NULL);
strcpy(blah[1], "will");
if ((blah[2] = malloc(sizeof "work")) != NULL);
strcpy(blah[2], "work");

Are those trailing semi-colons really valid? ;-)

Tom
 
A

Arthur J. O'Dwyer

Arthur J. O'Dwyer said:
if ((blah[0] = malloc(sizeof "This")) != NULL);
strcpy(blah[0], "This");
if ((blah[1] = malloc(sizeof "will")) != NULL);
strcpy(blah[1], "will");

Are those trailing semi-colons really valid? ;-)

Sure... who needs to do any complicated "branching", as long
as you've *tested* for NULL, right? :p

Whoops!

-Arthur
 
R

Richard Heathfield

Tobin said:
Colin said:
main() {

char *blah[3];

strcpy(blah[0], "This");
strcpy(blah[1], "doesn't");
strcpy(blah[2], "work");

That doesn't work, because you haven't allocated memory in blah
to hold your strings.

Correct.

One way to fix this is to allocate the memory manually. This will allow
blah[0] to store a string up to 1023 characters long:

blah[0] = (char *)malloc(1024);

The cast is unnecessary (and can hide the bug of failing to #include
<stdlib.h>), but you really ought to test that the allocation succeeded
before you use it...

if(blah[0] != NULL)
{
strcpy(blah[0], "This");

If you're only using five bytes, why allocate 1024? After all, if you need
more later, you can always realloc.
Something more along the lines of what you wrote originally is to use
"strdup", which allocates enough memory to hold a copy of a string, and
then copies the string:

The C language does not specify a strdup function.
blah[0] = strdup("This"); // five bytes are allocated

That is equivalent to this:

blah[0] = (char *)malloc(strlen("this")+1);
strcpy(blah[0],"this");

Better:

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

char *dupstr(const char *s)
{
char *p = malloc(strlen(s) + 1);
if(p != NULL)
{
strcpy(p, s);
}
return p;
}
 
A

Al Bowers

Colin Doig wrote:

.........snip..........
Note that I do not want to use

char *blah[3] = {"This", "doesn't", "work"};

because I need to create the strings on the fly.

If you want to dynamic create and array of strings, one
way to approach this would be to create a struct type
that will have the array and a count of the number of
strings in the array. You would write functions that will
manage this struct type, Write functions that will add
strings to the array , printing, removing, freeing
and others of interest.

An example:

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

typedef struct StrArray
{
char **name;
size_t count;
} StrArray;

typedef enum {FALSE, TRUE} bool;

bool AddName(StrArray *p,const char *s);
void FreeAllNames(StrArray *p);
void PrintNames(StrArray *p);
bool RemoveName(StrArray *p, size_t ele_num);

int main(void)
{
StrArray president = {NULL, 0};

AddName(&president, "George Washington");
AddName(&president, "Bill Clinton");
AddName(&president, "George Bush");
PrintNames(&president);
puts("\nRemoving Name #2\n");
RemoveName(&president,2);
PrintNames(&president);
FreeAllNames(&president);
return 0;
}

bool AddName(StrArray *p,const char *s)
{
char **tmp, *value;

if((tmp = realloc(p->name,(p->count+1)*(sizeof *tmp))) == NULL)
return FALSE;
if((value = malloc(strlen(s)+1)) == NULL) return FALSE;
strcpy(value,s);
tmp[p->count++] = value;
p->name = tmp;
return TRUE;
}

void FreeAllNames(StrArray *p)
{
size_t i;

for(i = 0;i < p->count;i++)
free(p->name);
free(p->name);
p->name = NULL;
p->count = 0;
return;
}

void PrintNames(StrArray *p)
{
size_t i;

for(i = 0; i < p->count; i++)
printf("%u. %s\n",i+1,p->name);
return;
}

bool RemoveName(StrArray *p, size_t ele_num)
{
size_t i;
char **tmp;

if(ele_num == 0 || ele_num > p->count) return FALSE;
i = ele_num-1;
free(p->name);
if(ele_num != p->count)
memmove(&p->name, &p->name[i+1],
(p->count-ele_num)*sizeof(char *));
tmp = realloc(p->name,--p->count*(sizeof *tmp));
if(tmp) p->name = tmp;
return TRUE;
}
 

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

No members online now.

Forum statistics

Threads
474,129
Messages
2,570,770
Members
47,329
Latest member
FidelRauch

Latest Threads

Top