p124 K&R

M

mdh

Hi All,
I have a couple of questions regarding the discussions on page 124 of
k&r.

1)

This references functions working with complicated declarations in C.

before main(...,...){}, (in file main.c), an enum is defined thus.

enum {NAME, PARENS, BRACKETS};

int gettoken(){......}


refers to this with a return line thus.

return tokentype = PARENS;

Now, I am assuming that gettoken and main are compiled in the same
file?

I am trying to compile "gettoken" in my "foo.c" file. Hence I wish to
ref the enum from there.

I have tried this.

Giving the enum an identifier,

"enum syntax {NAME, PARENS, BRACKETS};"

and then declaring it in foo.c thus.

extern enum syntax; but this gives a warning "useless storage class
specifier in empty declaration "



2) staying with the "extern" theme.

A char array is defined in main.c

" char token[10]; "



Again referenced from foo.c using syntax "extern char token".

This in of itself does not give a warning, ( which I think is
expected) but this line does.

char *p = token " initialization makes pointer from integer without a
cast"

I would have thought that 'token' is changed to a ptr to char of the
first element in the array, so not quite sure what or if to try and
correct this.

Thanks in advance....and holding my head...there have been some testy
responses lately...so hopefully I do not fit those stereoptypes!!
 
M

mdh

Hi All,
I have a couple of questions regarding the discussions  on page 124 of
k&r.

1)

This references functions working with complicated declarations in C.

before  main(...,...){},  (in file main.c), an enum is defined thus.

enum {NAME, PARENS, BRACKETS};

int gettoken(){......}

refers to this with a return line thus.

return  tokentype = PARENS;

Now, I am assuming that gettoken and main are compiled in the same
file?

I am trying to compile "gettoken" in my "foo.c" file. Hence I wish to
ref the enum from there.

I have tried this.

Giving the enum an identifier,

 "enum syntax {NAME, PARENS, BRACKETS};"

and then declaring it in foo.c thus.

extern enum syntax;  but this gives a warning  "useless storage class
specifier in empty declaration "


Please ignore Q2...figured it out.
 
B

Barry Schwarz

Hi All,
I have a couple of questions regarding the discussions on page 124 of
k&r.

1)

This references functions working with complicated declarations in C.

before main(...,...){}, (in file main.c), an enum is defined thus.

enum {NAME, PARENS, BRACKETS};

int gettoken(){......}


refers to this with a return line thus.

return tokentype = PARENS;

Now, I am assuming that gettoken and main are compiled in the same
file?

As shown on pages 124 and 125, they must be.
I am trying to compile "gettoken" in my "foo.c" file. Hence I wish to
ref the enum from there.

In addition to the enum, you also need token, tokentype, string.h, and
ctype.h.
I have tried this.

Giving the enum an identifier,

"enum syntax {NAME, PARENS, BRACKETS};"

and then declaring it in foo.c thus.

extern enum syntax; but this gives a warning "useless storage class
specifier in empty declaration "

Even if this did not generate a diagnostic, while compiling foo.c what
would the compiler know about enum syntax? How would it know that
NAME was a synonym for 0, PARENS for 1, etc?
2) staying with the "extern" theme.

A char array is defined in main.c

" char token[10]; "



Again referenced from foo.c using syntax "extern char token".

This in of itself does not give a warning, ( which I think is
expected) but this line does.

char *p = token " initialization makes pointer from integer without a
cast"

I would have thought that 'token' is changed to a ptr to char of the
first element in the array, so not quite sure what or if to try and
correct this.

Your extern declaration did not say token was an array. It said token
was a char. If you had coded
extern char token[];
your initialization of p would have worked.
Thanks in advance....and holding my head...there have been some testy
responses lately...so hopefully I do not fit those stereoptypes!!

If you want to split the main and gettoken functions into different
source files (technically translation units), you have to insure that
every file scope declaration in main.c is visible in foo.c AND that
every file scope object in main.c is declared as extern in foo.c.

One way to do this is copy all the lines on page 124 from
#include <stdio.h>
through
int gettoken(void);
from main.c to foo.c. Then copy the lines from
int tokentype;
through
char out[1000];
and insert the storage class "extern" in front of each.

I don't like this because 1) any change in main.c must be duplicated
in foo.c and 2) the compiler cannot check it the two are consistent.

