supplementary C frequent answers

R

Richard Heathfield

Peter said:
With more missing context in this message, I thought that he was talking
about /having/ a prototype yet casting to the wrong type. Can you see any
problems with that other than failing to compile?

Not without thinking about it, and perhaps not even then. Note to self:
"sign up for my own refresher course in Reading For Comprehension..."
 
P

pete

Joe said:
Show me. strncpy() will write n characters to the destination.

Yes, strncpy() will write n characters to the destination.
What I meant to say, but missed by a lot,
is that for cases where there are less than n bytes in string s2,
that strncpy will write more characters than strcpy will
for the same s1 and s2.
 
I

Ian Woods

Such a problem would be covered by the second point, "Casting its
return value can mask a failure to #include <stdlib.h>, which
leads to undefined behavior." We are discussing the third point,
"If you cast to the wrong type by accident, odd failures can
result."

Trivial possible example:

You have a machine where all types must be aligned to a boundary the same
as the types size: it can do 8-bit transfers from any 8 bit boundary, 16-
bit transfers from any 16-bit boundary and so on. The machine has
different instructions to read different sized values, and uses an index
system so that:

LD8 A,42 loads the 8 bit value at the 43rd address,
LD16 B, 42 loads the 16 bit value at the 43rd 16-bit address (coinciding
with the 84th and 85th 8-bit address)
and so on.

On this machine, a void pointer is going to be a flat address like that
used with LD8. A cast to a 16-bit short will perform a right shift.

Using a pointer to the wrong type will mean reading from the wrong
address.

I don't know of any machines which actually do this, but I'm sure that
something like it exists somewhere! (The ARM is somewhat like this,
especially since you can shifts with memory-load instructions without
cost. You could index different sized types in this way.)

Ian Woods
 
P

Peter Pichler

Ian Woods said:
Trivial possible example:

You have a machine where all types must be aligned to a boundary the same
as the types size: it can do 8-bit transfers from any 8 bit boundary, 16-
bit transfers from any 16-bit boundary and so on. The machine has
different instructions to read different sized values, and uses an index
system so that:

LD8 A,42 loads the 8 bit value at the 43rd address,
LD16 B, 42 loads the 16 bit value at the 43rd 16-bit address (coinciding
with the 84th and 85th 8-bit address)
and so on.

On this machine, a void pointer is going to be a flat address like that
used with LD8. A cast to a 16-bit short will perform a right shift.

Using a pointer to the wrong type will mean reading from the wrong
address.

Yes, but... malloc() returns an address properly aligned for any type.
In other words, even if you cast void* to any wider type*, thus losing
the lowest bits of the address, it's no real loss because those bits
must have been zeros anyway. You have to cast again b efore assigning
to a pointer, otherwise it would not compile at all.

NB: I understand that we are talking about cases like

double *p = (int*)malloc(sizeof *p);

This would not compile, unless your compiler is seriously broken.
To make it compile, you would have to cast again, e.g.

double *p = (double*)(int*)malloc(sizeof *p);

I cannot see any possible problems with that, other than it is UGLY,
provided that stdlib.h is #included.

The problems that Ben encountered may have been something like

double *p = (double*)malloc(sizeof(int));

Because *p used to be int*, but changed to double*, and the programmer
failed to notice that (s)he had to change the parameter as well. This
may even work by accident, if the implementation allocates memory in
segments, making such bugs even more difficult to spot.

Peter Pichler
 
E

Eric Sosman

Ben said:
Over the last couple of years, and especially recently, I've
built up a few "stock" answers that supplement the C FAQ. If
anyone wants to review and comment on them, I've just now put
them up on my webpage, at
http://benpfaff.org/writings/clc

In "Why should toupper()'s argument be cast to unsigned
char?" the second (?) bulleted exception appears garbled:

. But these implementations are invariably free-
standing, meaning that they don't implement the
character handling functions anyway.)

In "How should malloc()'s return value be used?" the
second argument against casting is beginning to lose its
force. As of C99, the cast cannot mask the error.

