Dynamically allocating a stack of dynamically allocated strings?


#1

I’m still practicing the stuff I’ve learned up through Ex17. In this code I set out to dynamically allocate strings so they can be various sizes without specifying sizes to allocate. I also wanted to collect them all in a dynamically allocated stack so they can easily be cleaned up when the program closes and be used without duplication. So far this code works fine aside from maybe needing more safety checks n stuff, but my question is just about the basic functionality, did I actually achieve what I think I did? Or am I somehow misleading myself here and this code is working differently than I realize?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char **stack;
int top;
unsigned allocated;

char *Alloc_string(char *text)
{
    size_t size = strlen(text);
    char *string = calloc(size + 1, sizeof(char));
    string = strncpy(string, text, size);
    return string;
}

char *String(char *text)
{
    if (!stack) {
        stack = calloc(1, sizeof(char *));
        allocated = 1; top = 0;
        stack[top] = Alloc_string(text);
    } else {
        stack = realloc(stack, ++allocated * sizeof(char *));
        stack[++top] = Alloc_string(text);
    }
    return stack[top];
}

void Cleanup()
{
    for (int i = 0; i < allocated; i++) {
        free(stack[i]);
    }
    free(stack);
}

int main(int argc, char *argv[])
{
    String("Something I made up.");
    String("Something else I made up");

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

    char *str = String("Something new?");
    printf("My string: %s\n", str);

    char *str_two = String("12345");
    printf("My second string: %s\n", str_two);
    Cleanup();
}

#2

Close, but your Alloc_string() should include the length of the string so that it doesn’t have to use strlen. This turns out to be a lot harder than you think, so give that a try. Basically change your code so that there are no str* functions (strlen, strncpy, etc.).


#3

Challenge accepted. Wonder how many days I’ll work on that :stuck_out_tongue:


#4

However imperfect, I’m proud that I got it figured out faster than I thought.

Got rid of string.h and still have the same functionality :grin:

#include <stdio.h>
#include <stdlib.h>

char **stack;
int top;
unsigned allocated;

char *String(char *text)
{
    int size = 0;
    while (text[++size]);

    if (!stack) {
        stack = calloc(1, sizeof(char *));
        allocated = 1; top = 0;
    } else {
        stack = realloc(stack, ++allocated * sizeof(char *));
        top++; 
    }

    stack[top] = calloc(size, sizeof(char *));

    char *row = stack[top];
    for (int i = 0; i < size; i++) {
        row[i] = text[i];
    }

    return stack[top];
}

void Cleanup()
{
    for (int i = 0; i < allocated; i++) {
        free(stack[i]);
    }
    free(stack);
}

int main(int argc, char *argv[])
{
    String("Something I made up.");
    String("Something else I made up");

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

    char *str = String("Something new?");
    printf("My string: %s\n", str);

    char *str_two = String("12345");
    printf("My second string: %s\n", str_two);

    Cleanup();
}

#5

Changed main to this (it’s been running a LONG time but I’m curious):

    for (int i = 0; i < 1000000000000000000; i++) {
        String("Something Repetitive.");
    }

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

Edit (can watch it better printing out “%d\n”, i ):

for (int i = 0; i < 1000000000000000000; i++) {
        printf("%d\n", i);
        String("Something Repetitive.");
    }

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

EDIT AGAIN: running it at a sensible number for allotted time: 1000000 and it complete without errors or memory leaks :smile:


#6

Still works when using a lot of long strings. But this code will keep going until it uses as much memory as it takes to accommodate the whole stack of strings. But if it doesn’t crash the system it completes without errors.

#include <stdio.h>
#include <stdlib.h>

char **stack;
int top;
unsigned allocated;

char *String(char *text)
{
    int size = 0;
    while (text[++size]);

    if (!stack) {
        stack = calloc(1, sizeof(char *));
        allocated = 1; top = 0;
    } else {
        stack = realloc(stack, ++allocated * sizeof(char *));
        top++; 
    }

    stack[top] = calloc(size, sizeof(char *));

    char *row = stack[top];
    for (int i = 0; i < size; i++) {
        row[i] = text[i];
    }

    return stack[top];
}

void Cleanup()
{
    for (int i = 0; i < allocated; i++) {
        free(stack[i]);
    }
    free(stack);
}

char *Long_string(char *string, size_t max)
{
    size_t size = 0;
    while (size < max) {
        string[size++] = 'X';
    }
    string[size] = '\0';
    return string;
    
}

int main(int argc, char *argv[])
{
    char string[100000] = { '\0' };
    Long_string(string, 100000);

    for (int i = 0; i < 1000; i++) {
        printf("%d\n", i);
        String(string);
    }

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

    Cleanup();
}

Edit: Just cleaning up a little.


#7

I think this is closer to what you might have had in mind before?

#include <stdio.h>
#include <stdlib.h>

char **stack;
int top;
unsigned allocated;

void Stack_alloc()
{
    if (!stack) {
        stack = calloc(1, sizeof(char *));
        allocated = 1; top = 0;
    } else {
        stack = realloc(stack, ++allocated * sizeof(char *));
        top++; 
    }
}

char *String(char *text)
{
    size_t size = 0;
    while (text[++size]); 
    
    Stack_alloc();
    stack[top] = calloc(size, sizeof(char *));

    char *row = stack[top];
    for (int i = 0; i < size; i++) {
        row[i] = text[i];
    }

    return stack[top];
}

void Cleanup()
{
    for (int i = 0; i < allocated; i++) {
        free(stack[i]);
    }
    free(stack);
}

char *Long_string(char *string, size_t max)
{
    size_t size = 0;
    while (size < max) {
        string[size++] = 'X';
    }
    string[size] = '\0';
    return string;
    
}

int main(int argc, char *argv[])
{
    char string[100000] = { '\0' };
    Long_string(string, 100000);

    for (int i = 0; i < 1000; i++) {
        printf("%d\n", i);
        String(string);
    }

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

    Cleanup();
}

#8

You need to jump ahead to my Awesome Debug Macros and use those next. You aren’t checking the return values of any of these so you are probably going to miss errors.