Нужно реализовать библиотеку функций для построителя строк

Нужно реализовать библиотеку функций для построителя строк, позволяющую «собирать строку» по частям (в динамической памяти, из других строк и символов, соединяя их последовательно). Компилятор выдает ошибку(в функции char*gluestr невозможно преобразовать тип void в char), помогите пожалуйста.

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

char*topstr(char*str)
{
    size_t len = strlen(str);                      
    char*top =(char*) malloc(len + 1);

    if (!top) 
    {
        fprintf(stderr, "malloc() failed: insufficient memory!\n");
        return NULL;
    }
        
    return top;
}
 
 char*gluestr(char*top, char*anystr)
 {
    size_t len1 = strlen(top);
    size_t len2 = strlen(anystr);
    topstr(top);
    top=realloc(top, sizeof(char)*(len1+len2+1));
    
    if (!top) 
    {
        fprintf(stderr, "malloc() failed: insufficient memory!\n");
        return NULL;
    }
    
    strcat(top, anystr);
    
    return top;
 }
 
 int main()
 {
    char str1[20];
    char str2[20];
    puts("Type a string");
    fgets(str1,sizeof(str1),stdin);
    fgets(str2,sizeof(str2),stdin);
    
    gluestr(str1,str2);
    puts(str1);
    return 0;
 }

Ответы (2 шт):

Автор решения: Harry

Компилируйте как С, а не как С++. Установите соответствующие настройки проекта или ключи командной строки.

Или добавьте приведение типа (char*), как вы это делали с malloc:

top = (char*)realloc(top, sizeof(char)*(len1+len2+1));
→ Ссылка
Автор решения: Stanislav Volodarskiy

Ошибки

  1. Не компилируется потому что вы, видимо, компилируете код C++ компилятором. Компилятор C не будет выдавать эту ошибку. В C++ void * не приводится автоматически к char *, а в C приводится.

  2. topstr выделяет память и забывает скопировать туда строку.

  3. gluestr вызывает topstr и теряет выделенную память.

  4. gluestr вызывает realloc на указателе top который указывает на буфер в стеке, а не на объект в куче. Это ломает всю свободную память и затирает стек.

  5. strcat добавляет новые символы в буфер на стеке, а не в новой памяти. Хотя после предыдущего пункта это уже не важно.

Как поправить

В topstr добавить копирование строки:

char *topstr(const char *s) {
    char *s2 = malloc(strlen(s) + 1);
    if (s2 == NULL) {
        fprintf(stderr, "malloc() failed: insufficient memory\n");
        return NULL;
    }
    strcpy(s2, s);
    return s2;
}

gluestr придётся капитально переписать. Теперь она возвращает флаг что строки склеены успешно. Если памяти хватило, то указатель s1 может быть обновлен. Никогда не пишите p = realloc(p, ...) - в случае ошибки вы теряете указатель на старую память:

bool gluestr(char **s1, const char *s2) {
    size_t len1 = strlen(*s1);
    size_t len2 = strlen(s2);

    char *s3 = realloc(*s1, len1 + len2 + 1);
    if (s3 == NULL) {
        fprintf(stderr, "realloc() failed: insufficient memory\n");
        return false;
    }
    strcat(s3, s2);
    *s1 = s3;
    return true;
}

Пример использования:

int main() {
    char str1[20];
    char str2[20];
    puts("Type a string");
    fgets(str1, sizeof(str1), stdin);
    fgets(str2, sizeof(str2), stdin);

    char *s = topstr(str1);
    if (s != NULL && gluestr(&s, str2)) {
        puts(s);
    }
}

Бонус

topstr не нужна, если доработать gluestr. Добавим возможность обрабатывать нулевой указатель в gluestr. Изменится первая строчка: если *s1 == NULL то нельзя вызывать strlen. В конце функции strcat(s3 заменён на strcpy(s3 + len1. Это нужно, так как strcat нельзя вызывать если в s3 неинициализированная память:

bool gluestr(char **s1, const char *s2) {
    size_t len1 = (*s1 == NULL) ? 0 : strlen(*s1);
    size_t len2 = strlen(s2);

    char *s3 = realloc(*s1, len1 + len2 + 1);
    if (s3 == NULL) {
        fprintf(stderr, "realloc() failed: insufficient memory\n");
        return false;
    }
    strcpy(s3 + len1, s2);
    *s1 = s3;
    return true;
}

Тогда пользоваться ею можно так:

char *s = NULL;
if (gluestr(&s, str1) && gluestr(&s, str2)) {
    puts(s);
}

realloc сам выделит память под первую строку если ему дать нулевой указатель.

→ Ссылка