My preferred approach is MOVE the lines from
#include <stdio.h>
through
int gettoken(void);
to foo.h. Then COPY the lines from
int tokentype;
through
char out[1000];
and insert the storage class "extern" in front of each. Add
#include "foo.h"
to the front of both main.c and foo.c. Changes to the various
declarations need only appear in foo.h. During the compilation of
main.c, any inconsistencies between foo.h and the file scope objects
defined in main will be flagged.
 
M

mdh

In addition to the enum, you also need token, tokentype, string.h, and
ctype.h.

In reality, my "foo.c" and "foo.h" contain all the little functions I
have written as I have moved through k&r. This has helped me in
understanding C. ( So I have str_cmp, str_len etc etc and if I need a
new one, I have written it)...but, perhaps the time has come to move
on and use those libraries.

Even if this did not generate a diagnostic, while compiling foo.c what
would the compiler know about enum syntax?  How would it know that
NAME was a synonym for 0, PARENS for 1, etc?


Well, that is what I find a little confusing. On p 124, k&r use the
syntax "enum { NAME....etc}; without anything between the "enum" and
the "{".From the appendix I think this is legal...in fact, if it is in
K&R I assume it is legal. And like you, I also asked how foo.c could
thus know about it in main.c, hence I added a "name" (perhaps syntax
was a poor choice for a name)


.
A char array is defined in main.c
" char token[10]; "
char *p = token " initialization makes pointer from integer without a
cast"



Your extern declaration did not say token was an array.  It said token
was a char.  If you had coded
        extern char token[];
your initialization of p would have worked.


Point taken...
If you want to split the main and gettoken functions into different
source files (technically translation units), you have to insure that
every file scope declaration in main.c is visible in foo.c AND that
every file scope object in main.c is declared as extern in foo.c.

One way...........

I don't like this because 1) any change in main.c must be duplicated
in foo.c and 2) the compiler cannot check it the two are consistent.

If you don't like it, neither will I!!
My preferred approach is MOVE the lines from
        #include <stdio.h>
through
        int gettoken(void);


OK...so this includes " enum {NAME, ......};


to foo.h.  Then COPY the lines from
        int tokentype;
through
        char out[1000];
and insert the storage class "extern" in front of each.  Add
        #include "foo.h"
to the front of both main.c and foo.c.  Changes to the various
declarations need only appear in foo.h.  During the compilation of
main.c, any inconsistencies between foo.h and the file scope objects
defined in main will be flagged.

