Questions, please

J

Jack Klein

Régis did not attempt to use the past tense of "teach,"
but the past participle of "teach." The first is "taught,"
as Christopher correctly states, but the second is "taught."
See the difference?

Strained attempt at topicality: In C, the keyword stating
that an identifier has internal linkage is "static." The
keyword stating that an identified data object persists for
the entire life of the program is also "static." See the
difference?

Aside from the extreme reluctance to wear out valuable and expensive
teletypes (after all, "ls" is one character shorter than spendthrift
excesses like "dir" or old HP's "cat"), the originators of C and UNIX
were properly parsimonious with keywords.

Still, there is a certain sense to it. Any object, file or block
scope, defined as static has both of the following two
characteristics:

- no external linkage

- static storage duration

There is no way other than using the static keyword to define a data
object with both of these attributes, only one or the other.

So effectively a static object has essentially the same properties
regardless of whether it is defined at file or block scope.
 
J

Jack Klein

That's not true.

The Standard (or rather, my copy of the draft) says:

If the expression that precedes the parenthesized argument list in a
function call consists solely of an identifier, and if no declaration is
visible for this identifier, the identifier is implicitly declared exactly
as if, in the innermost block containing the function call, the declaration

extern int identifier();


As you can see, no casting is involved.

You misread the question. He was not asking about the type assumed by
the compiler for the function, but the fate of an argument of type
short passed to a function without a prototype in scope.
 
C

Chris Torek

regarding
This code gives ap a known value, which is useful in ensuring that the
program is deterministic. i.e. it prevents (or rather, helps to prevent)
the program's behaviour from being unpredictable. That's a Good Thing.

Except when it does not compile, such as when "va_list" is an alias
for an array type (e.g., array 1 of some struct, on the PowerPC).

You can safely write:

va_list ap = { 0 };

since braces are legal for both scalar and aggregate types, and 0
is always a valid initializer. The resulting "va_list" is not
good for anything, but could at least fail in a more-reproducible
manner.

(I prefer not to initialize "intentionally uninitialized" variables
myself, and turn on compiler warnings where possible to make sure
that code paths that use them uninitialized are caught, but this
is one of the more difficult compromise-choices. Unlike the debate
about casting malloc, no single way is statistically much more
reliable than the other, in my own observations at least.)
 
J

Jack Klein

comp.lang.c:

[snip]
Thank you all for responses!
Here is another line I am reading, which is confusing to me. Would you
elaborate on it?
"If no prototype for a function specifying its argument types, value of
short is cast'ed to int."

The sentence you quoted accurately describes what happens, but is uses
incorrect wording to do so.

Given this declaration:

void func(); /* declaration with no information about arguments */

int main(voia)
{
short x = 3;
func(x);
return 0;
}

Then the value of "x", a short, is not "cast'ed" to int. The only
thing that performs a cast in C is a cast operator. If you wanted to
cast the value of "x" to int, you would write a cast like this:

int y = (int)x;

What C has are conversions and promotions. Many conversions and
promotions are implicit, that is they happen automatically and do not
require a cast, like assigning the value of short "x" to the int "y",
above. The value of "x" is converted to int before assigning to the
int "y" even if the cast is removed.

There is no such thing as an "implicit" or "automatic" cast in C, a
cast is an explicit conversion.

In the case of a function without a prototype, all integer types of
rank less than int, that is _Bool, char, and short, are passed as
either int or in some cases unsigned int. This is an automatic
conversion (not any kind of cast) and this particular automatic
conversion is called "default argument promotion".
 
C

CBFalconer

Peter said:
AKAICS, neither does...

va_list ap = 0;

...or...

while (yadda)
{
continue;
}

Yet there is at least one clc regular who chooses to utilise
such practices. ;)

You misquote me :). I recommend:

while (yadda) continue;

which avoids the pain of using the shift key for /* do nothing */
comments, yet firmly reminds the casual reader that "yadda"
contains the meat. This, to me, comes under the heading of
"something good".
 
C

CBFalconer

Malcolm said:
.... snip ...
With the exception of casts from floating point types to integers,
C casts are simple reinterpretations of bits. If the bit pattern
doesn't make sense for the type you are casting to, you will get
garbage results and the compiler won't warn about them.

Likely, but NOT guaranteed. The only data type that is guaranteed
not to have any padding or guard bits is unsigned char. Anything
else can have trap values.
 
R

Richard Heathfield

Jack said:
You misread the question. He was not asking about the type assumed by
the compiler for the function, but the fate of an argument of type
short passed to a function without a prototype in scope.

