Type of variable

J

James Kuyper

Nick said:
I can't see how you can be unaware of the type of a variable
in normal C. There must be a declaration/definition in scope.
Unless gcc has added some sort of anonymous type

Lets say the code has #included a standard header that defines size_t.
How would you determine whether or not it's a typedef for unsigned long?
You could check the header file (if it is in fact available as a
separate file - this is not required) or by reading the implementation's
documentation. However, it's not something your code itself could determine.

for instance, consider you've got a function that expects a pointer to
an array of unsigned long, and the program has an array of size_t. If
size_t is a typedef for unsigned long, it can just pass a pointer to the
original array. If not, it will have to take the slower approach of
copying each element of the first array into a second array of unsigned
long, and then pass a pointer to the second array to the function. In
code intended to be portable, I'd like it to use the faster method when
possible, and the slower method if necessary - but C provides no way of
determining this with certainty.

If the two types have the same size it is a pretty good indicator that
they might be the same. Having SIZE_MAX and ULONG_MAX be the same value
would be an even stronger clue. But even if both of those features
match, in principle it's possible that one is a big-endian type, and the
other is a little-endian type, or some other variation in the order of
the value bits.

In the most recent case where I've had to worry about something like
this, it was a third-party typedef (which varied depending upon which
platform the third-party library was installed on), so I didn't even
have *_MAX macros to rely upon, even if *_MAX would have been sufficient
to resolve the question.
void print (ANON x)
{
switch (typeof(x))
{
case int:
printf ("%d", (int)x);
break;

...etc...
}
}

this seems to do some violence to the fundamentals of C

As I understand it, the typeof which is a common extension to C can be
used only in the same places that a type name can be used; hence, not in
a switch statement. Therefore, it's not sufficient for the purpose I
outlined above. The C++ typeid operator, on the other hand, does return
a value of type std::typeinfo that supports equality comparisons. This
isn't suitable for switch(), but can be used in an extended if-else series.
 
T

Tom St Denis

No, the preprocessor doesn't implement typeof() any more than it implements
sizeof(). It just happens to be most useful in type-generic macros.

When I say

  int i, j;
  SWAP(i,j);

the preprocessor's output is

  int i, j;
  do { typeof(i) tmp; tmp=i; i=j; j=tmp; } while(0);

You could do the same with

do { void *p = malloc(sizeof i); if (p) { memcpy(p, i, sizeof i);
memcpy(i, j, sizeof i); memcpy(j, p, sizeof j); free(p); } else { fail
(__LINE__); } } while (0);

Which is better since it doesn't involve putting potentially large
objects on the stack, can handle any type with normal C syntax, etc...

Of course I'd just write that as a function

void swap(void *a, void *b, size_t length);

Tom
 
O

osmium

Nick said:
unsurprisingly I disagree. There *is* no way to find type at runtime
in C. C++ has ways to do this. So do some other languages with
run-time type systems. C is statically typed so you can't do this.
The normal thing to is to do as much type checking at compile time
as possible. So I asked the OP why he wanted to do this, in the hope
of getting some insight into why he was doing this. This might
have led me to some suggestion as to how he avoid trying to
do the impossible. For instance void* + tag or union + tag or
use C's type system correctly.

etc. I fail to see how my question was dumb enough to encourage
your sarky answer.

Perhaps I am being more literal than you. The OP asked *why* and your
response (now) has to do with the feasibility of doing so. If you had
prefaced your answer with "You can't do that", I would have remained silent.
Sorry for the sarky answer.
 
N

Nobody

You could do the same with

do { void *p = malloc(sizeof i); if (p) { memcpy(p, i, sizeof i);
memcpy(i, j, sizeof i); memcpy(j, p, sizeof j); free(p); } else { fail
(__LINE__); } } while (0);

Which is better since it doesn't involve putting potentially large
objects on the stack, can handle any type with normal C syntax, etc...

That's a rather novel meaning of "better". About the only situation where
malloc() is a good choice is when it's the only choice, e.g. if you
can't put a reasonable upper bound on the memory at compile time, or if
you don't know the lifetime.

