0

What I'm trying to do is pretty straight forward in other languages. But I'm struggling with this in a C project and didn't find a right way to do it in my researches.

What I need to do is:

  1. Declare an global empty array of strings
  2. Inside a function I want to populate this global array with X new strings
  3. In another function, I want loop through all new elements of this array printing them out.

The code I have now is listed below.

#include <stdio.h>

const char *menu[] = {};

void populateMenu(){

    // populate this menu with some itens
    *menu = {
        "New item A",
        "New item B",
        NULL
    };
}

int main(int argc, const char * argv[])
{

    // 1. print inicial menu

    int menuAlen = sizeof(menu)/sizeof(*menu);
    int i;
    for(i = 0; i < menuAlen; i++){
        printf("%s\n", menu[i]);
    }

    // 2. populate the menu
    populateMenu();

    // 3. print it again with new values
    int menuBlen = sizeof(menu)/sizeof(*menu);
    for(i = 0; i < menuBlen; i++){
        printf("%s\n", menu[i]);
    }

    return 0;
}

I'm currently getting this error on build time.

main.c:16:16: Expected expression

Line 16 is the first line inside populateMenu function.

Can someone help me out with this? Thanks in advance.

Best. George.

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • 1
    You can't use an expression like `{ ... }` in an assignment, only in variable initialization. – Barmar Mar 14 '14 at 21:39

3 Answers3

4

You should declare menu as a pointer to [some array of] pointer strings:

char**menu;

You need to know the number of menu entries. So declare also

size_t nb_menu;

and have some way to compute it at runtime. (I leave that up to you, but you cannot use sizeof which is a compile-time thing).

You then allocate in the heap, using calloc(3), that array:

menu = calloc (nb_menu+1, sizeof(char*));
if (!menu) { perror("calloc menu"); exit (EXIT_FAILURE); };

I'm doing nb_menu+1 because you probably want to NULL terminate your menu.

Now you could fill it, e.g.

menu[0] = "Some string";

or better yet, use strdup(3).

You should have a convention (and document it) about when and which pointers are into the heap -i.e; malloc-ed or calloc-ed and who should free them.

You should later free(3) your pointers to avoid memory leaks. You can only free a pointer obtained by malloc or calloc or strdup (which calls malloc) etc...

On Linux, you should learn how to use valgrind. Perhaps using Boehm Garbage Collector could help you. Certainly, understand what garbage collectors are and learn a lot more about C memory management.

Actually, you could use flexible array members and have your menu be a pointer to

struct menu_st {
  unsigned nb_entries;
  char* entry_array[]; // nb_entries elements in the array
};

Read about the C memory model.

Don't forget to compile with all warnings and debug info (e.g. gcc -Wall -g) and learn how to use the debugger (e.g. gdb). Be very scared of undefined behavior.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 1
    `calloc` does not generate null pointers if you're on a system where null pointers are not all-bits-zero; so to be more portable, use `malloc` and then assign NULL to each pointer if it's important to do so (OP was using a null-terminated list of pointers so he only needs to set the first one). – M.M Mar 15 '14 at 01:15
  • @MattNabb: I agree with you in theory, but can you name today a widely used system with a `NULL` pointer not represented by all-zero bits? I can't! – Basile Starynkevitch Mar 15 '14 at 06:58
  • @BasileStarynkevitch Thanks a lot for the answer. I managed to get it working following for steps and understating whats actually going on behind the scenes. – George Villasboas Mar 17 '14 at 12:22
1
#include <stdio.h>

const char **menu = NULL;

void populateMenu(){
    static const char *items[] = {
        "New item A",
        "New item B",
        NULL
    };
    menu = items;
}

int menuLen(const char **menu){
    int len = 0;
    if(!menu) return 0;
    while(*menu++)
        ++len;
    return len;
}

int main(int argc, const char * argv[]){
    // 1. print inicial menu

    int menuAlen = menuLen(menu);
    int i;
    for(i = 0; i < menuAlen; i++){
        printf("%s\n", menu[i]);
    }

    // 2. populate the menu
    populateMenu();

    // 3. print it again with new values
    int menuBlen = menuLen(menu);
    for(i = 0; i < menuBlen; i++){
        printf("%s\n", menu[i]);
    }

    return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
0

I hate global variables. I suggest you create the array inside main() and pass it throughout your code as needed.

// #includes ...
// prototypes
int main(void) {
    char menu[1000][80]; // space for 1000 strings each up to 79 characters long
    size_t nmenu;        // number of string in menu

    nmenu = populate(menu); // change contents of array, set nmenu
    menuprint(menu, nmenu); // print

    return 0;
}

The rest of the program (the functions populate() and menuprint()) is up to you.

Note: the 1000 and 80 are large(ish) estimates. A better way to do it would be with dynamic memory allocation.

pmg
  • 106,608
  • 13
  • 126
  • 198
  • Thanks for the answer. Your suggestion actually works. The problem is the device I'm running it into. I'm very constraint in memory, so initialising a 1000 array (or any other var size bigger then I really need) seems a problem to me. But thanks! – George Villasboas Mar 17 '14 at 12:16