malloc experiments

  • Thread starter Steve Zimmerman
  • Start date
E

Eric Sosman

Mark A. Odell said:
strncat(president->name, "George Washington", PRES_NAME_LEN - 1);

Question on style here. How does PRES_NAME_LEN improve readability versus
the orignal

struct pres {
char name[25];
struct pres *next;
};

if we use sizeof president->name instead of PRES_NAME_LEN? I see the
manifest constant as extra work without real additional clarity.

It doesn't clarify the struct declaration. However,
elsewhere in the code we find things like the above-mentioned

strncat(president->name, "George Washington",
PRES_NAME_LEN - 1);

.... and this "spelling" of the third argument is superior to
a bare `24' or even `25 - 1' for the simple reason that it's
easier to alter the program to accommodate "George Herbert
Walker Bush" (26 letters, plus terminating '\0'): just change
the #define and all your worries are over. No need to go
groveling through the code looking for all occurrences of
`24' and `25' and trying to figure which need changing.

To my taste, though, it's still not a good solution. If
you come upon this strncat() call in the middle of some code
somewhere, how can you tell that `president->name' is in fact
an array of `PRES_NAME_LEN' characters? To me, the name
suggests that the array should have `PRES_NAME_LEN+1' elements,
but the main point would be the same even with a less equivocal
identifier: How do you know that the coder used the manifest
constant associated with `president->name' and not with some
other array altogether? The situation is analogous to the
oft-deprecated

ptr = malloc(sizeof(struct header_type_6));

.... where there's no immediate confirmation that the coder has
passed the correct type to `sizeof'.

The recommended cure for the malloc case is to write

ptr = malloc(sizeof *ptr);

.... and I'd suggest that the cure for the strncat() case should
be similar:

strncat(president->name, "George Washington",
sizeof president->name - 1);

No worries about whether the argument should be `PRES_NAME_LEN'
or `PRES_NAME_LEN - 1' or perhaps `NAME_BUFFER_SIZE - 1', and
the correctness can be established by purely local inspection.

"Almost established," anyhow, because the coder needs to
know that `president->name' is an actual array and not a pointer
to the start of an array elsewhere. But that's a burden that
C coders shoulder pretty much all the time anyhow, and we should
by now be accustomed to and wary of it.

Summary: `sizeof' is your friend most of the time, and you
should enjoy that friendship whenever it's offered.
 
M

Mark A. Odell

Question on style here. How does PRES_NAME_LEN improve readability
versus the orignal

struct pres {
char name[25];
struct pres *next;
};

if we use sizeof president->name instead of PRES_NAME_LEN? I see the
manifest constant as extra work without real additional clarity.

Although I'm not the person who suggested `PRES_NAME_LEN' originally in
this thread, I still like the idea. The reason is that I prefer to
#define /all/ magic numbers in one place per file, so if this were my
code, I would have used the macro in the structure declaration, but
still referred to the size of the member as `sizeof president->name'
later.

Fair enough.
 
M

Mark A. Odell

Eric Sosman said:
Mark A. Odell said:
Question on style here. How does PRES_NAME_LEN improve readability
versus the orignal

struct pres {
char name[25];
struct pres *next;
};

if we use sizeof president->name instead of PRES_NAME_LEN? I see the
manifest constant as extra work without real additional clarity.

It doesn't clarify the struct declaration. However,
elsewhere in the code we find things like the above-mentioned

[snip]

Summary: `sizeof' is your friend most of the time, and you
should enjoy that friendship whenever it's offered.

In the end, do I take it that you agree with me? That is, to use the
sizeof on the array and not create a manifest constant? I appreciate
Martin's desire to put all magic numbers in one place however. I might
amend this to "comment" all my magic numbers w/ a manifest constant, use
them in the definition of the size of the array, but then never use them
in the code again using only sizeof arrayname. How does this sound?
 
E

Eric Sosman

Mark A. Odell said:
Eric Sosman said:
Mark A. Odell said:
Question on style here. How does PRES_NAME_LEN improve readability
versus the orignal

struct pres {
char name[25];
struct pres *next;
};

if we use sizeof president->name instead of PRES_NAME_LEN? I see the
manifest constant as extra work without real additional clarity.

It doesn't clarify the struct declaration. However,
elsewhere in the code we find things like the above-mentioned

[snip]

