lisp toy: strangest warning I've seen

L

luserXtrog

Can anyone shed light upon this warning. From the line
number it refers to the anonymous union, but I don't
understand what the compiler's griping about.

193(1)09:30 PM:~ 0> gcc -std=c99 -o list list.c
list.c:12: warning: declaration does not declare anything

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }

struct sexpr {
bool atom;
union {
int cari;
struct sexpr *carl;
};
struct sexpr *cdr;
};

struct list {
int *car;
struct list *cdr;
};

bool init() {
struct list *lp, *h;
(lp=malloc(sizeof*lp)) || error("poof!");
h=lp;
for(int *i = (int[]){ 42, 69, 613, 0 }; *i; i++) {
lp->car = i;
(lp->cdr=malloc(sizeof*lp)) || error("poof!");
lp = lp->cdr;
*lp = (struct list){ NULL, NULL };
}
return true;
}

struct list *read(FILE *f) {
struct list *lp;
int c;
(lp=malloc(sizeof*lp)) || error("poof!");
while( EOF!= (c=fgetc(stdin)) ) {
switch(c) {
case '(': lp->car = 0;
lp->cdr = read(f);
break;
case ')': return NULL;
default:
(lp->car=malloc(sizeof*lp)) || error("poof!");
*lp->car = c;
}
}
return lp;
}

struct list *eval(struct list *lp) {
return lp;
}

bool print(FILE *f, struct list *lp) {
if (lp) {
fprintf(f, "%d\n", lp->car);
print(f,lp->cdr);
return true;
} else {
return false;
}
}

bool run() {
while (print(stdout,eval(read(stdin)))) ;
return true;
}

int main() {
return init()&&run()?0:EXIT_FAILURE;
}

/*eof*/
 
S

Spiros Bousbouras

Can anyone shed light upon this warning. From the line
number it refers to the anonymous union, but I don't
understand what the compiler's griping about.

193(1)09:30 PM:~ 0> gcc -std=c99 -o list list.c
list.c:12: warning: declaration does not declare anything

Standard C does not have anonymous unions. Lose the -std=c99
part and the warning goes away.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }

struct sexpr {
bool atom;
union {
int cari;
struct sexpr *carl;
};
struct sexpr *cdr;

};
[...]


