understanding enum type

A

arnuld

From section 6.2.5, point number 16 in (n1256.pdf):

An enumeration comprises a set of named integer values. Each distinct
enumeration constitutes a different enumerated type.

Now I don' get this. Does it say 2 different enum types are different ?
Practically, they are not:


#include <stdio.h>


enum RET_VALUES_1 { VAL_FAIL = 0 };
enum RET_VALUES_2 { VAL_FALSE = 0 };


int main(void)
{
enum RET_VALUES_1 j1 = VAL_FAIL;
enum RET_VALUES_2 j2 = VAL_FALSE;

if(j1 == j2)
{
printf("oops! 2 enum types are equal :-/ \n");
}


return 0;
}
================== OUTPUT ======================
[arnuld@dune programs]$ gcc -ansi -pedantic -Wall -Wextra enum.c
[arnuld@dune programs]$ ./a.out
oops! 2 enum types are equal :-/
[arnuld@dune programs]$
 
J

Joachim Schmitz

arnuld said:
From section 6.2.5, point number 16 in (n1256.pdf):

An enumeration comprises a set of named integer values. Each distinct
enumeration constitutes a different enumerated type.

Now I don' get this. Does it say 2 different enum types are different
? Practically, they are not:


#include <stdio.h>


enum RET_VALUES_1 { VAL_FAIL = 0 };
enum RET_VALUES_2 { VAL_FALSE = 0 };


int main(void)
{
enum RET_VALUES_1 j1 = VAL_FAIL;
enum RET_VALUES_2 j2 = VAL_FALSE;

if(j1 == j2)
{
printf("oops! 2 enum types are equal :-/ \n");
}


return 0;
}
================== OUTPUT ======================
[arnuld@dune programs]$ gcc -ansi -pedantic -Wall -Wextra enum.c
[arnuld@dune programs]$ ./a.out
oops! 2 enum types are equal :-/
[arnuld@dune programs]$

The types are different, their values are not.

char c = 0;
int i = 0;

if ( i == c )
printf("oops!, two integer types, char and int, are equal???\n");

Bye, Jojo
 
K

Keith Thompson

arnuld said:
From section 6.2.5, point number 16 in (n1256.pdf):

An enumeration comprises a set of named integer values. Each distinct
enumeration constitutes a different enumerated type.

Now I don' get this. Does it say 2 different enum types are different ?
Practically, they are not:
[...]

enum RET_VALUES_1 { VAL_FAIL = 0 };
enum RET_VALUES_2 { VAL_FALSE = 0 };
[snip]

The fact that two types can be implicitly converted to one another
doesn't imply that the they're the same type. There are implicit
conversions between any two arithmetic types.

One way that the fact that two arithmetic types are distinct shows up is
that pointers to them are distinct, and there are no implicit
conversions between distinct pointer types (other than void*).

enum RET_VALUES_1 *p1;
enum RET_VALUES_2 *p2;
p1 = p2; /* constraint violation */
 
E

Eric Sosman

From section 6.2.5, point number 16 in (n1256.pdf):

An enumeration comprises a set of named integer values. Each distinct
enumeration constitutes a different enumerated type.

Now I don' get this. Does it say 2 different enum types are different ?

Yes.
Practically, they are not:


#include<stdio.h>


enum RET_VALUES_1 { VAL_FAIL = 0 };
enum RET_VALUES_2 { VAL_FALSE = 0 };
[...]

All your program has shown is that both the enum types can
represent the value zero, and that instances of the two types can
be compared. Big deal: `unsigned char' and `long double' can both
represent zero and can be compared; do you think they are alike?

Try continuing this way:

void func(enum RET_VALUES_1);
void (*fptr)(enum RET_VALUES_2) = func;
 
A

arnuld

All your program has shown is that both the enum types can
represent the value zero, and that instances of the two types can be
compared. Big deal: `unsigned char' and `long double' can both
represent zero and can be compared; do you think they are alike?

Okay, one more C-truth that supports the fact that it really takes 10
years to master C

Try continuing this way:

void func(enum RET_VALUES_1);
void (*fptr)(enum RET_VALUES_2) = func;


