1
// Online C compiler to run C program online
#include <stdio.h>
#include <stdlib.h>

char* word(char w[]) {
    char *temp = w;
    return temp;
}

char** tokens() {
    char *one = word("One");
    char *two = word("two");
    char *three = word("three");
    char *four = word("four");
    char *five = word("five");
    char *six = word("six");
    char *seven = word("seven");
    char *eight = word("eight");
    char *nine = word("nine");
    char *ten = word("ten");
    char *eleven = word("ten");
    char *twelve = word("ten");
    char *thirteen = word("ten");
    
    char *words[13];
    
    words[0] = one;
    words[1] = two;
    words[2] = three;
    words[3] = four;
    words[4] = five;
    words[5] = six;
    words[6] = seven;
    words[7] = eight;
    words[8] = nine;
    words[9] = ten;
    words[10] = eleven;
    words[11] = twelve;
    words[12] = thirteen;
    
    char **pp = words;
    return pp;
}

int main() {
    // Write C code here
    char **pp = (char **)malloc(13 * sizeof(char));
    
    pp = tokens();
    
    for (int i = 0; i < 12; i++) {
        printf("%s\n", *pp);
        *pp++;
    }
    // for (; *pp; *pp++) {
    //     printf("%s\n", *pp);
    // }
    
    return 0;
}

I want the code to print every word contained in the tokens() method. However, in the for loop in main(), it only prints: one, two, three, four, null, and then gets a segmentation fault. If I use the for loop that is commented, it only prints: one, two, three, four. I'm new to C, so pointers are still pretty confusing. I'd appreciate any help. Thanks.

  • 1
    What is the point of the function `word`? It does nothing else than return its argument. – Andreas Wenzel Sep 20 '21 at 22:20
  • I’m just testing some stuff I know it’s not necessary. – SebastianSrvn Sep 20 '21 at 22:27
  • Then don't post it. – Cheatah Sep 20 '21 at 22:47
  • @SebastianSrvn: In general, you are supposed to provide a [mre] for your problem, which means all unnecessary stuff should be removed. As far as I'm concerned, you don't have to remove it for this question, but it would be a good to do this for future questions. Otherwise, your question has a higher chance of being downvoted. – Andreas Wenzel Sep 20 '21 at 23:06

2 Answers2

1

The lifetime of the array words ends as soon as the function tokens returns. Therefore, it is not meaningful for the function tokens to return a pointer to this object, because that pointer will be pointing to an object that no longer exists. Dereferencing that pointer will cause undefined behavior (i.e. your program may crash).

If you want the function tokens to return a pointer to a valid object, you must ensure that the lifetime of words does not end, for example by using dynamic memory allocation (i.e. malloc) or change the lifetime of words to static, by using the static keyword.

In your case, it would be best to remove the function tokens and to simply define the variable words inside the function main, like this:

static const char *const words[] = {
    "one", "two", "three", "four", "five",
    "six", "seven", "eight", "nine", "ten",
    "eleven", "twelve", "thirteen"
};

If you really want to keep the function tokens, then you can rewrite it like this:

const char *const * tokens( void )
{
    static const char *const words[] = {
        "one", "two", "three", "four", "five",
        "six", "seven", "eight", "nine", "ten",
        "eleven", "twelve", "thirteen"
    };

    return words;
}

That way, the function tokens will return a pointer to a statically allocated object, which has static (unlimited) lifetime.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
1

You have quite a few errors:

char **pp = (char **)malloc(13 * sizeof(char));

When allocating memory, you want to allocate sizeof(<oneLevelUp>) * numOfElements. In this case, you're allocating for a char**, so "one level up" is a char*. Additionally, no need to cast the return value of malloc. Change that to

 char **pp = malloc(13 * sizeof(char*));

Finally, the best-considered practice is to include the variable name when using sizeof. This means there will be less maintenance if variable type ever changes:

 char **pp = malloc(13 * sizeof(*pp));  // preferred method

*pp is a char* type. If you change that to

 int **pp = malloc(13 * sizeof(*pp));

now *pp is an int* type automatically, without having to change the sizeof parameter from char* to int*.

The other problems have already been addressed in comments and answers. word essentially does nothing, as it simply returns the argument passed into it; this function will surely get optimized out. @AndreasWenzel's answer explains what's wrong with tokens. The top-rated answer here has an amusing anecdote about returning pointers to local variables.

One more final note, even if tokens didn't invoke UB, you would be/are creating a memory leak by assigning pp to the return value of tokens.

char **pp = malloc(13 * sizeof(*pp));
if (pp == NULL)
{
  // uh oh, we're out of memory, handle the error however you want. For this example, just exit
  exit(-1);
}

// uh oh, by making this assignment, pp no longer points to the memory block
// returned from `malloc`. Now that memory is reserved and nothing points to
// it. If we did this over and over, our process would continue to consume
// memory until none was left.
pp = tokens();
yano
  • 4,827
  • 2
  • 23
  • 35