By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
454,122 Members | 1,075 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 454,122 IT Pros & Developers. It's quick & easy.

an interesting problem in c

P: n/a
Hi Everyone,

I have the following structure in my program

struct sample
{
char *string;
int string_len;
};

struct sample **ptr; //serves as two-dimentional array

Now, based on run-time input, i will be allocating the memory for ptr
such that i have a 1d array of pointers to structure sample, and each
member will be holding a string, now the problem i have is that the
following is the run time input, say in a file,

string1
string2
string3

now, i need to allocate ptr to hold three set of struct sample and
store each of the string in one member's string (after allocation),
something like

struct sample ptr = (struct sample**)malloc(<value>*sizeof(struct
sample*));
for(i=0;i<value;i++)
{
struct sample ptr[i] = (struct sample*)malloc(sizeof(struct sample));
sample[i].string = (char*)malloc(100); //each will hold the string
}
The problem i'm facing is that i don't know the value in advance
(which i will know only after the end of the file), please give me any
suggestions as to how to do it?

Dec 19 '06 #1
Share this Question
Share on Google+
22 Replies


P: n/a
sa*****@yahoo.co.in wrote:
I have the following structure in my program

struct sample {
char *string;
int string_len;
};

struct sample **ptr; //serves as two-dimentional array

Now, based on run-time input, i will be allocating the memory for ptr
such that i have a 1d array of pointers to structure sample, and each
member will be holding a string, now the problem i have is that the
following is the run time input, say in a file,

string1
string2
string3

now, i need to allocate ptr to hold three set of struct sample and
store each of the string in one member's string (after allocation),
something like

struct sample ptr = (struct sample**)malloc(<value>*sizeof(struct sample*));
for(i=0;i<value;i++) {
struct sample ptr[i] = (struct sample*)malloc(sizeof(struct sample));
sample[i].string = (char*)malloc(100); //each will hold the string
}

The problem i'm facing is that i don't know the value in advance
(which i will know only after the end of the file), please give me any
suggestions as to how to do it?
I've written an entire library that solves this exact problem (and
others.) You can read about it here:

http://bstring.sf.net/

--
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Dec 19 '06 #2

P: n/a

sa*****@yahoo.co.in wrote:
Hi Everyone,

I have the following structure in my program

struct sample
{
char *string;
int string_len;
};

struct sample **ptr; //serves as two-dimentional array

Now, based on run-time input, i will be allocating the memory for ptr
such that i have a 1d array of pointers to structure sample, and each
member will be holding a string, ...
[snip]
The problem i'm facing is that i don't know the value in advance
(which i will know only after the end of the file), please give me any
suggestions as to how to do it?
You could look at something involving realloc() - allocating an initial
reasonable value for the array of pointers and increasing by a suitable
factor when it overflows.

You could look at a two-pass approach - either read the file once to
determine how much data you have, size the array and then read again;
or read the data initially into a different structure - say a linked
list - and transform it to an array once you have all the data.

Dec 19 '06 #3

P: n/a
>
You could look at something involving realloc() - allocating an initial
reasonable value for the array of pointers and increasing by a suitable
factor when it overflows.

You could look at a two-pass approach - either read the file once to
determine how much data you have, size the array and then read again;
or read the data initially into a different structure - say a linked
list - and transform it to an array once you have all the data.
I can't afford parsing the file two times and neither can i have a
temp list to hold all the values, first one is costly from time
prespective and second one is costly from memory prespective... so i
guess i'm left out with only realloc(), i have never used one... does
it make sure that old block's values are retained when i decide to
re-alloc?

Dec 19 '06 #4

P: n/a
sa*****@yahoo.co.in said:

<snip>
I can't afford parsing the file two times and neither can i have a
temp list to hold all the values, first one is costly from time
prespective and second one is costly from memory prespective... so i
guess i'm left out with only realloc(), i have never used one... does
it make sure that old block's values are retained when i decide to
re-alloc?
Yes, even if the reallocation fails and realloc returns NULL (which it
might).

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 19 '06 #5

P: n/a
>
Yes, even if the reallocation fails and realloc returns NULL (which it
might).
Do you mean, even if re-alloc fails due to memory shortage, it would
return NULL and it wouldn't disturb the old memory which was previously
allocated?

