Search code examples
csymbol-table

Using an array of strings to implement a symbol table in C


I am trying to use an array of structs to create a symbol table. This is what I have so far, but I am having trouble allocating memory in the create function, is what I have so far correct?

I want something like this as my final result for arr { {"sym1"; 1}, {"sym2"; 2}, {"sym3"; 3} }

struct str_id {
  char* s;
  int id;
}

struct symbol_table {
  int count;
  struct str_id** arr;
}

struct symbol_table *symbol_table_create(void) {
  struct symbol_table *stt = malloc(sizeof(struct symbol_table));
  stt->count = 1;
  stt->arr =  malloc(sizeof(struct str_id*) * stt->count);
  return stt;

}

Solution

    • Use descriptive names for identifiers, not cryptic short names (like s and str_id).
    • Avoid Systems Hungarian Notation (i.e. naming or prefixing identifiers after their type or what-they-are as opposed to what-they-mean).
      • In your case, I assume str_id is an abbreviation for struct_id (or string_id) - which is a bad name because it's already immediately obvious that it's a struct (or contains a string).
      • It was popular right until the 1990s when programmers started using more powerful editors and IDEs that kept track of variable types - it just isn't needed today.
      • *
    • Always check if a heap allocation succeeded or failed by comparing calloc and malloc's return values to NULL. This can be done with if( some_pointer ) abort().
      • Don't use assert( some_pointer ) because assertions are only enabled in debug builds, use abort instead as it signifies abnormal program termination compared to exit.
    • Pass a size_t parameter so consumers can specify the size of the symbol table.
    • Quantities of objects held in memory should be expressed as size_t (e.g. array indexers). Never use int for this!
    • You need to put a semi-colon at the end of each struct definition.
    • Are you sure you want an array-of-pointers-to-structs and not just an array-of-structs? In this case you can use inline structs and use a single allocation for the array, instead of allocating each member separately.
    • Because you're performing custom allocation, you must also define a destructor function.
    struct symbol_table_entry {
      char* symbolText;
      int   id;
    };
    
    struct symbol_table {
      size_t count;
      struct symbol_table_entry** entries;
    };
    
    struct symbol_table* create_symbol_table( size_t count ) {
        struct symbol_table* stt = malloc( sizeof(struct symbol_table) );
        if( !stt )
        {
            abort();
        }
        stt->count = count;
        stt->entries = calloc( count, sizeof(struct symbol_table_entry) );
        if( !stt->entries ) {
            free( stt );
            abort();
        }
        // Note that calloc will zero-initialize all entries of the array (which prevents debuggers showing garbage string contents) so we don't need to do it ourselves.
        return stt;
    }
    
    void destroy_symbol_table( struct symbol_table* stt, bool free_strings ) {
        if( stt->entries ) {
            if( free_strings ) {
                for( size_t i = 0; i < stt->count; i++ ) {
                    free( stt->entries[i]->symbolText );
                }
            }
            free( stt->entries );
        }
        free( stt );
    }