============ Structs in C [TOC] ============ ... and a bit on how to implement single linked lists with them. ---- VIDEO ------------------------------ https://www.youtube.com/embed/3w33ucYO_7w ----------------------------------------- Here the link to __VCF East 2019 -- Brian Kernighan interviews Ken Thompson__ and to the pioneers of C and Unix: - __Ken Thompson__: Designed and implemented Unix - __Brian Kernighan__: Contributed to the development of Unix and C. Author of the book __The C Programming Language__ which had a big impact on making C popular. - __Dennis Ritchie__: Created the C programming language. :links: VCF East 2019 -- Brian Kernighan interviews Ken Thompson -> https://www.youtube.com/watch?v=EY6q5dv_B-o&t=1576s Ken Thompson -> https://en.wikipedia.org/wiki/Ken_Thompson Brian Kernighan -> https://en.wikipedia.org/wiki/Brian_Kernighan Dennis Ritchie -> https://en.wikipedia.org/wiki/Dennis_Ritchie The C Programming Language -> https://en.wikipedia.org/wiki/The_C_Programming_Language Exercise: ABC Compiler Project ============================== In the header _lexer.h_ change the declarations ---- CODE (type=h) ------------------------------------------------------------- extern int token_kind; extern size_t token_line; extern size_t token_col; -------------------------------------------------------------------------------- to ---- CODE (type=h) ------------------------------------------------------------- struct Token { enum TokenKind kind; struct TokenPos { size_t line, col; } pos; }; extern struct Token token; -------------------------------------------------------------------------------- or (this is equivalent) ---- CODE (type=h) ------------------------------------------------------------- struct Token { enum TokenKind kind; struct { size_t line, col; } pos; }; extern struct Token token; -------------------------------------------------------------------------------- Modify you implementation of the lexer so that after a call of _getToken()_ in the global variable _token_ the member _token.kind_ has the correct token kind. Change the test program ~xtest_lexer.c~ for the lexer to ---- CODE (type=c) ------------------------------------------------------------- #include #include "lexer.h" int main(void) { while (getToken() != EOI) { printf("%zu.%zu: %s\n", token.pos.line, token.pos.col, strTokenKind(token.kind)); } } -------------------------------------------------------------------------------- Adapt your implementation in _lexer.c_ accordingly. Commit this changes to your git repository. Grammar ======= In Session 9 you saw the gammar for __declarations in C__. This is the part for _structure type specifiers_ in C: :links: declarations in C -> doc:session09/page03#toc5 ---- LATEX --------------------------------------------------------------------- \begin{array}{rcl} \langle\text{structure-type-specifier}\rangle & \to & \textbf{struct}\; \langle\text{identifier}\rangle\; \textbf{\{}\; \langle\text{struct-declaration-list}\rangle\; \textbf{\}}\; \\ & \to & \textbf{struct}\; \textbf{\{}\; \langle\text{struct-declaration-list}\rangle\; \textbf{\}}\; \\ & \to & \textbf{struct}\; \langle\text{identifier}\rangle\; \\ \langle\text{struct-declaration-list}\rangle & \to & \langle\text{struct-declaration}\rangle\; \\ & \to & \langle\text{struct-declaration-list}\rangle\; \langle\text{struct-declaration}\rangle\; \\ \langle\text{struct-declaration}\rangle & \to & \langle\text{specifier-qualifier-list}\rangle\; \langle\text{struct-declarator-list}\rangle\; \textbf{;}\; \\ \langle\text{specifier-qualifier-list}\rangle & \to & \langle\text{type-specifier}\rangle\; \\ & \to & \langle\text{type-specifier}\rangle\; \langle\text{specifier-qualifier-list}\rangle\; \\ & \to & \langle\text{type-qualifier}\rangle \\ & \to & \langle\text{type-qualifier}\rangle\; \langle\text{specifier-qualifier-list}\rangle \\ \langle\text{struct-declarator-list}\rangle & \to & \langle\text{struct-declarator}\rangle \\ & \to & \langle\text{struct-declarator-list}\rangle\; \textbf{,}\; \langle\text{struct-declarator}\rangle \\ \langle\text{struct-declarator}\rangle & \to & \langle\text{declarator}\rangle \\ & \to & \langle\text{declarator}\rangle\; \textbf{:}\; \langle\text{constant-expression}\rangle \\ & \to & \textbf{:}\; \langle\text{constant-expression}\rangle \\ \end{array} -------------------------------------------------------------------------------- Use Cases Not Shown in the Video ================================ Some of this features were used but not explained in detail, some where mentioned and some you might see here the first time. Initialized Definition ---------------------- When you define a struct variable you can initialize all of its member or just the first few of them: ---- CODE (type=c) ------------------------------------------------------------- struct Foo { int a, b; }; struct Foo foo1 = {23, 42}; // initialize: foo1.a = 23, foo1.b = 42 struct Foo foo2 = {23}; // initialize: foo2.a = 23 -------------------------------------------------------------------------------- It is not possible to skip in the initialization the first few and initialize just the rest. If you do not initialize all members it matters whether the variable is global or local. If it is global all other members are zero initialized. For local variables they will have a undefined value (just like for uninitialized local variables in general). For nested structs you can use nested initializer lists: ---- CODE (type=c) ------------------------------------------------------------- struct Foo { int a, b; struct Bar { long c; char d; } bar; int e; }; struct Foo foo = { 1, 2, { 3, 4, }, 5, }; /* foo is initialized with: foo.a = 1, foo.b = 2, foo.bar.c = 3, foo.bar.d = 4, foo.e = 5 */ -------------------------------------------------------------------------------- In initializer lists the last comma is optional. Assignments ----------- Like for "normal" variables you can use structured variables in assignments. In the assignment all members will be copied: ---- CODE (type=c) ------------------------------------------------------------- struct Dummy { int a, b; }; int main(void) { Dummy foo, bar = {1, 2}; foo = bar; // assign all members: foo.a = bar.a, foo.b = bar.b } -------------------------------------------------------------------------------- Function Parameters ------------------- Structs can be parameters of a function. The function receives a copy of the struct, i.e. we have "_call by value_" as always in C. For imitating a _call by reference_ you have to pass a pointer: ---- CODE (file=session15/structs/struct_param.c) ------------------------------ #include struct Dummy { int a, b; }; void foo(struct Dummy dummy) { dummy.a = 12; dummy.b = 34; } void bar(struct Dummy *dummy) { (*dummy).a = 12; (*dummy).b = 34; } int main(void) { struct Dummy d = { 1, 2 }; foo(d); printf("After 'foo(d)': d.a = %d, d.b = %d\n", d.a, d.b); bar(&d); printf("After 'foo(d)': d.a = %d, d.b = %d\n", d.a, d.b); } -------------------------------------------------------------------------------- Here the generated output of the executable: ---- SHELL (path=session15/structs) -------------------------------------------- gcc struct_param.c ./a.out -------------------------------------------------------------------------------- Recall the __precedence of operators in C__ and in particular that postfix operators have a higher precedence than prefix operators: - The dereference operator ('_*_') is a prefix operator. - The member operator ('_._') is a postfix operator. Hence, the expression _*dummy.a_ would be equivalent to _*(dummy.a)_. This makes no sense: _dummy_ is a pointer and has no member, so the compiler will complain about that for a good reason. :links: precedence of operators in C -> doc:session08/page04#toc3 Indirect Member Access ---------------------- The (postfix) operator for indirect member access ('_->_') is equivalent to first dereferencing a pointer and then access a member. Hence, _dummy->a_ is equivalent to _(*dummy).a_. This makes code more expressive: ---- CODE (file=session15/structs/struct_param2.c) ----------------------------- #include struct Dummy { int a, b; }; void foo(struct Dummy dummy) { dummy.a = 12; dummy.b = 34; } void bar(struct Dummy *dummy) { dummy->a = 12; // indirect member access dummy->b = 34; // indirect member access } int main(void) { struct Dummy d = { 1, 2 }; foo(d); printf("After 'foo(d)': d.a = %d, d.b = %d\n", d.a, d.b); bar(&d); printf("After 'foo(d)': d.a = %d, d.b = %d\n", d.a, d.b); } -------------------------------------------------------------------------------- Here the generated output of the executable: ---- SHELL (path=session15/structs) -------------------------------------------- gcc struct_param2.c ./a.out -------------------------------------------------------------------------------- Return Value ------------ Functions can also return a struct. The caller receives a copy of the returned structured variable: ---- CODE (file=session15/structs/struct_retval.c) ----------------------------- #include struct Dummy { int a, b; }; struct Dummy foo(void) { struct Dummy d = { 12, 34 }; return d; } int main(void) { struct Dummy d = foo(); printf("d.a = %d, d.b = %d\n", d.a, d.b); } -------------------------------------------------------------------------------- Here the generated output of the executable: ---- SHELL (path=session15/structs) -------------------------------------------- gcc struct_retval.c ./a.out -------------------------------------------------------------------------------- Declaration and Definition -------------------------- You can declare a structure _Foo_ and define variables of type _struct Foo_ in one sweep: ---- CODE (type=c) ------------------------------------------------------------- struct Foo { int a, b; } foo; -------------------------------------------------------------------------------- This is equivalent to ---- CODE (type=c) ------------------------------------------------------------- struct Foo { int a, b; }; struct Foo foo; -------------------------------------------------------------------------------- Anonymous Structs ----------------- This will define a variable _foo_ of an anonymous struct: ---- CODE (type=c) ------------------------------------------------------------- struct { int a, b; } foo; -------------------------------------------------------------------------------- This for example makes sense if you need exactly one instance. Internally the compiler will give the struct declaration some unique tag, e.g. ---- CODE (type=c) ------------------------------------------------------------- struct .SomeUniqueTag { int a, b; } foo; -------------------------------------------------------------------------------- Anonymous Structs in Nested Structs ----------------------------------- Anonymous struct can also be useful in nested structs if you just want more structure: ---- CODE (type=c) ------------------------------------------------------------- struct Dummy { struct { int a, b; } foo; struct { int a, b; } bar; }; struct Dummy dummy; -------------------------------------------------------------------------------- Then _dummy_ has members like _dummy.foo.a_ and _dummy.bar.a_.