Mannequin* said:
I'm working on a quick program to bring the Bible into memory from a
text file. Anyway, I have three questions to ask.
First, is my implementation of malloc () correct in the program to
follow?
I can't see any "implementation" of malloc(), I guess you mean
"invocation";-)
Second, have I correctly passed the structure's pointer to the
functions in this program?
Last, how do I go about initializing the structure variables in the
given function?
Here's the program:
#include <stdio.h>
#include <stdlib.h>
struct bibtxt
{
char book[32];
int chapter;
int verse;
char text[500];
};
One remark: It's usually not good style to use such "magic" numbers like
32 and 500. If you need them it tends to be preferable to #define a sym-
bolic name for then and then use that names in your program - that will
make it a lot easier to read and also to modify (just think what happens
if you find a verse with more than 499 characters and then having to
hunt through your program to replace the 500's by a new value). So I
would recommend to use e.g.
#define MAX_BOOK_NAME_LENGTH 32
#define MAX_VERSE_LENGTH 500
#define MAX_VERSE_COUNT 1000
(In a later stage of the development of your program you probably will
find that it's going to be more practical to make both that 'book' and
'text' members of the structure char pointers and only allocate as much
memory for them as you really need instead of using some fixed-length
arrays.)
typedef struct bibtxt BIBLE;
void fillverse (BIBLE *pverse);
int main (void)
{
BIBLE *pverse;
int x;
pverse = malloc (1000 * sizeof (BIBLE));
if (pverse == NULL)
{
printf ("It didn't work.\n");
return -1;
Better make that
return EXIT_FAILURE;
so you can be sure that a value meaning falure will be returned on
whatever operating system you are going to compile the program.
The way you call malloc() as well as checking the return value (and
including the necessary header files) is fine. The only thing I would
do differently is to use
pverse = malloc( 1000 * sizeof *pverse );
since if written like this you don't have to worry about replacing
'BIBLE' by something else in case you decide later to make 'pverse'
a pointer to an object of a different type.
printf ("pverse = %d\n", (sizeof (BIBLE) * 1000));
I guess you know that what you're printing here isn't the location to
which 'pverse' is pointing to but the number of allocated bytes (or,
to be precise, the number of chars). Moreover, the value you get from
sizeof() is of type 'size_t' which isn't an int. It is always unsigned
and could also be e.g. a long int. So it might be prudent to cast the
second argument of printf() to the largest unsigned integral type
available on your machine and use the appropriate conversion specifier,
perhaps like this:
printf( "pverse = %lu\n", ( unsigned long ) ( sizeof *pverse * 1000 ) );
for (x = 0; x < 1000; x++)
fillverse (pverse);
If I guess correctly you don't want to "fill" the first structure over
and over again but all of them one after another. In that case (and if
fillverse() is actually meant to work on a single structure at a time)
what you need here is either
for ( x = 0; x < 1000; x++ )
fillverse( pverse + x );
or
for ( x = 0; x < 1000; x++ )
fillverse( &pverse[ x ] );
("pverse + x" and "&pverse[ x ]" are equivalent, both point to the
x-th structure in the array of structures you allocated).
for (x = 0; x < 1000; x++)
printverse (pverse);
There's probably the same problem here...
To stay on the safe side you should use
return EXIT_SUCCESS;
here, a return value of 0 does not necessarily mean success under all
operating systems while EXIT_SUCCESS will be correct wherever you
compile the program.
void fillverse (BIBLE *pverse)
{
int x, y;
for (x = 0; x < 1000; x++)
Why do you loop over 'x' when it's never used in the following? You
are just heating up your CPU;-) That way you just repeat the inner
loop a thousand times on the same structure.
for (y = 0; y < 500; y++)
{
printf ("%s\n", pverse.text);
pverse.text[y] = '\0';
}
}
That doesn't make much sense. First of all, going by the name of the
function I would tend to assume that it's meant to put some data in
the structure. And since it's called directly after allocating memory
the 'text' member of your 'bibtxt' structure 'pverse' is pointing to
contains just random data and nothing you could print and, wprse, there
is also nothing that guarantees that there's a '\0' in the 'text' array,
so in the first invocation of printf() you could even end up with
accessing memory beyond the end of that array unless there's a '\0'
accidentally already in the memory you got from malloc(). The next
problem is that 'pverse' is a pointer so you would need
printf( "%s\n", pverse->text );
and, in the following line,
pverse->text[ y ] = '\0';
Since you then would put a '\0' into the first element later calls of
prinf() are ok (but they are rather useless, there's not much to see
when you print just the empty string).
All what that loop (after the replacing the dot by the arrow operator)
does is to initialize all elements of the 'text' array with '\0'. I do
not see what this is good for but if it's really what you want to do
using memset() would be probably a lot more efficient. And if you would
use calloc() instead of malloc() you would get the same effect without
any additional code at all.
If the purpose of the fillverse() function is actually to zero out the
'text' member arrays of all the 'bibtxt' structures you allocated then
you could write it as
void fillverse( BIBLE *pverse )
{
int i;
for ( i = 0; i < 1000; i++ )
memset( pverse[ i ].text, 0, 500 );
}
or, if you prefer to loop over the elements of 'text'
void fillverse( BIBLE *pverse )
{
int i, j;
for ( i = 0; i < 1000; i++ )
for ( j = 0; j < 500; j++ )
pverse[ i ].text[ j ] = '\0';
}
Please note that now you have to use the dot instead of the arrow
operator. While the unadorned 'pverse' is a pointer, 'pverse[ i ]'
is the i-th structure of the array of structures 'pverse' is poin-
ting to.
If you write fillverse() like this you only have to call it once,
passing it the address of the memory you allocated, and not a
thousand times!
If you instead want to loop in main() you would rewrite fillverse()
as
void fillverse( BIBLE *pverse )
{
memset( pverse->text, 0, 500 );
}
and call it from main() like this
for ( i = 0; i < 1000; i++ )
fillverse( pverse + i );
Please give my any advice on style or whatever you think may be
appropriate.
Since it's a bit unclear what exactly the fillverse() function is
supposed to do (I don't think that you really want to zero out
the 'text' member of the first of your allocated structures a
million times, don't you?) it's hard to tell what to do instead.
Can you expain a bit more detailed? Since I assume that you want
to initialize the members of the structures in that function you
also will have to tell where you plan to get that data from etc.
Regards, Jens