Oops! My apologies to the OP.

Anyway, IMHO it's still not true, since an implicit conversion occurs,
rather than a cast.
 
R

Richard Heathfield

Chris said:
regarding



Except when it does not compile, such as when "va_list" is an alias
for an array type (e.g., array 1 of some struct, on the PowerPC).

Yes, I should have pointed that out, and chose to gloss over it.
You can safely write:

va_list ap = { 0 };

And I do...
since braces are legal for both scalar and aggregate types, and 0
is always a valid initializer. The resulting "va_list" is not
good for anything, but could at least fail in a more-reproducible
manner.

....but (well, not necessarily for va_list, but for structs in general) I get
a really, really annoying gcc diagnostic:

wnnint.c:3119: warning: missing initializer
wnnint.c:3119: warning: (near initialization for `j.sign')

I really wish there were a way of turning this off without losing any other
diagnostics.

(I prefer not to initialize "intentionally uninitialized" variables
myself, and turn on compiler warnings where possible to make sure
that code paths that use them uninitialized are caught, but this
is one of the more difficult compromise-choices. Unlike the debate
about casting malloc, no single way is statistically much more
reliable than the other, in my own observations at least.)

Indeed.
 
J

Jack Klein

On Mon, 2 Feb 2004 23:15:32 -0000, "Malcolm"

[snip]
With the exception of casts from floating point types to integers, C casts
are simple reinterpretations of bits. If the bit pattern doesn't make sense
for the type you are casting to, you will get garbage results and the
compiler won't warn about them.

Um, err, cough, all right out with it...

RUBBISH! BALDERDASH!

Consider our old friend the signed magnitude integer machine, with 16
bit ints (INT_MAX = 32767, UINT_MAX = 65534). On such a platform, the
humble value of -1 is represented as (binary):

1000000000000001 (or 0x8001, if you prefer)

And yet, should such a value be cast (or even converted by assignment)
to unsigned int, the bits needs must rearrange themselves,
instantaneously, into:

1111111111111111 (known to friends as 0xFFFF)
eg

char buff[32];

void *ptr = buff;

double *real = (double *) ptr;

could well load "real" with an illegal value, if the platform demands
doubles aligned on an 8-bit boundary but "buff" is not so aligned.

Remember too that a pointer to char and/or void may actually be
physically larger, and contain more bits, than a pointer to some or
all other object types. I've heard that such is true of some Cray
boxes, but I can't confirm by direct experience because no one's been
silly enough to give me one to play with.
 
S

Simon Biber

Jack Klein said:
Consider our old friend the signed magnitude integer machine, with 16
bit ints (INT_MAX = 32767, UINT_MAX = 65534). On such a platform, the
^^^^^
UINT_MAX must be of the form pow(2,n)-1 where n is the number of value bits.
Therefore 65534 is impossible, it would still be 65535.

The thing with signed magnitude is that INT_MIN == -32767
instead of the usual INT_MIN == -32767-1
 
R

Richard Bos

Malcolm said:
The * here is used to declare a pointer. * is also used as the indirection
operator, which causes a lot of confusion.
Your statement will set p to 0, ie to the NULL pointer.

No, it won't. At least, it probably will, but that's not guaranteed.

What it will do is to set p to _the pointer value that results from
translating an integer object with value 0 to pointer_.
This is not necessarily the same thing as the pointer value that results
from translating a _constant_ integral zero to pointer - _that_ is a
null pointer. (Note, btw: null pointer, not NULL pointer. NULL is a
macro which expands to a null pointer constant, but it isn't a pointer
itself - it's a compile-time constant.)
On the majority, indeed probably the vast majority of systems, these
will be the same. However, a system which has a hardware null pointer of
<FFFF:FFFF> is not unimaginable, and on that system, a wise
implementation might well compile this code:

#include <stdio.h>

int main(void)
{
int i=0;
void *p_int=i, p_direct=0;

printf("p_int is %p; p_direct is %p.\n", p_int, p_direct);
return 0;
}

to output

p_int is <FFFF:FFFF>; p_direct is <0000:0000>.

Not very likely on everyday systems, perhaps, but far from impossible.
With the exception of casts from floating point types to integers, C casts
are simple reinterpretations of bits.

No; of values. From n869, 6.5.4:

# [#4] Preceding an expression by a parenthesized type name
# converts the value of the expression to the named type.

Note: _value_ of the expression. Not bit pattern.

Richard
 
D

Dan Pop

In said:
This is not portable. The result of assigning an integer to a pointer is
implementation dependent.

This is not C. The attempt to assign an integer (other than the null
pointer constant) to a pointer requires a diagnostic.

Dan
 
D

Dan Pop

In said:
Here is another line I am reading, which is confusing to me. Would you
elaborate on it?
"If no prototype for a function specifying its argument types, value of
short is cast'ed to int."

You should choose your C tutorials more carefully. The value is
converted, not cast and this is only a particular case of a general rule:
the integer promotions are applied to the arguments of unprototyped
functions.

Dan
 
M

Martin Dickopp

Malcolm said:
The * here is used to declare a pointer. * is also used as the indirection
operator, which causes a lot of confusion.
Your statement will set p to 0, ie to the NULL pointer.

No, `i' is not an integer *constant* expression. Therefore, after the
following definitions

int i = 0;
char *p = (char *)i;

the value of `p' is implementation-defined. It is /not/ guaranteed to be
a null pointer.

Martin
 
P

Peter Nilsson

Chris Torek said:
(I prefer not to initialize "intentionally uninitialized" variables
myself, and turn on compiler warnings where possible to make sure
that code paths that use them uninitialized are caught, but this
is one of the more difficult compromise-choices. Unlike the debate
about casting malloc, no single way is statistically much more
reliable than the other, in my own observations at least.)

You say 'statistically', have there been any formal studies on malloc
casting? If so, I'd love to read them. [Seriously!]

For me, sitting on the 'outside' observing the debates, I can't help but
think of the Stroustrup quote about C in general...

"C has its problems, ... [but] we know C's problems."

One of those problems is the use of unprototyped functions. Another is the
inherent weakness of type weak languages. C programmers have dealt
relatively comfortably with both for decades.
 
P

Peter Nilsson

I misremembered... va_list ap = { 0 };
This code gives ap a known value,

What _value_ would that be? What is the benefit of this known value (sic)?
which is useful in ensuring that the
program is deterministic.

Um, how does _not_ initialising va_lists make a program less or
non-deterministic?
i.e. it prevents (or rather, helps to prevent)
the program's behaviour from being unpredictable.

That's a Good Thing.

It's a style thang! ;)
This shows that the loop is "intentionally left blank" to use that awful
phrase, and thus has a certain self-documenting value.

'Self-documenting' is how some malloc casters describe the malloc cast.
Irrespective of 'cost', if a redundant token 'adds' something to the above
code in your mind, then couldn't a redundant cast also 'add' something in
the minds of some other C programmers?
On balance, I find that the benefits outweigh the costs.

That is _your_ finding, in _this_ case. Yet I've yet to see yourself
vehemently thrusting this as the One True Style on newbies, let alone other
similarly competent C experts who have disagreed with your style.


So, quite seriously I ask: how is the situation different for malloc
casting?

The pros and cons of either side have been stated. I simply want to know why
some clc regulars find it an almost pathological urge to convert the pagens,
so to speak, when the issue is nothing more than a balance of benefits v
costs?

I'm quite sure that casted malloc has caused problems in more than one
non-trivial program written by one or more experts. I'm just not convinced
that disaster is the forgone inevitability that it's portrade as. I'm quite
sure that other quirks of C have caused considerably more problems,
considerably more often.
Form follows function.

Interesting. Would you say that void * and its associated deliberate type
weakness was a _good_ choice for the C language? Would you say it was the
_best_ choice?
 
M

Martin Dickopp

Peter Nilsson said:
'Self-documenting' is how some malloc casters describe the malloc
cast. Irrespective of 'cost', if a redundant token 'adds' something to
the above code in your mind, then couldn't a redundant cast also 'add'
something in the minds of some other C programmers?

If it does, then why stop with `malloc'? By that logic, every expression
(including every subexpression it might be composed of) should be cast
to its type... ;)

