это моя идея:
int main(){
struct mystruct {
char a;
char b;
char c;
char d;
};
struct mystruct structinstance;
//1094795585 is in binary: 01000001 01000001 01000001 01000001 -to dez-> 65 65 65 65 -in ASCII-> AAAA
int myint = 1094795585;
//here the gcc compiler gives me this error (it marked "mystruct"): error: conversion to non-scalar type requested
structinstance = (struct mystruct)(myint);
printf("%c,%c,%c,%c",structinstance.a, structinstance.b, structinstance.c, structinstance.d);
}
Ожидаемый результат функции printf будет «AAAA». У меня возникли проблемы с поиском ресурсов о том, как приводить типы со структурами, поэтому поделитесь ссылками, если они у вас есть.
Не делайте этого, вместо этого используйте битовый сдвиг и маскирование. Последний четко определен и гарантированно работает, в отличие от .
Приведение значения к типу, не являющемуся указателем, приводит к преобразованию типа. Например, приведение нуля int
к float
дает float
со значением ноль. Не существует осмысленного способа преобразования целого числа в структуру, поэтому (struct mystruct)myint
приводит к ошибке компиляции.
Приведение указателя — это другое. Например, вы можете привести &myint
к struct mystruct *
и использовать полученный указатель. Вот бы скомпилировалось. Однако это является нарушением строгих правил псевдонимов, что означает, что его поведение не определено.
Самый простой вариант, доступный вам здесь, — это привести к unsigned char *
. Приведение указателя на объект к char *
, signed char *
или unsigned char *
всегда допустимо.
Итак, если мы продолжим считать, что размер myint
равен четырем (что ни в коем случае не гарантируется, даже если это int
), все, что вам нужно, это следующее:
char *p = &myint;
printf( "%c%c%c%c\n", p[0], p[2], p[2], p[3] );
Мы можем легко адаптировать это к объектам любого размера.
char *p = &myint;
for ( size_t n = sizeof( myint ); n--; )
printf( "%c", *(p++) );
printf( "\n" );
Вы также можете использовать memcpy
или union
, но это слишком сложно для представленного вами сценария.
@ikegami, можешь объяснить, что происходит, когда ты наводишь указатель? Я думал, что это просто адреса памяти, как на них влияет приведение?
Допустим, myint
находится по адресу 0x1000. &myint
создает указатель типа int *
, который указывает на адрес 0x1000. Приведение этого значения к char *
создает указатель типа char *
, который указывает на адрес 0x1000. Таким образом, разыменование этого указателя (p
) обеспечивает доступ к памяти, используемой myint
.
в сети c (в Windows) вы сталкиваетесь с некоторым приведением типов структур, и это похоже на плохую практику: "bind(ListenSocket, (SOCKADDR*) &socketaddr_ininstance, sizeof(sockaddr)); " Во втором аргументе метода связывания() вы выполняете приведение типов экземпляр структуры "socketaddr_in" в структуру SOCKADDR, я думаю, потому что она использовалась ранее, и многие функции (включая привязку) полагаются на эту старую структуру. По сути, сокет_addr_in и SOCKADDR имеют в общей сложности 16 байт, но у сокета_addr_in есть еще несколько переменных. Можете ли вы объяснить, почему мы кодируем таким образом?
Он обеспечивает полиморфизм. Для семейств адресов предусмотрены различные типы структур. Но в качестве типа аргумента следовало бы использовать void *
. Я считаю, что технически код представляет собой неопределенное поведение (хотя у меня есть ноющее ощущение, что это может быть не так, потому что они оба являются указателями на структуры). Обратная совместимость иногда отстой.
Поскольку эти два типа несовместимы, вам придется проявить немного изобретательности. Я могу придумать кучу способов:
указатель int на приведение указателя структуры (хотя это работает, это неопределенное поведение, как заметил @ikegami, проверьте [ТАК]: Что такое строгое правило псевдонимов?)
Используйте память
Оберните 2 в объединение (распространенный способ проверки порядка байтов)
код00.с:
#include <stdint.h>
#include <stdio.h>
#include <string.h>
uint32_t test = 0x41424344; // "ABCD"
typedef struct {
uint8_t c0;
uint8_t c1;
uint8_t c2;
uint8_t c3;
} S;
void cast_pointer()
{
S *ps = (S*)(&test); // !!! UB !!!
printf("Pointer cast (undefined behavior): %c, %c, %c, %c\n", ps->c0, ps->c1, ps->c2, ps->c3);
}
void cast_memcpy()
{
S s;
memcpy(&s, &test, sizeof(test));
printf("Memcpy cast: %c, %c, %c, %c\n", s.c0, s.c1, s.c2, s.c3);
}
typedef union {
S s;
uint32_t i;
} U;
void cast_union()
{
U u;
u.i = test;
printf("Union \"cast\": %c, %c, %c, %c\n", u.s.c0, u.s.c1, u.s.c2, u.s.c3);
}
int main()
{
cast_pointer();
cast_memcpy();
cast_union();
printf("\nDone.\n\n");
return 0;
}
Выход:
(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q078656715]> ~/sopr.sh ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [064bit prompt]> ls main00.c [064bit prompt]> gcc main00.c [064bit prompt]> [064bit prompt]> ls a.out main00.c [064bit prompt]> [064bit prompt]> ./a.out Pointer cast (undefined behavior): D, C, B, A Memcpy cast: D, C, B, A Union "cast": D, C, B, A Done.
Примечания:
Я заменил int на uint32_t и char на uint8_t, чтобы код был совместим с любой системой (проверьте [ТАК]: разница между int32, int, int32_t, int8 и int8_t)
Члены структур могут автоматически дополняться компилятором ([SO]: эффект #pragmapack, в данном случае это не тот случай)
Вывод осуществляется в обратном порядке, как вы, наверное, догадались: потому что я использую процессор Intel (pc064), который имеет прямой порядок байтов (хотя и связан с Python, [SO]: поведение Python struct.pack() (ответ @CristiFati) есть информация по теме)
Излишне говорить, что не используйте № 1.
Обратите внимание: я не говорил, что приведение указателя нельзя использовать. (На самом деле я сказал, что его можно использовать.) Я сказал, что конкретный состав, который вы использовали, привел к UB. В этом случае есть способ использовать приведение, который я продемонстрировал в своем ответе.
Спасибо за совет! Я делал такое приведение структур несколько раз на протяжении многих лет. всегда приятно узнать что-то новое!
Короткий ответ: да: используйте указатель, но это плохая практика. Вот MRE с вашим кодом:
#include <stdio.h>
#include <stdint.h>
int main(){
// to-be-confirmed: big-vs-little endian
struct mystruct {
char d;
char c;
char b;
char a;
};
// struct mystruct structinstance;
//1094795585 is in binary: 01000001 01000001 01000001 01000001 -to dez-> 65 65 65 65 -in ASCII-> AAAA
int myint = 1094795586;
// option 1 -- union
union {
struct mystruct s;
uint32_t i;
} decompose;
decompose.i = myint;
printf("%c,%c,%c,%c\n",decompose.s.a, decompose.s.b, decompose.s.c, decompose.s.d);
// option 2 -- bitstuff
printf("%c,%c,%c,%c\n",(myint >> 24), ((myint >> 16) & 0xff), ((myint >> 8) & 0xff), myint & 0xff);
// option 3 -- bad practice
struct mystruct* s = (struct mystruct*)&myint;
printf("%c,%c,%c,%c\n",s->a, s->b, s->c, s->d);
return 0;
}
Обратите внимание, что порядок байтов важен. В моем примере я переименовал/изменил положение ваших mystruct
участников.
В варианте 1 я покажу вам, как это сделать с помощью союза. Это самый чистый и типобезопасный способ сделать это, но вам нужно отслеживать, когда безопасно использовать член «int» или «struct». В данном случае это не имеет значения. Самое приятное в этом то, что вы можете изменить значение буквы, и целое число также изменится.
По моему опыту, вариант 2 — более распространенный способ разложения целого числа. Короче говоря, создайте 1-байтовое окно (0xff) и сдвиньте нужные значения: байт 1 = биты 24–32; байт 2 = 16–24; байт 3 = 8–16; а байт 4 — это всего лишь младшие 8 бит. В этом случае вы должны использовать значения unsigned
, иначе у вас возникнут проблемы с расширением знака.
Вариант 3 — прямой ответ на ваш вопрос. int
находится где-то в памяти, по какому-то адресу X
. Создайте указатель типа mystruct
и наведите его на этот адрес. Затем используйте его как любой другой указатель. В целом это рискованный код, но его можно использовать в нишевых функциях с очень ограниченным поведением.
Хотя это немного сложнее, я бы рекомендовал использовать объединение для ясности. Всегда помните о том, что нужно быть вежливым с беднягой, которому приходится поддерживать ваш код, потому что, скорее всего, это будете вы.
Выход:
A,A,A,B A,A,A,B A,A,A,B
«Создайте указатель типа mystruct и укажите его по этому адресу». Это не разрешено в C, вы вызовете неопределенное поведение. Так что это не просто плохая практика, это ошибка. Что может проявляться, а может и не проявляться в зависимости от компилятора, сборки и т. д. Лучше избегать написания ошибок.
<пожимаю плечами>. UB не означает, что при каждом вызове происходят случайные вещи; это просто означает, что стандарт не определяет поведение. Компилятор сгенерирует нечто предсказуемое. Если это работает, то это работает. Но, как я уже говорил, профсоюз – это путь. Я согласен, что нет смысла писать плохой код, если вы знаете лучше. Хотя это и не «решение», это прямой ответ на конкретный вопрос, который, я надеюсь, ФП оценит.
«что-то, что демонстрирует предсказуемое поведение» Не обязательно. clang, например, прекращает генерировать исполняемый файл в тот момент, когда находит определенный UB — вы не получите полный исполняемый файл, вы получите половину одного. Другие формы UB включают в себя чтение случайных ячеек ОЗУ, которые могут меняться при каждом чтении, а это означает, что поведение будет меняться при каждом выполнении. И т. д. и т. п.
structinstance = (union{struct mystruct m;int i;}){.i=myint}.m;