If I couldn't use typeof(), I'd either use alloca() or pass the type as an
argument. Using malloc() for a value whose size is fixed at compile time
(as either a primitive type or a struct) and whose lifetime is typically
around three instructions is gratuitous.
 
B

Ben Pfaff

Tom St Denis said:
You could do the same with

do { void *p = malloc(sizeof i); if (p) { memcpy(p, i, sizeof i);
memcpy(i, j, sizeof i); memcpy(j, p, sizeof j); free(p); } else { fail
(__LINE__); } } while (0);

Ugh.

Why not:

unsigned char tmp[sizeof i];

/* Check sizes are the same. */
assert(sizeof i == sizeof j);

/* Provoke warning (typically) if types are different. */
(void) (&i == &j);

memcpy(tmp, &i, sizeof i);
memcpy(&i, &j, sizeof i);
memcpy(&j, tmp, sizeof i);
 
T

Tom St Denis

Tom St Denis said:
On Aug 17, 6:44 am, (e-mail address removed) (Alan Curry) wrote:
You could do the same with
do { void *p = malloc(sizeof i); if (p) { memcpy(p, i, sizeof i);
memcpy(i, j, sizeof i); memcpy(j, p, sizeof j); free(p); } else { fail
(__LINE__); } } while (0);

Ugh.

Why not:

  unsigned char tmp[sizeof i];

  /* Check sizes are the same. */
  assert(sizeof i == sizeof j);

  /* Provoke warning (typically) if types are different. */
  (void) (&i == &j);

  memcpy(tmp, &i, sizeof i);
  memcpy(&i, &j, sizeof i);
  memcpy(&j, tmp, sizeof i);

Because sometimes stack space is more precious than heap space.

And to answer "nobody's" question... Sometimes structs are non-
trivial. I routinely have structs that have temp buffers in them
ranging from 64 to 512 bytes in length. I'd rather malloc the struct
than place it on the stack because you don't know how deep you are by
time you've been called.

Tom
 
J

jameskuyper

Tom said:
On Aug 17, 6:44 am, (e-mail address removed) (Alan Curry) wrote: ....

You could do the same with

do { void *p = malloc(sizeof i); if (p) { memcpy(p, i, sizeof i);
memcpy(i, j, sizeof i); memcpy(j, p, sizeof j); free(p); } else { fail
(__LINE__); } } while (0);

Which is better since it doesn't involve putting potentially large
objects on the stack, can handle any type with normal C syntax, etc...

Whether it's better depends upon the context. It's a horribly
inefficient way to swap just about anything small, for some
implementation-dependent value of "small". When typeof() is available
(and keep in mind that this is an argument for making it more widely
available), the SWAP macro is far more efficient for such objects.
 
B

Ben Pfaff

Tom St Denis said:
Because sometimes stack space is more precious than heap space.

In that case, it might be better to make the choice conditional
on the size of the data.
 
T

Tom St Denis

Whether it's better depends upon the context. It's a horribly
inefficient way to swap just about anything small, for some
implementation-dependent value of "small". When typeof() is available
(and keep in mind that this is an argument for making it more widely
available), the SWAP macro is far more efficient for such objects.

Oh no doubt, but as a "hey lets all use this one construct" it's
definitely not the safest way to go. I'd rather start with the slower/
safe route and then optimize down to the select case, then work
outwards from something that doesn't work.

Tom
 
N

Nick Keighley

Perhaps I am being more literal than you.  The OP asked *why* and your
response (now) has to do with the feasibility of doing so.  If you had
prefaced your answer with "You can't do that", I would have remained silent.
Sorry for the sarky answer

By my reading he said "how" not "why".
and, I prefaced my question with "no."

As to feasability. Well you can do even impossible things in C (for
small
values of impossible). You could implement a run-time type system.

http://tinyscheme.sourceforge.net/home.html
:)

You could pass around tagged values

struct Tagged
{
enum Type type;
void *data;
};

or

struct Tagged
{
enum Type type;
union /* beware I don't use unions so I don't know the syntax */
{
int i;
double d;
};
};