Martin
 
E

Eric Sosman

Malcolm said:
[...]
With the exception of casts from floating point types to integers, C casts
are simple reinterpretations of bits. If the bit pattern doesn't make sense
for the type you are casting to, you will get garbage results and the
compiler won't warn about them.

Nonsense. Here are a few examples of casts that are
*not* mere "reinterpretations of bits" on most implementations:

(long)0
`0' has type `int', which might have as few as sixteen
bits. `(long)0' has type `long', which has at least
thirty-two. What sort of "reinterpretation" created
the additional bits?

(double)1u
`1u' has type `unsigned int', and exactly one of its
bits is non-zero. `(double)1u' has type `double', and
the number of non-zero bits is unspecified. How did
"reinterpretation" arrive at different values for
corresponding bit positions?

(float)1.0
`1.0' has type `double', usually represented as a 64-
bit value. `(float)1.0' has type `float', usually
represented in 32 bits. How did "reinterpretation"
destroy 32 bits?

(double*)NULL
This one's trickier, since the Standard doesn't specify
the type of the expression `NULL'. However, one of the
allowed types for `NULL' is `void*', and on a platform
where this is so it may also be the case that a `void*'
value has more bits than a `double*'. On a platform
where `NULL' has type `int', it is not necessarily the
case that a `double*' has the same number of bits as
an `int', nor that all the bits of `(double*)NULL' are
zeros. See Question 5.17 in the comp.lang.c FAQ --
in fact, read all of Section 5; it will do you good.