In "How should sizeof be used in malloc()'s argument?"
the second reason given isn't quite right. It's not "the
sizeof syntax" that is self-evidently correct (the compiler,
after all, will complain loudly about syntax errors), but
the agreement between the type pointed to and the type of
sizeof's operand.

In "How can I shuffle the contents of an array?" I'm
not sure why shuffle() uses `j = i + rand() % (n - i)'
instead of the apparently simpler `j = rand() % (i + 1)'.
The important thing, of course, is *not* to use the still
simpler but biased `j = rand() % n'.

Also, the discussion might be improved (or might not;
de gustibus) by making two additional points:

- `rand() % N' is not an especially good idiom for
a random integer in [0,N). The FAQ suggests one
alternative; rejection is another.

- For the particular case of shuffling a simulated
deck of 52 playing cards, note that rand() can
deliver no more than UINT_MAX distinct sequences.
UINT_MAX is about 4e9 on most implementations but
52 factorial is about 8e67, so the vast majority
of shuffled decks are unattainable. (A 226-bit
or wider `unsigned' would overcome this problem.)
 
B

Ben Pfaff

Eric Sosman said:
In "Why should toupper()'s argument be cast to unsigned
char?" the second (?) bulleted exception appears garbled:

. But these implementations are invariably free-
standing, meaning that they don't implement the
character handling functions anyway.)

Hmm. Turns out to be a bug in my (almost)-plaintext-to-HTML
translator. Fixed.
In "How should malloc()'s return value be used?" the
second argument against casting is beginning to lose its
force. As of C99, the cast cannot mask the error.

Good point, noted.
In "How should sizeof be used in malloc()'s argument?"
the second reason given isn't quite right. It's not "the
sizeof syntax" that is self-evidently correct (the compiler,
after all, will complain loudly about syntax errors), but
the agreement between the type pointed to and the type of
sizeof's operand.
Ditto.

In "How can I shuffle the contents of an array?" I'm
not sure why shuffle() uses `j = i + rand() % (n - i)'
instead of the apparently simpler `j = rand() % (i + 1)'.
The important thing, of course, is *not* to use the still
simpler but biased `j = rand() % n'.

As is, the expression is the clearest way for me to visualize
what's going on the array. Each successive element of the array
becomes a randomly selected element taken from the as-yet
unvisited segment of the array. Your suggestion would require
moving backward in the array and I just have trouble visualizing
it for some reason. Because it (probably) wouldn't be any faster
to do it your way in practice, I write it the way that's clearest
to me.
Also, the discussion might be improved (or might not;
de gustibus) by making two additional points:

- `rand() % N' is not an especially good idiom for
a random integer in [0,N). The FAQ suggests one
alternative; rejection is another.

- For the particular case of shuffling a simulated
deck of 52 playing cards, note that rand() can
deliver no more than UINT_MAX distinct sequences.
UINT_MAX is about 4e9 on most implementations but
52 factorial is about 8e67, so the vast majority
of shuffled decks are unattainable. (A 226-bit
or wider `unsigned' would overcome this problem.)

Good points, updated.

Updates should propagate within half an hour or so.
 
B

Ben Pfaff

Keith Thompson said:
Ben Pfaff said:
Jeremy Yallop said:
Ben Pfaff wrote:
| Casts are generally undesirable, but there are several situations
| where a cast legitimately comes in handy:
[...]
| * Passing a null pointer to a varargs function.

More generally, calling a varargs function with any value whose type
isn't compatible with what the function expects.

Most of the time, though, such conversions are more reasonably
done without a cast. (In my opinion, of course.)

Can you give some examples?

Here are a couple of cases where (IMHO) a cast is appropriate:

int *ptr = <whatever>;

printf("sizeof(ptr) = %d\n", (int)sizeof(ptr));
printf("ptr = [%p]\n", (void*)ptr);

In C99, you can avoid the cast in the first printf by using "%zu", but
that's not universally supported yet. (I might use unsigned long
rather than int, but I'm reasonably certain in this case that
sizeof(ptr) <= INT_MAX.)

In the second printf, I don't see a good way to avoid the cast.

I think I agree with you now. I've updated the page and it
should propagate soon.
 

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
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top