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