[arnuld@dune programs]$ gcc -ansi -pedantic -Wall -Wextra enum.c
enum.c:9: warning: initialization from incompatible pointer type
/tmp/cclJPM48.o:(.data+0x0): undefined reference to `func'
collect2: ld returned 1 exit status
[arnuld@dune programs]$


If I use another declaration like:

void (*fptr2)(enum RET_VALUES_1) = func;

then I don't get incompatible pointer type warning. I understood that
they are different types and can hold same values.

Still, I never used function pointers(partly because I am scared of them
and partly because I never knew (still don't know) what they are useful
for). This reminds of a real life scenario (though OT) I have right now
in my place of work:


I have a function-1 and inside of it there is a call to function-A:

function-1() { .... fucntion-A() ....}

I have written 3 functions like this:

function-2() { ..... fucntion-B() .....}
function-3() { ..... function-C() .....}


Now the difference between all these function-1,2,3 is that only function
calls are different, rest 99^ of coding is same. Are function pointers
useful here ?
 
I

Ike Naar

I have a function-1 and inside of it there is a call to function-A:

function-1() { .... fucntion-A() ....}

I have written 3 functions like this:

function-2() { ..... fucntion-B() .....}
function-3() { ..... function-C() .....}

Now the difference between all these function-1,2,3 is that only function
calls are different, rest 99^ of coding is same. Are function pointers
useful here ?

Possibly. You could replace function-1, function-2 and function-3 by
a single function, say, function-N, that takes the inner function to be
called (function-A etcetera) as a parameter.

function-N(void (*callee)()) { ... (*callee)() ... }

Then, instead of

function-1()
function-2()
function-3()

use

function-N(&function-A)
function-N(&function-B)
function-N(&function-C)
 
B

BartC

Ike Naar said:
Possibly. You could replace function-1, function-2 and function-3 by
a single function, say, function-N, that takes the inner function to be
called (function-A etcetera) as a parameter.

function-N(void (*callee)()) { ... (*callee)() ... }

Then, instead of

function-1()
function-2()
function-3()

use

function-N(&function-A)
function-N(&function-B)
function-N(&function-C)

The caller of function-1, function-2 and so on may not know that each will
call function-A, function-B, etc in turn, if the function bodies are not
visible.

This is a 'problem' for the implementer of function-'n' to sort out.
Presumably, since there are only 3 functions, the object is avoid
replicating a possible large body of code in each.

Possibly, use your function-N() scheme, the caller still calls
function-1/2/3(), but each of those now consists of code such as:

function-2(){ function-N(&function-B);}

Then the same interface is preserved.
 
B

BartC

arnuld said:
Okay, one more C-truth that supports the fact that it really takes 10
years to master C

That's my feeling too; on the face of it, C seems quite simple..

Anyway, these different types all have a slightly different version of zero,
for example 0 is an int, and 0.0 is a double (or is it a float); but C
allows you to compare different numeric types by automatically converting
one to the more dominant type.

In the case of enums, I think their values have to be ints, so in:

enum Z{zero=0};

Then zero may be an int value, or an int literal (I'm not going to wade
through 700 pages of the standard to find out exactly what). It is anyway
not what you might expect (a Z-literal or whatever).
 
E

Eric Sosman

Okay, one more C-truth that supports the fact that it really takes 10
years to master C

If it takes ten years to learn that the value zero can be
represented in several different ways, perhaps you're in the wrong
line of work. :)
Try continuing this way:

void func(enum RET_VALUES_1);
void (*fptr)(enum RET_VALUES_2) = func;


[arnuld@dune programs]$ gcc -ansi -pedantic -Wall -Wextra enum.c
enum.c:9: warning: initialization from incompatible pointer type

And why are they incompatible? Because RET_VALUES_1 and
RET_VALUES_2 are not the same type, that's why. Point made.
(Keith Thompson's example is a clearer one, though.)
I have a function-1 and inside of it there is a call to function-A:

function-1() { .... fucntion-A() ....}

I have written 3 functions like this:

function-2() { ..... fucntion-B() .....}
function-3() { ..... function-C() .....}

Now the difference between all these function-1,2,3 is that only function
calls are different, rest 99^ of coding is same. Are function pointers
useful here ?

Quite possibly, if function-{A,B,C} have the same parameter
lists and return types. (If they disagree it may be *possible*,
but will certainly be clumsier.) Illustration, concretized just
a little bit for clarity:

/* The three functions you might want to call: */
void function_A(double);
void function_B(double);
void function_C(double);

/* A type that can point to any of them: */
typedef void (*FuncPtr)(double);

/* The omnibus caller function: */
void function_123(double x, FuncPtr func) {
...
func(x); /* some prefer `(*func)(x)'; no difference */
...
}

/* A few calls to the omnibus caller: */
int main(void) {
function_123(3.14, function_A);
function_123(2.78, function_B);
function_123(42.0, function_C);
return 0;
}

You could write it without the typedef, if desired, but I think
typedeffing function pointers can be an aid to clarity. (Typedeffing
other kinds of pointers is usually the opposite, IMHO; YMMV.)
 
K

Keith Thompson

BartC said:
In the case of enums, I think their values have to be ints, so in:

enum Z{zero=0};