A cast is an operator, just like `+' or `%'. Almost all[*]
C operators work with the values of their operand or operands,
and none[**] works with the representations.

[*] The only operators I can think of that do not use their
operands' values are `sizeof' and unary `&', but perhaps
I've overlooked something.

[**] Perhaps it's debatable, but I consider this true even of
the bitwise operators: binary `&', `|', and `^'. They
are described as operating on the binary digits of their
operand values, but that's a convenience, an aid to
clarity of exposition. In particular, note that they are
not defined to operate on any "padding bits," which are
part of the representation (if present), but not part of
the value.

C programmers are often tempted to think about the representations
of the values their programs manipulate. IMHO this is a temptation
worth resisting: if you concentrate on the values themselves and
let the implementation figure out how to represent them, you will
be a happier programmer in the long run.
 
P

Peter Pichler

Martin Dickopp said:
If it does, then why stop with `malloc'? By that logic, every expression
(including every subexpression it might be composed of) should be cast
to its type... ;)

Of ourse (given short x),
x = (short)(x + 42);
is _much_ clearer than
x += 42;
At least to my compiler, that is. Otherwise it whines about possible loss
of data in the latter case ;-)

Peter
 
R

Richard Heathfield

Peter said:
I misremembered... va_list ap = { 0 };
Yup.


What _value_ would that be? What is the benefit of this known value (sic)?

Good question. :)

Your followup makes it clear that your choice of va_list wasn't random, so I
went to have a look at a va_list-using function I wrote for my CLINT
library, some months ago.

Here's the first part of the function, warts and all:

void wnn_Log(unsigned char wnn_Depth,
unsigned long wnn_LogFlags,
const char *wnn_Owner,
const char *wnn_Library,
const char *wnn_Filename,
const char *wnn_Function,
int wnn_Line,
const char *wnn_Format,
...)
{
static unsigned long LineNumber = 0;
va_list ap;
assert(wnn_GlobalConfig != NULL);
va_start(ap, wnn_Format);

Clearly, I agree with you on this occasion! :)

Um, how does _not_ initialising va_lists make a program less or
non-deterministic?

In the more general case, however, it's a useful hobbit I'll leave that typo
there for what little amusement value it has but I meant to type that it's
a useful /habit/ to get into.
It's a style thang! ;)

You have said so. :)
'Self-documenting' is how some malloc casters describe the malloc cast.

Yes. I'm not convinced by that argument, however. Otherwise, surely instead
of:
printf("%f\n", d);

they'd write:

(void)(int)((int(*)(const char *, ...))printf((const char *)"%f\n",
(double)d));

Yes, you could argue that that's self-documenting, but it's hardly clearer,
is it?
Irrespective of 'cost', if a redundant token 'adds' something to the above
code in your mind, then couldn't a redundant cast also 'add' something in
the minds of some other C programmers?

But the cast isn't redundant - that is, code with it does not have the same
meaning as code without it. An explicit conversion is performed that
otherwise would not be performed.
That is _your_ finding, in _this_ case.

That's right.
Yet I've yet to see yourself
vehemently thrusting this as the One True Style on newbies, let alone
other similarly competent C experts who have disagreed with your style.

That's for the very obvious reason that there are advantages and
disadvantages to both styles. Chris Torek rightly points out that it's a
close call. The malloc thing isn't.
Interesting. Would you say that void * and its associated deliberate type
weakness was a _good_ choice for the C language?
Yes.

Would you say it was the
_best_ choice?

I can't think of a better one off-hand (although I wish it also applied to
function pointers, and that void ** had the same kinds of guarantees that
come with void *).
 

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,141
Messages
2,570,813
Members
47,357
Latest member
sitele8746

Latest Threads

Top