Summary: `sizeof' is your friend most of the time, and you
should enjoy that friendship whenever it's offered.

In the end, do I take it that you agree with me? That is, to use the
sizeof on the array and not create a manifest constant?

It certainly seems we agree. There remains the problem
of an "array" that's actually a pointer (perhaps by virtue of
being passed as a function argument), but them's the breaks.
I appreciate
Martin's desire to put all magic numbers in one place however. I might
amend this to "comment" all my magic numbers w/ a manifest constant, use
them in the definition of the size of the array, but then never use them
in the code again using only sizeof arrayname. How does this sound?

Personally, I don't think I'd find it helpful. YMMV.

However, if you've got several arrays that are all supposed
to have the same number of elements, it may make sense to use
just one manifest constant for all the declarations even if you
go on to use `sizeof' everywhere else:

#define WIDE 20
#define TALL 50
int matrix[TALL][WIDE];
int row_totals[TALL];
int row_maxima[TALL];
int col_totals[WIDE];
int col_maxima[WIDE];

It's possible to eliminate the manifest constants:

int matrix[50][20];
int row_totals[ sizeof matrix / sizeof matrix[0] ];
int row_maxima[ sizeof matrix / sizeof matrix[0] ];
int col_totals[ sizeof matrix[0] / sizeof matrix[0][0] ];
int col_maxima[ sizeof matrix[0] / sizeof matrix[0][0] ];

.... but to my taste this is surpassingly ugly, and not an aid
to clarity. (Confession: When I first wrote this, I got it
backwards.)
 
D

Default User

August said:
It's common practice in C++, one of the differences between that
language and C and one of the reasons experienced C programmers blanch
at the meaningless term 'C/C++'.


Common practice indeed, because it's REQUIRED in C++.




Brian Rodenborn
 
R

Randy Howard

Oh, dear. They also void main(). That website seems to be living up to its
name.

I downloaded the source code archives posted there for the "book form" of
this website. It is filled with similar problems. I sent an email to the
author about some of it, I don't know how it will turn out.
 
I

Irrwahn Grausewitz

(e-mail address removed) (Dave Vandervies) wrote in
'Tis? I thought using new and delete was also an option.

The latter has no impact on the requirement to explicitly
cast malloc's return value in (here off-topic) C++.

Irrwahn
 
D

Dave Vandervies

(e-mail address removed) (Dave Vandervies) wrote in


The latter has no impact on the requirement to explicitly
cast malloc's return value in (here off-topic) C++.

Butbutbut... if you avoid using malloc at all, there's no need to
explicitly cast its return value.

I think it's time for me to give up on this thread.


dave
[pokes the sigmonster with a pointy stick]
 
I

Irrwahn Grausewitz

Dave said:
In Irrwahn Grausewitz:

Butbutbut... if you avoid using malloc at all, there's no need to
explicitly cast its return value.

You are allowed to smoke at the petrol station
because you rode in on a bicycle? :eek:)

<SNIP>
 
D

Dave Vandervies

You are allowed to smoke at the petrol station
because you rode in on a bicycle? :eek:)

No, more like if you avoid petrol stations and other areas where you'd
find flammable vapors (which is a sensible thing to do if you're riding
a bicycle), you don't have to avoid smoking for fire-safety reasons.


dave
(Look! I changed the subject, so it's a different thread that I don't
have to give up on!)
 
J

John Bode

Steve Zimmerman said:
This program compiles fine, but are there any hidden dangers
in it, or is it ok?

Experiment 1 ##################################################

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

Huh? This isn't a standard header. Besides, the prototype for
malloc() is in stdlib.c anyway.
#include <string.h>

int main()
{
struct pres {
char name[25];
struct pres *next;
};

struct pres *president;

president = (struct pres *)malloc(sizeof(struct pres));

Don't cast the return value of malloc in C. First of all, you don't
need to; malloc() returns a void*, which is implicitly converted to
the target pointer type. Secondly, doing so will suppress a
diagnostic if you forget to include stdlib.h or otherwise don't have a
prototype for malloc() in scope.

*ALWAYS* check the return value of malloc() before attempting to use
it.

BTW, you can use sizeof *president instead of sizeof (struct pres) as
an argument to malloc(). That way, if the base type of president ever
changes, you don't have to hack all your calls to malloc().
strcpy(president->name, "George Washington");

It's a good idea to protect against potential buffer overruns; don't
just blindly assume that your target is always big enough to hold the
string you're copying. Even though you *know* that it is in this
particular case, it's a good idea to put in sanity checks anyway.
president->next = (struct pres *)malloc(sizeof(struct pres));

Same rant as above re: casting the return value of malloc().
printf("The first structure has been created:\n");
printf("president->name = %s\n", president->name);
printf("next structure address = %i\n", president->next);

Use the %p conversion specifier to print pointer values, not %i.
return 0;
}

