Должна ли нулевая символьная строка быть равна нулевому терминатору в C? - PullRequest
1 голос
/ 28 апреля 2020

Есть ли разница между инициализацией массива символов нулевыми символами и нулевым терминатором? Эти два примера:

char * result = ""
char * result = '\0';

Первый выдает ошибку, а другой - сносно.

Прототип функции:

char * form(int n, ...);

Код:

char * result = "";
char *next;
char val_int[12];
int i;
va_list args;
va_start(args, n);

for (i = 0; i < n; i++)
{
    int len = result ? strlen(result) : 0;
    sprintf(val_int, "%d", va_arg(args,int));
    next = val_int;
    char *tmp = (char *)malloc(len + strlen(next) + 1);

    strcpy(tmp, result ? result : "");
    strcat(tmp, next);

    free(result);
    result = tmp;
}
va_end(args);
return result;

Основная функция:

  char *s;
  s = form(3,123,456,789);
  printf("%s", s);
  free(s);
  return 0;

Ответы [ 2 ]

4 голосов
/ 28 апреля 2020

Первое объявление

char * result = "";

объявляет указатель на строковый литерал, который содержит один символ: завершающий ноль '\0'.

Это объявление

char * result = '\0';

инициализировать указатель константой нулевого указателя и эквивалентен

char * result = NULL;

Вот демонстрационная программа

#include <stdio.h>

int main(void) 
{
    char *s1 = "";
    char *s2 = '\0';

    printf( "s1 == NULL is %s\n", s1 == NULL ? "true" : "false" );
    printf( "s2 == NULL is %s\n", s2 == NULL ? "true" : "false" );

    return 0;
}

Его вывод

s1 == NULL is false
s2 == NULL is true

Что касается причина ошибки, о которой вы говорите, заключается в следующем:

free(result);

Вы не можете освобождать память, занятую строковым литералом. Вы можете освободить память, которая была выделена динамически.

Когда используется указатель, инициализированный символьным литералом '\0', тогда такой указатель является нулевым указателем, и вы можете вызвать free для нулевого указателя.

То есть, если у вас есть инициализированный указатель, такой как

char * result = NULL;

, такой же, как и для записи

char * result = '\0';

, тогда вы можете позвонить

free( result );

При таком вызове не будет выполнено никаких действий.

Но если вы напишите

free( "" );

, пытаясь освободить строковый литерал, имеющий стати c длительность хранения, вы получите неопределенное поведение.

И этот вызов

free( "" );

эквивалентен следующему фрагменту кода

char * result = "";
free( result );
1 голос
/ 28 апреля 2020

Кажется, проблема в том, что вы пытаетесь освободить память, которая не была выделена с помощью malloc.

В C все литеральные строки действительно хранятся (компилятором) как массивы неизменяемых символов .

Если у вас есть

char * result = "";

, компилятор создаст такой массив для пустой строки. Он будет содержать один единственный элемент - нулевой терминатор строки. Инициализация заставит result указать на первый элемент этого массива.

И, как и любой другой массив, вы не можете free это.

char * result = "";
free(result);

в основном то же самое as

char array[] = "";
char * result = array;
free(result);

Это просто не имеет смысла.

То есть работает с

char * result = '\0';

, потому что (как объяснено) инициализирует result как нулевой указатель и допустимо передать нулевой указатель на free (это неоперация).

...