Or his problem may be soluble using C's existing type system.
Or he may want gcc's typeof extension (else-thread)

But I can't point him in the way of these things if I don't
know why he wants the type of a variable.

Sorry for earlier grumpiness.
 
N

Nick Keighley

[...] About the only situation where
malloc() is a good choice is when it's the only choice, e.g. if you
can't put a reasonable upper bound on the memory at compile time, or if
you don't know the lifetime.

or (and I know you were't trying to be exhaustive) your stack
is small but your heap is large.
 
N

Nick Keighley

[...] Sometimes structs are non-
trivial.  I routinely have structs that have temp buffers in them
ranging from 64 to 512 bytes in length.  I'd rather malloc the struct
than place it on the stack because you don't know how deep you are by
time you've been called.

I once saw someone pass an 11k struct by value when he meant
to pass it by reference. The stack was 4k. Messy.
 
L

luserXtrog

struct Tagged
{
    enum Type type;
    union /* beware I don't use unions so I don't know the syntax */
    {
        int i;
        double d;
    }

u /* unless your compiler has an anonymous union extension,
you need to give the union variable a name in order to access
its component variables */

;
 
N

Nick Keighley

u /* unless your compiler has an anonymous union extension,
you need to give the union variable a name in order to access
its component variables */

ah. Something I should have remembered about unions
 
R

Richard Bos

Nick Keighley said:
I once saw someone pass an 11k struct by value when he meant
to pass it by reference. The stack was 4k. Messy.

Myeah, but let's be fair, that's a QOI issue. The implementation knows
that there's only 4k of stack, so it should never have tried to pass
that entire struct on that stack. It's allowed by the Standard to work
around such limitations, and though it's not required to, as a matter of
quality it should.

Richard
 
K

Keith Thompson

Myeah, but let's be fair, that's a QOI issue. The implementation knows
that there's only 4k of stack, so it should never have tried to pass
that entire struct on that stack. It's allowed by the Standard to work
around such limitations, and though it's not required to, as a matter of
quality it should.

The implementation might know, but the compiler easily might not.
It could be generating code that could run on systems with a variety
of stack sizes, and plenty of implementations don't do run-time stack
checks. (This all assumes the existence of a "stack", of course.)
 
N

Nick Keighley

The implementation might know, but the compiler easily might not.
It could be generating code that could run on systems with a variety
of stack sizes, and plenty of implementations don't do run-time stack
checks.  (This all assumes the existence of a "stack", of course.)

the stack size was specified when the program was started.
 
J

jameskuyper

Nick said:
the stack size was specified when the program was started.

Perhaps, though that need not be the case; an implementation where the
size of the stack changes with time is certainly possible; I've used
implementations where the stack and the heap both used the same block
of memory, and if too much heap space was used, the stack space would
become extremely limited.

In any event, "when the program was started" refers to a time that is
typically well after the time the compiler has finished running, so it
is not something that the compile necessarily knows about.
 
N

Nick Keighley

Perhaps, though that need not be the case;

it was actually the case. It just wasn't expected that large
structures would be passed on the stack. It was a typo that it
happened.
an implementation where the
size of the stack changes with time is certainly possible; I've used
implementations where the stack and the heap both used the same block
of memory, and if too much heap space was used, the stack space would
become extremely limited.

In any event, "when the program was started" refers to a time that is
typically well after the time the compiler has finished running, so it
is not something that the compile necessarily knows about.

I know
 
R

Richard Bos

Keith Thompson said:
The implementation might know, but the compiler easily might not.
It could be generating code that could run on systems with a variety
of stack sizes, and plenty of implementations don't do run-time stack
checks. (This all assumes the existence of a "stack", of course.)

*Shrug* If it is designed to generate code for such a restricted system,
I would consider it poor quality of service if it didn't check, _even_
if you were using a Frankenstein implementation. After all, only 4k of
stack, all by itself, screams "specialist application". It didn't 30
years ago, but today?

Richard
 

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

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,229
Latest member
GloryAngul

Latest Threads

Top