Dec 19 '06 #6

P: n/a
sa*****@yahoo.co.in said:
>
>>
Yes, even if the reallocation fails and realloc returns NULL (which it
might).

Do you mean, even if re-alloc fails due to memory shortage, it would
return NULL and it wouldn't disturb the old memory which was previously
allocated?
Correct. BUT that does require a little effort on your part, if you don't
want to lose track of that old memory.

#include <stdlib.h>

int main(void)
{
int *tmp = NULL;
size_t n = 1024;

int *p = malloc(n * sizeof *p);
if(p != NULL)
{
int i;
for(i = 0; i < n; i++)
{
p[i] = i;
}
/* now let's try to get some more memory */
n *= 2;
tmp = realloc(p, n * sizeof *p);
if(tmp == NULL)
{
/* the allocation FAILED, but because we used tmp to detect
this, we can still get to the old block, using p, which
still points to the old block.
*/
}
else
{
/* the allocation SUCCEEDED. p is now invalid, but we can
safely fix that now:
*/
p = tmp;
while(i < n)
{
p[i] = i;

etc etc etc
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 19 '06 #7

P: n/a

sa*****@yahoo.co.in wrote:
Hi Everyone,

I have the following structure in my program

struct sample
{
char *string;
int string_len;
};

struct sample **ptr; //serves as two-dimentional array

Now, based on run-time input, i will be allocating the memory for ptr
such that i have a 1d array of pointers to structure sample, and each
member will be holding a string, now the problem i have is that the
following is the run time input, say in a file,

string1
string2
string3

now, i need to allocate ptr to hold three set of struct sample and
store each of the string in one member's string (after allocation),
something like

struct sample ptr = (struct sample**)malloc(<value>*sizeof(struct
sample*));
for(i=0;i<value;i++)
{
struct sample ptr[i] = (struct sample*)malloc(sizeof(struct sample));
sample[i].string = (char*)malloc(100); //each will hold the string
}
The problem i'm facing is that i don't know the value in advance
(which i will know only after the end of the file), please give me any
suggestions as to how to do it?
member will be holding a string, now the problem i have is that the
following is the run time input, say in a file,

string1
string2
string3

now, i need to allocate ptr to hold three set of struct sample and
store each of the string in one member's string (after allocation),
something like

struct sample ptr = (struct sample**)malloc(<value>*sizeof(struct
sample*));
for(i=0;i<value;i++)
{
struct sample ptr[i] = (struct sample*)malloc(sizeof(struct sample));
sample[i].string = (char*)malloc(100); //each will hold the string
}

The problem i'm facing is that i don't know the value in advance
(which i will know only after the end of the file), please give me any
suggestions as to how to do it?
The best strategy will depend on the characteristics of typical input.

If possible, one option is to read the input twice, once to count the
number of string pointers needed and a second time to input them and
set the pointers into your dynamic array.

If the input can be read more than once, another option is to count the
number of strings and also accumulate the total number of characters in
them on the first run. Before the second run the pointer array is
allocated for the number of strings plus one and a single array of
characters is allocated for the total number of characters (you might
also want the nul terminators as well).

Then as the strings are read in they are placed 'end to end' in the
large character array and their start positions are recorded in the
pointer array. At the end the last pointer is set to one past the top
of the character array. Now the strings are contained between
consecutive pointer values. This avoids the overhead of many individual
memory allocations for each of the strings and simplifies the memory
allocation and deallocation tasks.

But if the input can only be read once you may need to allocate for a
fixed number of pointers and then to input strings until either the end
of the input or this number is reached. If the pointer array becomes
full before the end of file you can then use realloc() to obtain a
larger block of memory for your structure whilst preserving the
previous values you have set (the block might move but the values are
preserved).

How much to memory to allocate initially for pointers and how expand
the block each time you fill it will again depend on the typical
characteristics of your inputs.

Cactus

Dec 19 '06 #8

P: n/a

sa*****@yahoo.co.in wrote:

You could look at something involving realloc() - allocating an initial
reasonable value for the array of pointers and increasing by a suitable
factor when it overflows.

You could look at a two-pass approach - either read the file once to
determine how much data you have, size the array and then read again;
or read the data initially into a different structure - say a linked
list - and transform it to an array once you have all the data.

I can't afford parsing the file two times and neither can i have a
temp list to hold all the values, first one is costly from time
prespective and second one is costly from memory prespective...
The same set of structures can be members both of the linked list and
the array, of course...

Dec 19 '06 #9

P: n/a
>
#include <stdlib.h>

int main(void)
{
int *tmp = NULL;
size_t n = 1024;

int *p = malloc(n * sizeof *p);
if(p != NULL)
{
int i;
for(i = 0; i < n; i++)
{
p[i] = i;
}
/* now let's try to get some more memory */
n *= 2;
tmp = realloc(p, n * sizeof *p);
if(tmp == NULL)
{
/* the allocation FAILED, but because we used tmp to detect
this, we can still get to the old block, using p, which
still points to the old block.
*/
}
else
{
/* the allocation SUCCEEDED. p is now invalid, but we can
safely fix that now:
*/
p = tmp;
while(i < n)
{
p[i] = i;

etc etc etc
--
I see what you are trying to say, to consider the return value of
re-alloc() when it is successful and when it fails, stick to the old
pointer. And i' m trying to understand as to how re-alloc() works, does
it try to allocate memory at the end of the current chunk and if it
can't do because memory next to current chunk is not free, it would
allocate a whole new block to hold old value and copies old value to
new block making sure new memory is good enough to satisfy the memory
request and free the old block...

Is my understanding correct?

Dec 19 '06 #10

P: n/a
sa*****@yahoo.co.in said:

<snip>
I see what you are trying to say, to consider the return value of
re-alloc()
realloc()
when it is successful and when it fails, stick to the old
pointer.
Right.
And i' m trying to understand as to how re-alloc() works, does
it try to allocate memory at the end of the current chunk and if it
can't do because memory next to current chunk is not free, it would
allocate a whole new block to hold old value and copies old value to
new block making sure new memory is good enough to satisfy the memory
request and free the old block...
....if it can find such a block, yes. Otherwise, the existing memory block
will be left alone and realloc() will return NULL.
>
Is my understanding correct?
Yes, that's a perfectly reasonable description of realloc()'s behaviour.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 19 '06 #11

P: n/a
Richard Heathfield wrote:
sa*****@yahoo.co.in said:
.... snip ...
>
>And i' m trying to understand as to how re-alloc() works, does
it try to allocate memory at the end of the current chunk and
if it can't do because memory next to current chunk is not free,
it would allocate a whole new block to hold old value and copies
old value to new block making sure new memory is good enough to
satisfy the memory request and free the old block...

...if it can find such a block, yes. Otherwise, the existing
memory block will be left alone and realloc() will return NULL.
> Is my understanding correct?

Yes, that's a perfectly reasonable description of realloc()'s
behaviour.
Reasonable, yes. Required, no.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 19 '06 #12

P: n/a
CBFalconer said:
Richard Heathfield wrote:
>sa*****@yahoo.co.in said:
... snip ...
>>
>>And i' m trying to understand as to how re-alloc() works, does
it try to allocate memory at the end of the current chunk and
if it can't do because memory next to current chunk is not free,
it would allocate a whole new block to hold old value and copies
old value to new block making sure new memory is good enough to
satisfy the memory request and free the old block...

...if it can find such a block, yes. Otherwise, the existing
memory block will be left alone and realloc() will return NULL.
>> Is my understanding correct?

Yes, that's a perfectly reasonable description of realloc()'s
behaviour.

Reasonable, yes. Required, no.
Would you care to expand on that, Chuck?

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 19 '06 #13

P: n/a
Richard Heathfield wrote:
CBFalconer said:
>Richard Heathfield wrote:
>>sa*****@yahoo.co.in said:
... snip ...
>>>
And i' m trying to understand as to how re-alloc() works, does
it try to allocate memory at the end of the current chunk and
if it can't do because memory next to current chunk is not free,
it would allocate a whole new block to hold old value and copies
old value to new block making sure new memory is good enough to
satisfy the memory request and free the old block...

...if it can find such a block, yes. Otherwise, the existing
memory block will be left alone and realloc() will return NULL.

Is my understanding correct?

Yes, that's a perfectly reasonable description of realloc()'s
behaviour.

Reasonable, yes. Required, no.

Would you care to expand on that, Chuck?
There is no requirement to attempt to preserve the same pointer and
avoid data copying. My nmalloc goes to lengths to avoid the
copying overhead, but it is not required in order to have a working
malloc system.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 19 '06 #14

P: n/a
sa*****@yahoo.co.in wrote:
I can't afford parsing the file two times
Not even if you use the rewind function?

Depending on what you're parsing for,
sometimes you can go through a file twice pretty quick.

unsigned long max_line_len(FILE *fd)
{
unsigned long count, max;
int rc;

count = max = 0;
while ((rc = getc(fd)) != EOF) {
if (rc == '\n') {
if (count max) {
max = count;
}
count = 0;
} else {
++count;
}
}
rewind(fd);
return max;
}

--
pete
Dec 19 '06 #15

P: n/a
CBFalconer said:
Richard Heathfield wrote:
>CBFalconer said:
>>Richard Heathfield wrote:
<snip>
>>>>
Yes, that's a perfectly reasonable description of realloc()'s
behaviour.

Reasonable, yes. Required, no.

Would you care to expand on that, Chuck?

There is no requirement to attempt to preserve the same pointer and
avoid data copying.
Oh, that's what you meant. And yes, of course you are right. Sorry for not
making that clear in my own reply.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 19 '06 #16

P: n/a
pete <pf*****@mindspring.comwrites:
sa*****@yahoo.co.in wrote:
> I can't afford parsing the file two times

Not even if you use the rewind function?

Depending on what you're parsing for,
sometimes you can go through a file twice pretty quick.
rewind() doesn't always work; try rewinding a keyboard.

And the rewind() function in particular has no way to report an error.
If you want to *try* to rewind a stream, use fseek() instead.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 20 '06 #17

P: n/a
sa*****@yahoo.co.in wrote:
>
>You could look at something involving realloc() - allocating an
initial reasonable value for the array of pointers and increasing
by a suitable factor when it overflows.

You could look at a two-pass approach - either read the file once
to determine how much data you have, size the array and then read
again; or read the data initially into a different structure -
say a linked list - and transform it to an array once you have all
the data.

I can't afford parsing the file two times and neither can i have a
temp list to hold all the values, first one is costly from time
prespective and second one is costly from memory prespective... so
i guess i'm left out with only realloc(), i have never used one...
does it make sure that old block's values are retained when i
decide to re-alloc?
Please don't strip attributions for material you quote. Those are
the initial lines that read "joe wrote:".

Take a look at the methods used in freverse.c, which is a
demonstration and test program that comes with the ggets package.
In turn, ggets can be found at:

<http://cbfalconer.home.att.net/download/>

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 20 '06 #18

P: n/a
Keith Thompson wrote:
>
pete <pf*****@mindspring.comwrites:
sa*****@yahoo.co.in wrote:
I can't afford parsing the file two times
Not even if you use the rewind function?

Depending on what you're parsing for,
sometimes you can go through a file twice pretty quick.

rewind() doesn't always work;
OK, well ...,
if OP wants to make an array of struct sample,
then one of the interesting properties of
a linked list of generic nodes,
is that the list nodes can be freed
while the node's data pointers remain valid.

/* BEGIN array_from_list.c */
/*
** get_line reads a line from a text stream
** and writes the characters to an array of char,
** replacing the newline character with a null character.
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
/*
** INITIAL_BUFFER_SIZE can be any number.
** If INITIAL_BUFFER_SIZE is equal to the number of
** characters in the longest input line,
** then no further allocation will be needed.
*/
#define INITIAL_BUFFER_SIZE 0

struct sample {
char *string;
size_t string_len;
};

struct list_node {
struct list_node *next;
void *data;
};

int get_line(char **lineptr, size_t *n, FILE *stream);
struct list_node *sample_node(struct list_node **head,
struct list_node *tail,
char *data);
void list_free(struct list_node *node, void (*free_data)(void *));
int sample_fprintf(struct list_node *node, FILE *stream);
void sample_free(void *data);
void no_data_free(void *data);

int main(void)
{
struct list_node *head, *tail, *list_ptr;
int rc;
char *buff_ptr;
size_t buff_size;
long unsigned line_count, index;
struct sample **array;

buff_size = INITIAL_BUFFER_SIZE;
buff_ptr = malloc(buff_size);
if (buff_ptr == NULL && buff_size != 0) {
printf("malloc(%lu) == NULL\n", (long unsigned)buff_size);
buff_size = 0;
}
tail = head = NULL;
line_count = 0;
puts("\nThis program makes and prints a list of all the lines\n"
"of text entered from standard input.\n"
"Just hit the Enter key to end,\n"
"or enter any line of characters to continue.");
while ((rc = get_line(&buff_ptr, &buff_size, stdin)) 1) {
++line_count;
tail = sample_node(&head, tail, buff_ptr);
if (tail == NULL) {
break;
}
puts("\nJust hit the Enter key to end,\n"
"or enter any line of characters to continue.");
}
switch (rc) {
case EOF:
if (buff_ptr != NULL && strlen(buff_ptr) != 0) {
puts("rc equals EOF\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = sample_node(&head, tail, buff_ptr);
}
break;
case 0:
puts("realloc returned a null pointer value");
if (buff_size 1) {
puts("rc equals 0\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = sample_node(&head, tail, buff_ptr);
}
break;
default:
break;
}
if (line_count != 0 && tail == NULL) {
puts("Node allocation failed.");
puts("The last line entered didn't make it onto the list:");
puts(buff_ptr);
}
free(buff_ptr);
puts("\nThe line buffer has been freed.\n");
printf("%lu lines of text were entered.\n", line_count);
puts("They are:\n");
sample_fprintf(head, stdout);
array = malloc(line_count * sizeof *array);
if (array != NULL) {
list_ptr = head;
for (index = 0; index != line_count; ++index) {
array[index] = list_ptr -data;
list_ptr = list_ptr -next;
}
list_free(head, no_data_free);
puts("\nThe list nodes have been freed,\n"
"but their data pointers are still valid.");
puts("\nThe array contents is:\n");
for (index = 0; index != line_count; ++index) {
printf("array[%lu] -string_len is %u\n"
"array[%lu] -string is:%s\n\n",
index,
(unsigned)(array[index] -string_len),
index,
array[index] -string);
}
for (index = 0; index != line_count; ++index) {
free(array[index]);
}
free(array);
puts("The array has been freed.");
} else {
list_free(head, sample_free);
puts("\narray == NULL. The list has been freed.\n");
}
return 0;
}

int get_line(char **lineptr, size_t *n, FILE *stream)
{
int rc;
void *p;
size_t count;

count = 0;
while ((rc = getc(stream)) != EOF) {
++count;
if (count + 2 *n) {
p = realloc(*lineptr, count + 2);
if (p == NULL) {
if (*n count) {
(*lineptr)[count] = '\0';
(*lineptr)[count - 1] = (char)rc;
} else {
ungetc(rc, stream);
}
count = 0;
break;
}
*lineptr = p;
*n = count + 2;
}
if (rc == '\n') {
(*lineptr)[count - 1] = '\0';
break;
}
(*lineptr)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count INT_MAX ? INT_MAX : count;
} else {
if (*n count) {
(*lineptr)[count] = '\0';
}
}
return rc;
}

struct list_node *sample_node(struct list_node **head,
struct list_node *tail,
char *string)
{
struct list_node *node;
struct sample *sample_data;
size_t length;

node = malloc(sizeof *node);
if (node != NULL) {
node -next = NULL;
node -data = malloc(sizeof *sample_data);
if (node -data != NULL) {
length = strlen(string);
sample_data = node -data;
sample_data -string_len = length;
sample_data -string = malloc(length + 1);
if (sample_data -string != NULL) {
if (*head == NULL) {
*head = node;
} else {
tail -next = node;
}
memcpy(sample_data -string, string, length + 1);
} else {
free(node -data);
free(node);
node = NULL;
}
} else {
free(node);
node = NULL;
}
}
return node;
}

void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;

while (node != NULL) {
next_node = node -next;
free_data(node -data);
free(node);
node = next_node;
}
}

void sample_free(void *data)
{
free(((struct sample *)data) -string);
free(data);
}

void no_data_free(void *data)
{
data;
}

int sample_fprintf(struct list_node *node, FILE *stream)
{
struct sample s;

while (node != NULL) {
s = *(struct sample *)(node -data);
if (fprintf(stream,
"String length is %3u:%s\n",
(unsigned)s.string_len, s.string) == EOF)
{
break;
}
node = node -next;
}
return node == NULL ? '\n' : EOF;
}

/* END array_from_list.c */

--
pete
Dec 20 '06 #19

P: n/a
pete wrote:
>
Keith Thompson wrote:

pete <pf*****@mindspring.comwrites:
sa*****@yahoo.co.in wrote:
>
> I can't afford parsing the file two times
>
Not even if you use the rewind function?
>
Depending on what you're parsing for,
sometimes you can go through a file twice pretty quick.
rewind() doesn't always work;

OK, well ...,
if OP wants to make an array of struct sample,
then one of the interesting properties of
a linked list of generic nodes,
is that the list nodes can be freed
while the node's data pointers remain valid.
.... which only enables you to make
an array of pointers to struct sample,
but that's pretty close.

--
pete
Dec 20 '06 #20

P: n/a
hi dear richard !

this is gelareh . infact i wanted to get help from you if you can ,
my major is computer
infact i 'm familiar with language "c" to some extent but can not how
to work programming
i mean my base is weak but i have the tendancy to work programming i
wanted to get help
if it's possible for you just to introduce some good reference books
in c programming
and direct me how i can learn programming online and making projects if
i became more
exprienced . i request you from which titles should i begin to make
progress and be professional . thanks very much .and please excuse me
for talking so much.

Dec 20 '06 #21

P: n/a
pete wrote:
for (index = 0; index != line_count; ++index) {
free(array[index]);
}
free(array);
I forgot to free the strings!

Should be:

for (index = 0; index != line_count; ++index) {
free(array[index] -string);
free(array[index]);
}
free(array);

--
pete
Dec 20 '06 #22

P: n/a
pete wrote:
>
pete wrote:

Keith Thompson wrote:
>
pete <pf*****@mindspring.comwrites:
sa*****@yahoo.co.in wrote:

I can't afford parsing the file two times

Not even if you use the rewind function?

Depending on what you're parsing for,
sometimes you can go through a file twice pretty quick.
>
rewind() doesn't always work;
OK, well ...,
if OP wants to make an array of struct sample,
then one of the interesting properties of
a linked list of generic nodes,
is that the list nodes can be freed
while the node's data pointers remain valid.

... which only enables you to make
an array of pointers to struct sample,
but that's pretty close.
But making an array of structures instead of an
array of pointers from a list, isn't really any more difficult.
In this version, the structures and the list nodes
have pointers to non null terminated arrays instead of strings.

/* BEGIN array_from_list.c */

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

#define INITIAL_BUFFER_SIZE 0
/*
** The pointer variables called "string"
** in the struct sample declaration,
** and also in the sample_node function definition,
** actually point to non null terminated arrays
** of "length" number of characters.
*/
struct sample {
char *string;
long unsigned length;
};

struct list_node {
struct list_node *next;
void *data;
};

int get_line(char **lineptr, size_t *n, FILE *stream);
struct list_node *sample_node(struct list_node **head,
struct list_node *tail,
char *string);
void list_free(struct list_node *node, void (*free_data)(void *));
void sample_fprintf(struct list_node *node, FILE *stream);
void sample_free(void *data);

int main(void)
{
struct list_node *head, *tail, *list_ptr;
int rc;
char *buff_ptr, *ptr;
size_t buff_size;
long unsigned line_count, index, length;
struct sample *array;

buff_size = INITIAL_BUFFER_SIZE;
buff_ptr = malloc(buff_size);
if (buff_ptr == NULL && buff_size != 0) {
printf("malloc(%lu) == NULL\n", (long unsigned)buff_size);
buff_size = 0;
}
tail = head = NULL;
line_count = 0;
puts("\nThis program makes and prints a list of all the lines\n"
"of text entered from standard input.\n"
"Just hit the Enter key to end,\n"
"or enter any line of characters to continue.");
while ((rc = get_line(&buff_ptr, &buff_size, stdin)) 1) {
++line_count;
tail = sample_node(&head, tail, buff_ptr);
if (tail == NULL) {
break;
}
puts("\nJust hit the Enter key to end,\n"
"or enter any line of characters to continue.");
}
switch (rc) {
case EOF:
if (buff_ptr != NULL && strlen(buff_ptr) != 0) {
puts("rc equals EOF\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = sample_node(&head, tail, buff_ptr);
}
break;
case 0:
puts("realloc returned a null pointer value");
if (buff_size 1) {
puts("rc equals 0\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = sample_node(&head, tail, buff_ptr);
}
break;
default:
break;
}
if (line_count != 0 && tail == NULL) {
puts("Node allocation failed.");
puts("The last line entered didn't make it onto the list:");
puts(buff_ptr);
}
free(buff_ptr);
puts("\nThe line buffer has been freed.\n");
printf("%lu lines of text were entered.\n", line_count);
puts("They are:\n");
sample_fprintf(head, stdout);
array = malloc(line_count * sizeof *array);
if (array != NULL) {
list_ptr = head;
for (index = 0; index != line_count; ++index) {
array[index] = *(struct sample *)(list_ptr -data);
list_ptr = list_ptr -next;
}
list_free(head, free);
puts("\nThe list nodes have been freed,\n"
"but their string pointers are still valid.");
puts("\nThe array contents is:\n");
for (index = 0; index != line_count; ++index) {
printf("array[%lu].length is %lu\n"
"array[%lu].string is:",
index, array[index].length, index);
length = array[index].length;
ptr = array[index].string;
while (length-- != 0) {
putchar(*ptr++);
}
puts("\n");
}
for (index = 0; index != line_count; ++index) {
free(array[index].string);
}
free(array);
puts("The array has been freed.");
} else {
list_free(head, sample_free);
puts("\narray == NULL. The list has been freed.\n");
}
return 0;
}

int get_line(char **lineptr, size_t *n, FILE *stream)
{
int rc;
void *p;
size_t count;

count = 0;
while ((rc = getc(stream)) != EOF) {
++count;
if (count + 2 *n) {
p = realloc(*lineptr, count + 2);
if (p == NULL) {
if (*n count) {
(*lineptr)[count] = '\0';
(*lineptr)[count - 1] = (char)rc;
} else {
ungetc(rc, stream);
}
count = 0;
break;
}
*lineptr = p;
*n = count + 2;
}
if (rc == '\n') {
(*lineptr)[count - 1] = '\0';
break;
}
(*lineptr)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count INT_MAX ? INT_MAX : count;
} else {
if (*n count) {
(*lineptr)[count] = '\0';
}
}
return rc;
}

struct list_node *sample_node(struct list_node **head,
struct list_node *tail,
char *string)
{
struct list_node *node;
struct sample *sample_data;
size_t length;

node = malloc(sizeof *node);
if (node != NULL) {
node -next = NULL;
node -data = malloc(sizeof *sample_data);
if (node -data != NULL) {
length = strlen(string);
sample_data = node -data;
sample_data -length = length;
sample_data -string = malloc(length);
if (sample_data -string != NULL) {
if (*head == NULL) {
*head = node;
} else {
tail -next = node;
}
memcpy(sample_data -string, string, length);
} else {
free(node -data);
free(node);
node = NULL;
}
} else {
free(node);
node = NULL;
}
}
return node;
}

void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;

while (node != NULL) {
next_node = node -next;
free_data(node -data);
free(node);
node = next_node;
}
}

void sample_free(void *data)
{
free(((struct sample *)data) -string);
free(data);
}

void sample_fprintf(struct list_node *node, FILE *stream)
{
struct sample s;
char *ptr;
size_t length;

while (node != NULL) {
s = *(struct sample *)(node -data);
fprintf(stream, "String length is %3u:",
(unsigned)s.length);
ptr = s.string;
length = s.length;
while (length-- != 0) {
putc(*ptr++, stream);
}
putc('\n', stream);
node = node -next;
}
}

/* END array_from_list.c */

--
pete
Dec 20 '06 #23

This discussion thread is closed

Replies have been disabled for this discussion.