Barry...I actually think to a large extent I have been doing this all
along. But I could be wrong. I have all my definitions in foo.c, all
my declarations in foo.h ( include foo.h in foo.c) and include foo.h
in main.c The part that is new to me is the use of extern, but I
think I have am getting the hang of it.
One question though. If one includes enum {NAME...} in foo.h, is this
not a definition. I was under the impression, that definitions should
not be included in .h files. ( I guess the same question would apply
to #define MAXTOKEN 100.} Or are these simple pre-processor macros?
Sorry if I have misunderstood your answer and you have already
answered this.
Thanks
 
B

Barry Schwarz

My preferred approach is MOVE the lines from
        #include <stdio.h>
through
        int gettoken(void);


OK...so this includes " enum {NAME, ......};


to foo.h.  Then COPY the lines from
        int tokentype;
through
        char out[1000];
and insert the storage class "extern" in front of each.  Add
        #include "foo.h"
to the front of both main.c and foo.c.  Changes to the various
declarations need only appear in foo.h.  During the compilation of
main.c, any inconsistencies between foo.h and the file scope objects
defined in main will be flagged.

Barry...I actually think to a large extent I have been doing this all
along. But I could be wrong. I have all my definitions in foo.c, all
my declarations in foo.h ( include foo.h in foo.c) and include foo.h
in main.c The part that is new to me is the use of extern, but I
think I have am getting the hang of it.
One question though. If one includes enum {NAME...} in foo.h, is this
not a definition. I was under the impression, that definitions should
not be included in .h files. ( I guess the same question would apply
to #define MAXTOKEN 100.} Or are these simple pre-processor macros?
Sorry if I have misunderstood your answer and you have already
answered this.

In order for the enum declaration to define an object, you would have
to name the object as part of the declaration. Something along the
lines of
enum {...} x;
would define the object (variable) x of the enum type. The actual
type would be an implementation specific type of integer.

This is almost identical to the difference between the declaration
struct tag {...};
and the definition
struct tag {...} y;
The first declares the structure type and tells the compiler
everything it needs to know about that type and its members. The
second does all this plus defines an (aggregate) object of that type.

So your impression is correct. Only declarations (and macro
definitions) should be in header files. Your (actually K&R's)
original enum statement did not define an object and so is in keeping
with this general rule. If you look in 6.7.2.2-3, the standard
clearly states that the identifiers in the enumerator list (NAME, etc)
are declared (not defined) as constants of type int. The net effect
is almost identical to a sequence of macro definitions
#define NAME 0
#define PARENS 1
etc.
and no one thinks twice about including those multiple source files.
 
M

mdh

In order for the enum declaration to define an object, you would have
to name the object as part of the declaration.  Something along the
lines of
    enum {...} x;
would define the object (variable) x of the enum type.  ....
...snip...


Your (actually K&R's)
original enum statement did not define an object and so is in keeping
with this general rule.

So is it fair to say one can use enums in 2 general ways.

One...without an (enum) identifier in which case the list identifiers
are used as a substitute for simple #define statements.
Two...with an identifier, in which case one can declare a variable of
type "enum myenum" and assign to that variable one of the list
constants ?

In either case, if I wish to used that enum in another file ( ?
translation unit) I need to **declare** it either in the header or
before using it in that **other** function.


thanks in advance.
 
B

Barry Schwarz

So is it fair to say one can use enums in 2 general ways.

One...without an (enum) identifier in which case the list identifiers
are used as a substitute for simple #define statements.

I think this is true but I don't know if it is complete. What I mean
is that the enumerators appear to act the same as simple macro names
but I don't know if there are any other aspects. For example, if you
view an object of enum type in the debugger, do you see the integer
value or the name or the enumerator? With a macro the answer is
obviously the integer value but with an enumerator?
Two...with an identifier, in which case one can declare a variable of
type "enum myenum" and assign to that variable one of the list
constants ?

In either case, if I wish to used that enum in another file ( ?
translation unit) I need to **declare** it either in the header or
before using it in that **other** function.

Just like a macro name.
 
F

Flash Gordon

Barry Schwarz wrote, On 05/08/08 18:23:
I think this is true but I don't know if it is complete. What I mean
is that the enumerators appear to act the same as simple macro names
but I don't know if there are any other aspects. For example, if you
view an object of enum type in the debugger, do you see the integer
value or the name or the enumerator? With a macro the answer is
obviously the integer value but with an enumerator?

That all depends on the debugger. Some debuggers even know about macros
these days!

Another advantage of using an enum is that the identifiers then follow
"normal" C scoping unlike macros. Comnsider the following...

int foo() {
#define BUFSIZ 50
unsigned char buf[BUFSIZ];
....

int bar() {
enum { BUFSIZ=50 };
unsigned char buf[BUFSIZ];
....

Just like a macro name.

Indeed. Also like a typedef or struct definition.
 
M

mdh

I think this is true but I don't know if it is complete.

......snip

....

Just like a macro name.

Thank you for that. I think section 6 ( K&R) will give me a little
more insight into enumerators. For some reason, K&R go into very
little detail about this ( other than describing how to define it) ,
and the only example I saw where it is used with an identifier was in
one of the exercises (2.2) in which T&G use that technique to write a
solution. So your explanation has been very helpful.
 
B

Barry Schwarz

Barry Schwarz wrote, On 05/08/08 18:23:
I think this is true but I don't know if it is complete. What I mean
is that the enumerators appear to act the same as simple macro names
but I don't know if there are any other aspects. For example, if you
view an object of enum type in the debugger, do you see the integer
value or the name or the enumerator? With a macro the answer is
obviously the integer value but with an enumerator?

That all depends on the debugger. Some debuggers even know about macros
these days!

Another advantage of using an enum is that the identifiers then follow
"normal" C scoping unlike macros. Comnsider the following...

int foo() {
#define BUFSIZ 50
unsigned char buf[BUFSIZ];
...

int bar() {
enum { BUFSIZ=50 };
unsigned char buf[BUFSIZ];

Thank you. I knew there had to be a non-trivial distinction but I
couldn't think of what it might be.
 

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


Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top