Печать члена возвращенной структуры

У меня возникла проблема с печатью члена структуры, возвращаемого функцией:

#include <stdio.h>

struct hex_string
{
    char a[9];
};

struct hex_string to_hex_string_(unsigned x)
{
    static const char hex_digits[] = "0123456789ABCDEF";
    struct hex_string result;
    char * p = result.a;
    int i;
    for (i = 28; i >= 0; i -= 4)
    {
        *p++ = hex_digits[(x >> i) & 15];
    }
    *p = 0;
    printf("%s\n", result.a);   /* works */
    return result;
}

void test_hex(void)
{
    printf("%s\n", to_hex_string_(12345).a);   /* crashes */
}

Вызов printf внутри to_hex_string_ выводит правильный результат, но вызов printf внутри test_hex вызывает сбой моей программы. Почему именно это? Это проблема на всю жизнь, или это что-то еще?

Когда я заменяю вызов printf на puts(to_hex_string_(12345).a), я получаю ошибку компилятора:

invalid use of non-lvalue array

Что здесь происходит?

Ответы

Ответ 1

Существует правило в C, которое редко вступает в силу, в котором говорится:

Если делается попытка изменить результат вызова функции или доступ к нему после следующей точки последовательности, поведение undefined. (C99 §6.5.2.2)

В этом случае существует точка последовательности после вычисления аргументов printf() и перед выполнением самой функции printf(). Указатель, который вы передаете в printf(), является указателем на элемент самого возвращаемого значения - и когда printf() пытается получить доступ к строке через этот указатель, вы получаете свой сбой.

Эта проблема трудно выполнить, потому что значение функции не является значением lvalue, поэтому вы не можете напрямую взять указатель на него с помощью &.

Ответ 2

Вам удалось столкнуться с довольно неясным краевым случаем языка.

Выражение типа массива в большинстве контекстов неявно преобразуется в указатель на первый элемент массива; исключения - это когда выражение является операндом унарного оператора &, когда он является операндом унарного оператора sizeof и когда он является строковым литералом в инициализаторе, используемом для инициализации объекта массива. Ни одно из этих исключений не применяется здесь.

Но есть неявное предположение в этом преобразовании: указатель относится к первому элементу объекта массива.

Большинство выражений массива - почти все из них - фактически - относятся к некоторому объекту массива, такому как объявленная переменная массива, элемент многомерного массива и т.д. Функции не могут возвращать массивы, поэтому вы не можете получить это выражение без значения lvalue.

Но, как вы видели, функция может возвращать структуру, содержащую массив, и нет объекта, связанного с выражением массива to_hex_string_(12345).a.

Новый стандарт ISO C11 решает это, добавив новую формулировку в раздел, описывающий длительность хранения. Проект N1570, раздел 6.2.4p8, гласит:

Не-lvalue выражение со структурой или типом объединения, где структура или объединение содержит элемент с типом массива (включая, рекурсивно, члены всех содержащихся структур и союзов) относится к объект с автоматическим временем хранения и временным временем жизни. Его время жизни начинается, когда выражение оценивается и его начальный value - значение выражения. Его срок службы заканчивается, когда оценка содержания полного выражения или полного декларатора заканчивается. Любая попытка изменить объект с временным временем жизни приводит к undefined.

В действительности, это говорит о том, что возвращаемое значение из вашей функции (в отличие от большинства результатов функции) - это значение временного объекта, позволяющее распаду его члена массива дать вам (временно) действительный указатель.

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

Ответ 3

Проблема, с которой вы сталкиваетесь, такова: переменная result, которая является возвратом, является локальной переменной функции _to_hex_string, что означает, что она удаляется в конце обратного вызова. поэтому, когда вы пытаетесь проверить его в fonction test_hex, он больше не доступен.

Чтобы решить вашу проблему, вы можете иметь дело с указателем.

вот ваш код изменить

struct hex_string
{
    char a[9];
};

struct hex_string * to_hex_string_(unsigned x) // here you return a pointer
{
    static const char hex_digits[] = "0123456789ABCDEF";
    struct hex_string result;

    result = (struct hex_string *) malloc(sizeof(struct hex_string));
    char * p = result->a;
    int i;

    for (i = 28; i >= 0; i -= 4)
    {
        *p++ = hex_digits[(x >> i) & 15];
    }

    *p = 0;
    printf("%s\n", result->a);   /* works */
    return result;
}

void test_hex(void)
{
    printf("%s\n", to_hex_string_(12345)->a);  /* works */
}

У меня хороший день