struct list *read(FILE *f) {
struct list *lp;
int c;
(lp=malloc(sizeof*lp)) || error("poof!");
while( EOF!= (c=fgetc(stdin)) ) {

Do you mean fgetc(f) ?

[...]
 
L

luserXtrog

Standard C does not have anonymous unions. Lose the -std=c99
part and the warning goes away.

But that's even worse!
199(1)10:19 PM:~ 0> gcc -o list list.c
list.c: In function 'init':
list.c:25: error: 'for' loop initial declaration used outside C99 mode

Granted that particular loop doesn't do anything useful
and will not be long for this earth. But I want all the goodies.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }
struct sexpr {
    bool atom;
    union {
        int cari;
        struct sexpr *carl;
    };
    struct sexpr *cdr;

[...]



struct list *read(FILE *f) {
    struct list *lp;
    int c;
    (lp=malloc(sizeof*lp)) || error("poof!");
    while( EOF!= (c=fgetc(stdin)) ) {

Do you mean fgetc(f) ?

Um. Yes. But something else is terribly wrong with it.
Don't anybody run this program! You have to Interrupt it
to make it stop. And I'm trying to start over with
the new structure (having gotten through that first John
McCarthy paper).

trog
as in ata-ara-ogg
the true mouth of ogh
 
S

Spiros Bousbouras

But that's even worse!
199(1)10:19 PM:~ 0> gcc -o list list.c
list.c: In function 'init':
list.c:25: error: 'for' loop initial declaration used outside C99 mode

Granted that particular loop doesn't do anything useful
and will not be long for this earth. But I want all the goodies.

Use a named union then. When I said "Lose the -std=c99 part" I
wasn't making a suggestion, I was explaining what's happening.
Personally I consider anonymous structs or unions a bug waiting
to happen since they may lead to ambiguous field references
which gcc may not notice.
 
L

luserXtrog

Use a named union then. When I said "Lose the -std=c99 part" I
wasn't making a suggestion, I was explaining what's happening.
Personally I consider anonymous structs or unions a bug waiting
to happen since they may lead to ambiguous field references
which gcc may not notice.

Thank you. I consider my question answered.
I suppose any tricks for allowing the two constructs to coexist
are implementation-specific and therefore OT.
Bummer.
 
V

vivek

Thank you. I consider my question answered.
I suppose any tricks for allowing the two constructs to coexist
are implementation-specific and therefore OT.
Bummer.
 
A

Antoninus Twink

193(1)09:30 PM:~ 0> gcc -std=c99 -o list list.c

I'm surprised CBF hasn't been along yet to tell you that lisp is
off-topic, without bothering to read your message and learn that the
subject line has a typo - that's usually the way he rolls.

Still, he's usually late to the party too, so give him 24 hours...
 
B

Ben Bacarisse

luserXtrog said:
Thank you. I consider my question answered.

No chance! I would not recommend it for new code but to get existing
code though a compiler that does not have anonymous unions, you will
sometimes see:

struct sexpr {
bool atom;
union {
int cari;
struct sexpr *carl;
} either;
#define cari either.cari
#define carl either.carl
struct sexpr *cdr;
};
I suppose any tricks for allowing the two constructs to coexist
are implementation-specific and therefore OT.

The above might count as such.
 
K

Keith Thompson

luserXtrog said:
Thank you. I consider my question answered.
I suppose any tricks for allowing the two constructs to coexist
are implementation-specific and therefore OT.
Bummer.

Not necessarily. If you really wanted to, you could write:

struct sexpr {
bool atom;
union {
int cari;
struct sexpr *carl;
} foo;
struct sexpr *cdr;
};
#define cari foo.cari
#define carl foo.carl
 
R

REH

Can anyone shed light upon this warning. From the line
number it refers to the anonymous union, but I don't
understand what the compiler's griping about.

Anonymous unions are part of C++, not C.

REH
 
R

Richard Tobin

Keith Thompson said:
Not necessarily. If you really wanted to, you could write: [...]
#define cari foo.cari
#define carl foo.carl

Unfortunately this pollutes the namespace, especially since it's
common to want to use variables with the same names as fields.

Anonymous unions make simple object-oriented programming much
neater.

-- Richard
 
L

luserXtrog

Not necessarily.  If you really wanted to, you could write:

struct sexpr {
    bool atom;
    union {
        int cari;
        struct sexpr *carl;
    } foo;
    struct sexpr *cdr;};

#define cari foo.cari
#define carl foo.carl

Yes. That would do it. (Props to Ben, too!)
Apropos, I discovered that a normal declaration sans identifier
produces quite a different warning:

224(1)10:29 PM:~ 0> cat noid.c && make noid
#include <stdio.h>

int main() {
int i;
int;
*((&i) + 1) = 5;
printf("%d\n", (&i)[1]);
return 0;
}
cc noid.c -o noid
noid.c: In function 'main':
noid.c:5: warning: useless type name in empty declaration

Oddly enough, on my system this prints 5 just before
segfaulting.

So it's possible that space for the union with no name
is included in the struct although the internal names aren't
accessable.

But I've decided against it all.
Since you're all here, I present take 2.
No real questions at the moment but if anything jumps out
as misguided, I'd appreciate a heads-up.

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }

union word {
int i;
struct sexpr *link;
};

struct sexpr {
bool atom; //if true, cdr s.b. NULL
union word car;
struct sexpr *cdr;
};

struct sexpr *globl;

bool init() {
struct sexpr *lp, *h;
(lp=malloc(sizeof*lp)) || error("poof!");
h=lp;
for(int *i = (int[]){ 42, 69, 613, 0 }; *i; i++) {
lp->atom = 0;
(lp->car.link=malloc(sizeof*lp->car.link)) || error("poof!");
lp->car.link->atom = 1;
lp->car.link->car.i = *i;
lp->car.link->cdr = NULL;
(lp->cdr=malloc(sizeof*lp)) || error("poof!");
lp = lp->cdr;
}
free(lp);
lp=NULL;
globl=h;
return true;
}

bool print(FILE *f, struct sexpr *lp) {
if (lp) {
if (lp->atom) {
fprintf(f, "%d\n", lp->car.i);
} else {
if (lp->car.link) print(f,lp->car.link);
if (lp->cdr) print(f,lp->cdr);
}
return true;
} else {
return false;
}
}

bool run() {
//while (print(stdout,eval(read(stdin)))) ;
print(stdout,globl);
return true;
}

int main() {
return init()&&run()?0:EXIT_FAILURE;
}

/*eof*/
 
B

Ben Bacarisse

Apropos, I discovered that a normal declaration sans identifier
produces quite a different warning:

224(1)10:29 PM:~ 0> cat noid.c && make noid
#include <stdio.h>

int main() {
int i;
int;
*((&i) + 1) = 5;
printf("%d\n", (&i)[1]);
return 0;
}
cc noid.c -o noid
noid.c: In function 'main':
noid.c:5: warning: useless type name in empty declaration

Oddly enough, on my system this prints 5 just before
segfaulting.

I don't see the oddity (a messed up stack often shows up only on
return).
So it's possible that space for the union with no name
is included in the struct although the internal names aren't
accessable.

But I've decided against it all.
Since you're all here, I present take 2.
No real questions at the moment but if anything jumps out
as misguided, I'd appreciate a heads-up.

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }

union word {
int i;
struct sexpr *link;
};

struct sexpr {
bool atom; //if true, cdr s.b. NULL
union word car;
struct sexpr *cdr;
};

I don't like the asymmetry. I you want an atom to use only one
pointer's worth of space (as is often done -- the cdr pointer being
used for the property list) I'd go so far as to do this:

