составные литералы и указатели - PullRequest
0 голосов
/ 26 октября 2018

Это безопасно инициализировать указатели, используя составные литералы, и это вообще возможно?:

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

void numbers(int **p)
{
        *p = (int []){1, 2, 3};
}

void chars(char **p)
{
        *p = (char[]){'a','b','c'};
}

int main()
{
    int *n;
    char *ch;

    numbers(&n);
    chars(&ch);
    printf("%d %c %c\n", n[0], ch[0], ch[1]);
}

вывод:

1 a b

Я не совсем понимаю, как это работаетэто не то же самое, что указатель инициализации с локальной переменной?

также, если я пытаюсь напечатать:

printf("%s\n", ch);

Это ничего не печатать.

Ответы [ 2 ]

0 голосов
/ 26 октября 2018

Зависит от того, где находится составной литерал.

C17 6.5.2.5 §5

Значение составного литерала равно значению безымянного объекта, инициализированного список инициализаторов. Если составной литерал находится вне тела функции, объект имеет статическую продолжительность хранения; в противном случае он имеет автоматическую продолжительность хранения, связанную с ограждающий блок.

То есть, если составной литерал находится в локальной области видимости, он работает точно так же, как локальная переменная / массив, и не безопасно возвращать указатель на него из функции.

Если, однако, он объявлен в области видимости файла, он работает как любая другая переменная со статической продолжительностью хранения, и вы можете безопасно вернуть указатель на него. Тем не менее, это, вероятно, является признаком сомнительного дизайна. Кроме того, вы получите обычные проблемы безопасности потоков в многопоточных приложениях.

0 голосов
/ 26 октября 2018

Составной литерал, объявленный внутри функции, имеет автоматическую продолжительность хранения, связанную с его вмещающим блоком (C 2018 6.5.2.5 5), что означает, что его время жизни заканчивается, когда заканчивается выполнение блока.

Внутри numbers, *p = (int []){1, 2, 3}; присваивает адрес составного литерала *p. Когда numbers возвращается, составной литерал перестает существовать, и указатель недействителен. После этого поведение программы, использующей указатель, не определено. Программа может печатать значения, потому что данные все еще находятся в памяти, или программа может печатать другие значения, потому что память изменилась, или программа может перехватывать, потому что она пыталась получить доступ к недоступной памяти, или все поведение программы может измениться. радикально, потому что оптимизация компилятора полностью изменила неопределенное поведение во что-то еще.

...