###################################################################

--Steve

I know you're just experimenting, but when you create abstract data
types like this, it's a good idea to create an abstract interface for
them as well. Instead of calling malloc() and strcpy() directly from
the application code, create a set of functions that encapsulate all
the operations necessary to create and manage objects of that type.
For example (off the top of my head, untested, all the usual caveats
apply):

struct pres *newPresident (char *name)
{
struct pres *p;

p = malloc (sizeof *p);
if (p)
{
p->next = NULL;
if (name)
{
if (strlen (name) > sizeof p->name - 1)
printf ("Name too long -- truncating...\n");

strncpy (p->name, name, sizeof p->name - 1);
p->name[sizeof p->name - 1] = 0;
}
}

return p;
}

void addToList (struct pres *p, struct pres *head)
{
struct pres *cur;
assert (head != NULL);

cur = head;
while (cur->next)
cur = cur->next;

cur->next = p;
}

void printEntry (struct pres *p)
{
printf ("addr = %p\nname = %s\nnext = %p\n\n",
p, p != NULL ? p->name : "(null)",
p != NULL ? p->next : "(null)");
}

void printList (struct pres *head)
{
struct pres *cur = head->next;
while (cur)
{
printEntry (cur);
cur = cur->next;
}
}

And a very crude example of these functions in use:

int main (void)
{
struct pres head, *newp;

newp = newPresident ("George Washington");
if (newp)
addToList (newp, &head);

newp = newPresident ("Abraham Lincoln");
if (newp)
addToList (newp, &head);

newp = newPresident ("James K. Polk");
if (newp)
addToList (newp, &head);

printList (&head);

return 0;
}
 
A

Alex

Huh? This isn't a standard header. Besides, the prototype for
malloc() is in stdlib.c anyway.

ITYM: stdlib.h

Use the %p conversion specifier to print pointer values, not %i.

....and cast your pointer to (void *), as that is what %p expects.

Alex
 
R

Richard Heathfield

Randy Howard wrote:

I downloaded the source code archives posted there for the "book form" of
this website. It is filled with similar problems. I sent an email to the
author about some of it, I don't know how it will turn out.

It is unlikely to turn out well. Last time I checked his web site, he seemed
to be downright proud of his rebellious stance on matters such as void
main(). He is clearly aware of the issue, and chooses (metaphorically) to
clap his hands over his ears and shout "Nah nah I can't hear you nah nah".

Heaven knows why.
 
D

Default User

Dave said:
'Tis? I thought using new and delete was also an option.


What do new and delete have to do with whether you cast the return of
malloc()?




Brian Rodenborn
 
D

Default User

Dave said:
Butbutbut... if you avoid using malloc at all, there's no need to
explicitly cast its return value.


What return value? If you don't use it, then there isn't one.




Brian Rodenborn
 
D

Dave Vandervies

What return value? If you don't use it, then there isn't one.

Fine, then, the value it would have returned had you used it.

(I should know better than to get myself into this sort of thing by now.)


dave
 
R

Randy Howard

Randy Howard wrote:



It is unlikely to turn out well. Last time I checked his web site, he seemed
to be downright proud of his rebellious stance on matters such as void
main(). He is clearly aware of the issue, and chooses (metaphorically) to
clap his hands over his ears and shout "Nah nah I can't hear you nah nah".

Perhaps. The response I received was basically (paraphrasing) "Thank you,
but I have asked on comp.lang.c about this in the past, and nobody could
point to a single system that would actually crash with void main()."

He did mention that the books were written before the standard and are
being revamped right now, so some of the items might be clarified in the
next edition.
 

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

Similar Threads

Adding adressing of IPv6 to program 1
Array of structs function pointer 10
Organization Assignment in C programming 0
Command Line Arguments 0
malloc 40
Linux: using "clone3" and "waitid" 0
Fibonacci 0
URGENT 1

Members online

Forum statistics

Threads
474,079
Messages
2,570,573
Members
47,204
Latest member
MalorieSte

Latest Threads

Top