Robert Elder Software Inc.
  • Home
  • Learn
  • Blog
  • Contact
  • Home
  • Learn
  • Blog
  • Contact
  • C Programming
  • |
  • Linux
  • |
  • Shell
  • |
  • Vim
  • |
  • Software Engineering
  • |
  • Other

Strange Corners of C

2015-05-25 - By Robert Elder

In my attempts to learn the C programming language well enough to write my own C compiler, I've encountered some very interesting examples of C syntax that you generally don't see every day.  All of these examples you'll see here will compile without warnings or errors even with very strict compiler flags in gcc and clang (gcc -Wall -ansi -pedantic -std=c89 main.c).  Here are a few of those examples together in one program:

#include <stdio.h>

/*  You can use this structure to access floats by field and by normal 'float' type 
    Note that the ordering of the bitfields must match the endianness of the host. 
    The example below will only work for IEEE 754 implementations of single-precision
    floats on little endian machines */
struct lol {
        /*  IEEE 754 Single Precision floating point accessor */
        union {
                /*  Access by single-precision floating point parts: */
                struct {
                        unsigned int /* fraction bits: */c:23, /* exponentbits: */b:8, /* sign bit: */a:1;
                } f;
                /*  Access float: */
                float g;
        } data;
};

/*  You can typedef a function declaration */
typedef unsigned int * koo(long);

/*  And declare function prototypes (but you can't define them using the typedef) */
koo loo;

/*  You can define a structure in a return type */
struct foo { int i; } function(void){
        struct foo boo = {0};
        return boo;
}

/*  What's going on with these functions?  */
/*  Nested function pointer return types of course.  */
int foo1(int);
int foo1(int foo1param){
/*  Function that takes an int and returns an int */
        return foo1param;
}

int (*foo2(void))(int i);
int (*foo2(void))(int i){
/*  Function that takes 0 parameters and returns a pointer to a function that
    takes an int and returns an int */
        return foo1;
}

int (*(*foo3(void))(void))(int i);
int (*(*foo3(void))(void))(int i){
/*  Function that takes 0 parameters and returns a pointer to a function that
    takes 0 parameters and returns a pointer to a function that takes an int
    and returns an int */
        return foo2;
}

int (*(*(*foo4(void))(void))(void))(int i);
int (*(*(*foo4(void))(void))(void))(int i){
/*  Function that takes 0 parameters and returns a pointer to a function that
    takes 0 parameters and returns a pointer to a function that takes 0
    parameters and returns a pointer to a function that takes an int and
    returns an int */
        return foo3;
}

int (*(*(*(*foo5(void))(void))(void))(void))(int i);
int (*(*(*(*foo5(void))(void))(void))(void))(int i){
/*  Function that takes 0 parameters and returns a pointer to a function that
    takes 0 parameters and returns a pointer to a function that takes 0
    parameters and returns a pointer to a function that takes 0 parameters
    and returns a pointer to a function that takes an int and returns an int */
        return foo4;
}
/*  **************************************  */

void do_f(void);
void do_f(void){
        /*  This is called duff's device and there are many great explanations online: 
            http://en.wikipedia.org/wiki/Duff%27s_device                             */
        /*  What does this do?  */
        unsigned int count = 22;
        unsigned int j = (count + 7) / 8;
        putchar('\n');
        switch(count % 8) {
                case 0: do{     putchar('0' + (int)j);
                case 7:         putchar('0' + (int)j);
                case 6:         putchar('0' + (int)j);
                case 5:         putchar('0' + (int)j);
                case 4:         putchar('0' + (int)j);
                case 3:         putchar('0' + (int)j);
                case 2:         putchar('0' + (int)j);
                case 1:         putchar('0' + (int)j);
                        } while(--j > 0);
        }
        putchar('\n');
}

