печать члена возвращаемой структуры - PullRequest
16 голосов
/ 01 ноября 2011

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

#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

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

Ответы [ 3 ]

18 голосов
/ 01 ноября 2011

В C редко применяется правило, которое гласит:

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

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

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

13 голосов
/ 01 ноября 2011

Вам удалось столкнуться с довольно туманным угловым падежом языка.

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

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

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

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

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

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

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

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

0 голосов
/ 01 ноября 2011

Проблема, с которой вы сталкиваетесь, заключается в следующем: возвращаемая переменная result является локальной переменной функции _to_hex_string , то есть она удаляется в конце вызова функции. поэтому, когда вы пытаетесь проверить это в функции 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 */
}

Хороший ли у меня день

...