union word {
int i;
struct sexpr *link;
};

struct sexpr {
bool atom;
union word car;
struct { struct sexpr *link; } cdr;
};

so that the pointers are sp->cdr.link and sp->car.link.

If you want to get the most storage out of an atom, I'd make is span
both links although there is no portable way to define an int that is
the size of two pointers:

struct sexp {
bool atom;
union {
long long int i;
struct {
struct sexp *car, *cdr;
} pair;
} sexp;
};

though this gives the rather messy names sp->sexp.pair.car and
sp->sexp.pair.cdr.

I'll just throw in the option that, for a fixed size heap, declaring
each part separately has some merit. Gregory Chaitin's tiny LISP
interpreter uses:

long car[SIZE], cdr[SIZE]; /* tree storage */
short atom[SIZE]; /* is it an atom? */
short numb[SIZE]; /* is it a number? */

and so on. (He uses indexes rather than pointers but you get the idea).
 
L

luserXtrog

luserXtrog <[email protected]> writes:



I don't like the asymmetry.  I you want an atom to use only one
pointer's worth of space (as is often done -- the cdr pointer being
used for the property list)

Ah. I'd forgotten about the property list (or association list,
as the original paper designates them).
I'd go so far as to do this:

  union word {
      int i;
      struct sexpr *link;
  };

  struct sexpr {
      bool atom;
      union word car;
      struct { struct sexpr *link; } cdr;
  };

so that the pointers are sp->cdr.link and sp->car.link.

Yes. Thanks. That's much better.
If you want to get the most storage out of an atom, I'd make is span
both links although there is no portable way to define an int that is
the size of two pointers:

  struct sexp {
      bool atom;
      union {
          long long int i;
          struct {
              struct sexp *car, *cdr;
          } pair;
      } sexp;
  };

though this gives the rather messy names sp->sexp.pair.car and
sp->sexp.pair.cdr.

I'll just throw in the option that, for a fixed size heap, declaring
each part separately has some merit.  Gregory Chaitin's tiny LISP
interpreter uses:

 long car[SIZE], cdr[SIZE]; /* tree storage */
 short atom[SIZE]; /* is it an atom? */
 short numb[SIZE]; /* is it a number? */

and so on.  (He uses indexes rather than pointers but you get the idea)..

Interesting. For the moment I prefer the structs so related data
is kept together. But garbage collection would be much simpler
with fixed-size arrays.

Thanks again.
 
A

Alan Curry

Granted that particular loop doesn't do anything useful
and will not be long for this earth. But I want all the goodies.

-std=c99 isn't that. But if you look at the documentation, in the list of
recognized -std= parameters you should find one that does mean effectively
"all the goodies".
 
L

luserXtrog

Not answering your question here, but... C is not PERL. Blech-an'a-half.

I knew somebody would have strong feelings against this form
if I continued to use it; but I think it reads better than
a negative predicate. It's also shorter than the alternative:

if ((lp=malloc(sizeof*lp)) == NULL) error("poof!");

For full disclosure, it does require extra decoration to pass splint:

(void)((lp=malloc(sizeof*lp)) || error("poof!"));

But it's still shorter.
 
J

jameskuyper

Richard said:
Not answering your question here, but... C is not PERL. Blech-an'a-half.

Nonetheless, the perl features that this idiom relies upon were all
borrowed from C, and it works just as well in C as in perl. I think
it's a somewhat confusing idiom, but that's just a true in perl as in
C.
 
S

Spiros Bousbouras

Nonetheless, the perl features that this idiom relies upon were all
borrowed from C, and it works just as well in C as in perl.

Perhaps Perl borrowed them from Unix shells rather than C?
 
K

Keith Thompson

luserXtrog said:
I knew somebody would have strong feelings against this form
if I continued to use it; but I think it reads better than
a negative predicate. It's also shorter than the alternative:

if ((lp=malloc(sizeof*lp)) == NULL) error("poof!");

For full disclosure, it does require extra decoration to pass splint:

(void)((lp=malloc(sizeof*lp)) || error("poof!"));

But it's still shorter.

Yes, it's shorter. You can make it shorter still by deleting the
spaces around the "||" operator.

Are you assuming that making it shorter is a *good* thing?

Within limits, it can be; it's certainly possible to be too verbose.
<OT>And I certainly use the "do-something or die" idiom in Perl.</OT>

But here's how I'd write it:

if ((lp = malloc(sizeof *lp) == NULL) {
error("poof!");
}

or even:

lp = malloc(sizeof *lp);
if (lp == NULL) {
error("poof!");
}

Note, among other things, the spaces around the "=" operator and after
the "sizeof" operator.

Y M M V.
 

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,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top