Then zero may be an int value, or an int literal (I'm not going to wade
through 700 pages of the standard to find out exactly what). It is anyway
not what you might expect (a Z-literal or whatever).

There's no need to wade through the whole standard, just read 6.7.2.2,
"Enumeration specifiers". It's not 100% obvious that this is going to
be under 6.7.2 "Type specifiers", which is under 6.7 "Declarations", but
looking up "enum" in the index would take you there.

(An aside: I wish that the index entries in the PDF versions of the
standard were clickable links to the referenced sections; PDF supports
that kind of thing, doesn't it?)

Given the declaration
enum Z { zero = 0 };
the identifier ``zero'' is a constant of type int. This is
admittedly somewhat counterintuitive (you might expect it to be
of type enum Z), but there are historical reasons. And in most
contexts, given C's rather promiscuous implicit conversions, it
doesn't matter.
 
K

Keith Thompson

arnuld said:
Okay, one more C-truth that supports the fact that it really takes 10
years to master C
[...]

You may be right about how long it takes to master C (certainly
one can adjust the qualifications for mastery to match any desired
time scale), but this particular C-truth is not all that complex.
I think the use of function pointers in the demonstration may have
made it seem more complex than it really is.

The fundamental idea is that all arithmetic types (that includes
integer, floating-point, enumeration, _Bool, and complex types)
may be implicitly converted to each other. In assignment, given
``x = y'', y is converted to the type of x; similar conversions
are done for initialization and parameter passing. For most binary
operators, ``x + y'', both operands are converted to a common type
before the operation is applied. But in spite of all these implicit
conversions, they are still distinct types.

Because C performs implicit conversions at the drop of a hat, it
can be a little difficult to construct an example where the fact
that x and y are of distinct types actually matters.

Types derived from distinct types are themselves distinct,
and may not be implicitly convertible. Thus pointer types or
pointer-to-function types can be used to demonstrate that two
arithmetic types are distinct:

enum foo x1;
int x2;
x1 = x2; /* ok, implicit conversion */

enum foo *p1;
int *p2;
p1 = p2; /* constraint violation, no implicit conversion */
 
K

Keith Thompson

Eric Sosman said:
Quite possibly, if function-{A,B,C} have the same parameter
lists and return types. (If they disagree it may be *possible*,
but will certainly be clumsier.) Illustration, concretized just
a little bit for clarity:

/* The three functions you might want to call: */
void function_A(double);
void function_B(double);
void function_C(double);

/* A type that can point to any of them: */
typedef void (*FuncPtr)(double);

/* The omnibus caller function: */
void function_123(double x, FuncPtr func) {
...
func(x); /* some prefer `(*func)(x)'; no difference */
...
}

/* A few calls to the omnibus caller: */
int main(void) {
function_123(3.14, function_A);
function_123(2.78, function_B);
function_123(42.0, function_C);
return 0;
}

You could write it without the typedef, if desired, but I think
typedeffing function pointers can be an aid to clarity. (Typedeffing
other kinds of pointers is usually the opposite, IMHO; YMMV.)

You can also typedef the function type itself:

/* The three functions you might want to call: */
void function_A(double);
void function_B(double);
void function_C(double);

/* The type of the functions: */
typedef void Func(double);

/* The omnibus caller function: */
void function_123(double x, Func *func) {
/* ... */
func(x); /* some prefer `(*func)(x)'; no difference */
/* ... */
}

/* A few calls to the omnibus caller: */
int main(void) {
function_123(3.14, function_A);
function_123(2.78, function_B);
function_123(42.0, function_C);
return 0;
}
 
L

lawrence.jones

Keith Thompson said:
(An aside: I wish that the index entries in the PDF versions of the
standard were clickable links to the referenced sections; PDF supports
that kind of thing, doesn't it?)

Yes, it does. Unfortunately, not all of groff (which is what I'm using
to format the standard) has been updated to support it and even the
basic formatting is broken in the most recent releases. Now that the
content is mostly frozen, I'm hoping to find some time to work on the
formatting infrastructure.
 
J

Johannes Schaub (litb)

Keith said:
arnuld said:
Okay, one more C-truth that supports the fact that it really takes 10
years to master C
[...]

You may be right about how long it takes to master C (certainly
one can adjust the qualifications for mastery to match any desired
time scale), but this particular C-truth is not all that complex.
I think the use of function pointers in the demonstration may have
made it seem more complex than it really is.

The fundamental idea is that all arithmetic types (that includes
integer, floating-point, enumeration, _Bool, and complex types)
may be implicitly converted to each other. In assignment, given
``x = y'', y is converted to the type of x; similar conversions
are done for initialization and parameter passing. For most binary
operators, ``x + y'', both operands are converted to a common type
before the operation is applied. But in spite of all these implicit
conversions, they are still distinct types.

Because C performs implicit conversions at the drop of a hat, it
can be a little difficult to construct an example where the fact
that x and y are of distinct types actually matters.

Types derived from distinct types are themselves distinct,
and may not be implicitly convertible. Thus pointer types or
pointer-to-function types can be used to demonstrate that two
arithmetic types are distinct:

enum foo x1;
int x2;
x1 = x2; /* ok, implicit conversion */

enum foo *p1;
int *p2;
p1 = p2; /* constraint violation, no implicit conversion */

This is not necessarily true. Pointer types only require that the pointee
types are *compatible*. They don't require that the types are the same. So
if it happens that the compiler choose "int" as the compatible type of the
enumeration, then "p1 = p2;" is alright. Type compatibility isn't
transitive, thus "enum foo2 *p2 = p1;" still won't work, even when foo2
would use the same compatible underlying type.

That's the same reason as to why the following will work

int b[2];
int (*a)[] = &b; // int[] compatible to int[2]
 
A

arnuld

You can also typedef the function type itself:

/* The three functions you might want to call: */ void
function_A(double);
void function_B(double);
void function_C(double);

/* The type of the functions: */
typedef void Func(double);

/* The omnibus caller function: */
void function_123(double x, Func *func) {
/* ... */
func(x); /* some prefer `(*func)(x)'; no difference */ /* ... */
}

/* A few calls to the omnibus caller: */ int main(void) {
function_123(3.14, function_A);
function_123(2.78, function_B);
function_123(42.0, function_C);
return 0;
}


It does work:

#include <stdio.h>

typedef void Func(double*);

void implicit_to_char(double*);
void implicit_to_int(double*);
void implicit_to_double(double*);

void omnibus_fn(double*, Func* func);

int main(void)
{
double d = 32.2;
double* p = &d;

omnibus_fn(p, implicit_to_char);
omnibus_fn(p, implicit_to_int);
omnibus_fn(p, implicit_to_double);

return 0;
}


void omnibus_fn(double* ptr, Func* fnptr)
{
(*fnptr)(ptr);
fnptr(ptr);
}



void implicit_to_char(double* ptr)
{
if(' ' == (char)*ptr) printf("SPACE\n");
else printf("char = %c\n", (char)*ptr);
}



void implicit_to_int(double* ptr)
{
printf("int = %d\n", (int)*ptr);
}


void implicit_to_double(double* ptr)
{
printf("double = %f\n", *ptr);
}



============================== OUTPUT ================================
[arnuld@dune programs]$ gcc -ansi -pedantic -Wall -Wextra function-
pointers.c
[arnuld@dune programs]$ ./a.out
SPACE
SPACE
int = 32
int = 32
double = 32.200000
double = 32.200000
[arnuld@dune programs]$



Actually its only after looking at ASCII Table came to know why it was
not printing any character if double has value either less than 33 or
more than 127.

What if number of arguments passed to 3 functions are different:

function A(double, int);
function B(double, int, int, char);
function C(char, char);

In this case can we write any omnibus call function to call these 3
functions ?
 
E

Eric Sosman

[... using function pointers ...]
What if number of arguments passed to 3 functions are different:

function A(double, int);
function B(double, int, int, char);
function C(char, char);

ITYM function_A, function_B, function_C.
In this case can we write any omnibus call function to call these 3
functions ?

As I mentioned earlier, this case is much clumsier. The problem
is that the call to a function -- by which I mean the stuff that rounds
up the arguments, transmits them to the parameters, and eventually
collects the returned value -- all this is fixed immutably at compile
time and can't be changed at run-time. Since the three functions you
show have different "signatures," they can't all be invoked by the
same function call. (The Standard uses somewhat different language to
express the restriction, but that's the gist.)

So to invoke these three dissimilar functions you need three
different calls in your source code, along with some way to figure
out which of the calls to execute. You *could* do this with a function
pointer plus some if's and things and a bunch of ugly casts, but it's
messy. One special case might not be *too* bad:

void omnibus(
void (*afunc)(double, int),
void (*bfunc)(double, int, int, char),
void (*cfunc)(char, char),
/* other arguments */ )
{
...
if (afunc != NULL)
afunc(42.0, 42);
...
if (bfunc != NULL)
bfunc(42.0, 42, -42, '\042');
...
if (cfunc != NULL)
cfunc('4', '2');
...
}

.... but even this is on the wrong side of "convenient."
 

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

Parsing a string 44
Finding a word inside a string 35
using a pointer vs array 12
Pointers and Sequence Point 12
Const Issue 2
const problem 1
selection-sort in C 22
Difference between an initialized and uninitialized Array 5

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top