/*  What does this line do?  */
/*  It declares one variable and two functions  */
/*  A function i that tkes no parameters and returns an unsigned int  */
/*  An unsigned int k initialized to 9  */
/*  A function g that takes no parameters and returns a pointer to an array
    of three unsigned integers  */
unsigned int i(void), k = 9, (*g(void))[3];

int main(void){
/*  This works because "Hello"[5] == 5["Hello"].*/
        char c = 3["Hello"];
/*
a[i] == *(      a        +  i   )  Here the type of a is 'const char [6]' and the type of i is 'int'
a[i] == *(const char [6] + int  )  These are the types corresponding to 'a' and 'i'.
        *(const char  *  + int  )  The type of a decays into 'const char *'
        *(int  +  const char  * )  Since addition is commutative, we can re-arrange freely
i[a] == *(int  +  const char  * )
*/
        int i = c;

        /*  What's the difference between these two? */
        int * j;
        int (* k);
        /*  There is no difference */

        /*  What's the difference between these two? */
        int * l[2];   /*  Array of two pointers to integers */
        int (* m)[2]; /*  Pointer to an array of two integers */


        int arr[2];
        struct lol p;
        arr[0] = 'a';
        arr[1] = 'b';
        j = &i;
        k = &i;
        l[0] = &i;
        m = &arr;

        do_f();

        p.data.f.a = 1;
        p.data.f.b = 3 + 127;  /* Bias of 127 */
        p.data.f.c = 2;

        printf("%.100f\n",p.data.g);

        p.data.f.a = 0;
        p.data.f.b = 0 + 127;
        p.data.f.c = 7;

        printf("%.100f\n",p.data.g);

        printf("%c %c %c %c %c %c %c\n", foo5()()()()('a'), c, i, *j, *k, *l[0], (*m)[0]);
        return 0;
}

If you found this interesting, you might want to check out my C compiler.

C Programming

The Jim Roskind C/C++ Grammar
The Jim Roskind C/C++ Grammar
Published 2018-02-15

Subscribe to New Posts

Email:
Modelling C Structs And Typedefs At Parse Time
Modelling C Structs And Typedefs At Parse Time
Published 2017-03-30
@RobertElderSoft On Twitter
An Artisan Guide to Building Broken C Parsers
An Artisan Guide to Building Broken C Parsers
Published 2017-03-30
Facebook PageHow to Get Fired Using Switch Statements & Statement Expressions
How to Get Fired Using Switch Statements & Statement Expressions
Published 2016-10-27
Building A C Compiler Type System - Part 2: A Canonical Type Representation
Building A C Compiler Type System - Part 2: A Canonical Type Representation
Published 2016-07-21
Building A C Compiler Type System - Part 1: The Formidable Declarator
Building A C Compiler Type System - Part 1: The Formidable Declarator
Published 2016-07-07
GCC's Signed Overflow Trapping With -ftrapv Silently Doesn't Work
GCC's Signed Overflow Trapping With -ftrapv Silently Doesn't Work
Published 2016-05-25
The Magical World of Structs, Typedefs, and Scoping
The Magical World of Structs, Typedefs, and Scoping
Published 2016-05-09
Be Careful When Using Scoped Structures In C
Be Careful When Using Scoped Structures In C
Published 2016-04-09
7 Scandalous Weird Old Things About The C Preprocessor
7 Scandalous Weird Old Things About The C Preprocessor
Published 2015-09-20
Should I use Signed or Unsigned Ints In C? (Part 2)
Should I use Signed or Unsigned Ints In C? (Part 2)
Published 2015-08-16
Should I use Signed or Unsigned Ints In C? (Part 1)
Should I use Signed or Unsigned Ints In C? (Part 1)
Published 2015-07-27
The registered trademark Linux® is used pursuant to a sublicense from the Linux Foundation, the exclusive licensee of Linus Torvalds, owner of the mark on a world­wide basis.
© 2018 Robert Elder Software Inc.
Priavcy Policy      PayPal Acceptable Use      Terms Of Use