Можем ли мы выделить память для char *, но вернуть ее как const char *? - PullRequest
1 голос
/ 06 января 2020

Верно ли возвращать const char * из функции, когда реальный возвращаемый буфер не является константным массивом?

Например, в этом минимальном примере ниже, buffer не является константой char *, но затем возвращается как const char *.

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

const char *str_alloc(const char *str, size_t n)
{
    char *buffer;
    if ((buffer = malloc(n)) != NULL) {
        memcpy(buffer, str, n);
    }
    return buffer;
}

int main()
{
    const char *str = str_alloc("hello", 6);

    if (str == NULL) {
        fprintf(stderr, "memory allocation error\n");
        return EXIT_FAILURE;
    }

    printf("str: %s\n", str);

    // str[0] = 'H'; // error: read-only variable is not assignable

    free((void *) str);

    return 0;
}

. В приведенном выше примере возвращение неконстантного char *buffer как const char * помогает мне добиться того, чтобы вызывающая сторона не могла изменить содержимое буфер без явного приведения типа. Но я хотел бы знать, является ли это четко определенным кодом или этот код вызывает неопределенное поведение из-за обработки char * как const char *?

Ответы [ 2 ]

3 голосов
/ 06 января 2020

Это четко определенный код, поскольку вы ограничиваете возможность использования значения. Как говорится, «кто может сделать больше, может сделать меньше». Оппозиция не будет действительной и потребует явного приведения.

Но есть проблема с вашим кодом. Передача размера строки в качестве аргумента небезопасна. В вашем примере вы не скопируете '\ 0' в выделенный буфер. Как следствие, строка не заканчивается правильно. Поведение вашей программы не определено и может привести к ошибке сегментации, потому что printf попытается получить доступ к данным за пределами буфера.

Было бы предпочтительно, чтобы длина строки определялась в функции str_alloc.

1 голос
/ 06 января 2020

Да, это хорошо определено.

Именно поэтому C позволяет свободно преобразовывать указатели в менее квалифицированные типы в указатели на более квалифицированные типы.

Преобразование обратно в указатель на менее квалифицированный тип (в данном случае char const* => char *) также возможно, но требует явного приведения, поскольку результирующий указатель на менее квалифицированный тип (в данном случае char *) может использоваться только для определенной разыменования, если эффективный тип целевого объекта isn не более квалифицированы, чем цель указателя ( 6.5p7 ).

В вашем случае пользователь char const* может привести его обратно к char * и затем разыменовать, потому что память фактически не является const, но если вы вернули указатель char const*, полученный, например, из static char const x[] = "foo";, то приведение к char * и разыменование приведут к неопределенному поведению, поскольку эффективный тип цели более квалифицирован, чем char.

...