Есть ли какие-либо гарантии относительно порядка структуры C?

Я широко использовал структуры, и я видел некоторые интересные вещи, особенно*value вместоvalue->first_value где значение является указателем на структуру,first_value самый первый член, это*value безопасный?

Также обратите внимание, что размеры не гарантированы из-за выравнивания, на чем основано значение альгинции, размер архитектуры / регистра?

Мы выравниваем данные / код для более быстрого выполнения, можем ли мы сказать компилятору не делать этого? так что, может быть, мы можем гарантировать определенные вещи о структурах, таких как их размер?

При выполнении арифметики указателей на элементах структуры для определения смещения элемента, я полагаю, что вы делаете- если немного порядковый+ для big endian, или это зависит только от компилятора?

what does malloc(0) really allocate?

The following code is for educational/discovery purposes, its not meant to be of production quality.

#include <stdlib.h>
#include <stdio.h>

int main()
{
    printf("sizeof(struct {}) == %lu;\n", sizeof(struct {}));
    printf("sizeof(struct {int a}) == %lu;\n", sizeof(struct {int a;}));
    printf("sizeof(struct {int a; double b;}) == %lu;\n", sizeof(struct {int a; double b;}));
    printf("sizeof(struct {char c; double a; double b;}) == %lu;\n", sizeof(struct {char c; double a; double b;}));

    printf("malloc(0)) returns %p\n", malloc(0));
    printf("malloc(sizeof(struct {})) returns %p\n", malloc(sizeof(struct {})));

    struct {int a; double b;} *test = malloc(sizeof(struct {int a; double b;}));
    test->a = 10;
    test->b = 12.2;
    printf("test->a == %i, *test == %i \n", test->a, *(int *)test);
    printf("test->b == %f, offset of b is %i, *(test - offset_of_b) == %f\n",
        test->b, (int)((void *)test - (void *)&test->b),
        *(double *)((void *)test - ((void *)test - (void *)&test->b))); // find the offset of b, add it to the base,$

    free(test);
    return 0;
}

призваниеgcc test.c с последующим./a.out Я получаю это:

sizeof(struct {}) == 0;
sizeof(struct {int a}) == 4;
sizeof(struct {int a; double b;}) == 16;
sizeof(struct {char c; double a; double b;}) == 24;
malloc(0)) returns 0x100100080
malloc(sizeof(struct {})) returns 0x100100090
test->a == 10, *test == 10 
test->b == 12.200000, offset of b is -8, *(test - offset_of_b) == 12.200000

Update это моя машина:

gcc --version

i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

uname -a

Darwin MacBookPro 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun  7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386
 Samy Vilar15 июн. 2012 г., 21:44
Хорошо, я уберу это.
 dmckee15 июн. 2012 г., 21:44
"Also whats malloc(0) allocating? " Arghhhhhh! Один вопрос за раз, пожалуйста. Две вещи, которые вы задали здесь, не имеют ничего общего друг с другом, они должны быть в разных вопросах.

Ответы на вопрос(4)

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

Так*value а такжеvalue->first обращается к памяти по одному и тому же адресу (но использует разные типы) в следующих

struct St {
  long first;
} *value;

Кроме того, порядок между членами структуры гарантированно совпадает с порядком объявления

Чтобы настроить выравнивание, вы можете использовать специфичные для компилятора директивы или использовать битовые поля.

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

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

 15 июн. 2012 г., 21:54
Это в значительной степени должно быть. Как бы низкоуровневый C ни был, он нанес бы ущерб, если бы структуры могли интерпретироваться по-разному, скажем, между компилятором, который вы используете, и тем, который создавал вашу ОС.
 Samy Vilar15 июн. 2012 г., 21:53
Also, the ordering between memebers of the structure is guaranteed to be the same as the declaration order это часть стандарта C?
 15 июн. 2012 г., 22:03
@ samy.vilar - В C (и C ++) языковые конструкции очень часто имеют тесную связь с аппаратными архитектурами (по крайней мере, с наиболее распространенными во время их создания)
 Samy Vilar15 июн. 2012 г., 21:58
Интересно, но я всегда думал, что структуры, в которых язык не имеет ничего общего с базовой аппаратной архитектурой, но я думаю, что они нуждаются в некоторой согласованности при перемещении данных между ОС и базовым скомпилированным кодом.
 15 июн. 2012 г., 22:42
@ samy.vilar - как я уже говорил, выравнивание (таким образом, "padding") обычно основано на аппаратном обеспечении. Вы правы в том, что вся структура не соответствует чему-то конкретному на аппаратном обеспечении, но ее (предельно примитивные) члены делают это, что влияет на саму структуру
Решение Вопроса

I've used structs extensively and I've seen some interesting things, especially *value instead of value->first_value where value is a pointer to struct, first_value is the very first member, is *value safe?

Да,*value безопасно; это дает копию структуры, котораяvalue указывает на. Но почти гарантированно иметь другой тип*value->first_valueтак что результат*value почти всегда будет отличаться от*value->first_value.

Counter-example:

struct something { struct something *first_value; ... };
struct something data = { ... };
struct something *value = &data;
value->first_value = value;

При этом довольно ограниченном наборе обстоятельств вы получите тот же результат от*value а также*value->first_value, Согласно этой схеме типы будут одинаковыми (даже если значения не совпадают). В общем случае тип*value а также*value->first_value бывают разных типов.

Also note that sizes aren't guaranteed because of alignment, but is alignment always on register size?

Так как «размер регистра» не является определенной концепцией C, не ясно, о чем вы спрашиваете. При отсутствии прагм (#pragma pack или аналогичные), элементы структуры будут выровнены для оптимальной производительности, когда значение будет прочитано (или записано).

We align data/code for faster execution; can we tell compiler not to do this? So maybe we can guarantee certain things about structs, like their size?

Компилятор отвечает за размер и расположениеstruct типы. Вы можете повлиять на тщательный дизайн и, возможно,#pragma pack или аналогичные директивы.

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

When doing pointer arithmetic on struct members in order to locate member offset, I take it you do subtraction if little endian, addition for big endian, or does it just depend on the compiler?

Возможно, вам лучше не делать арифметику с указателями наstruct члены. Если нужно, используйтеoffsetof() макрос из<stddef.h> правильно обрабатывать смещения (и это означает, что вы не выполняете арифметику указателя напрямую). Первый элемент структуры всегда находится по самому низкому адресу, независимо от значения порядка байтов. Действительно, порядок байтов не имеет отношения к расположению различных элементов в структуре; это влияет только на порядок следования байтов значений внутри (базового типа данных) члена структуры.

Стандарт C требует, чтобы элементы структуры были расположены в том порядке, в котором они определены; первый элемент находится по самому низкому адресу, а следующий - по более высокому адресу и так далее для каждого элемента. Компилятору не разрешено изменять порядок. Перед первым элементом структуры не может быть отступов. После любого элемента структуры может быть заполнение, поскольку компилятор сочтет нужным обеспечить то, что он считает соответствующим выравниванием. Размер структуры таков, что вы можете выделить (N & # xD7; размер) байты, которые соответствующим образом выровнены (например, черезmalloc()) и обработать результат как массив структуры.

 Samy Vilar15 июн. 2012 г., 22:32
+1; заUnder that scheme, the types would be the same.
 Samy Vilar15 июн. 2012 г., 22:34
+1; заThe compiler is in charge of the size and layout of struct types. You can influence by careful design and perhaps by #pragma pack or similar directives.
 15 июн. 2012 г., 22:39
Предложение без комментария в скобках является правильным. Комментарий в скобках является поддельным. Свидетель:struct InteriorPad { char c; double d; };  В большинстве систем будет 7 байт заполненияc а такжеd.
 15 июн. 2012 г., 22:17
Если у вас есть:struct something { struct something *first_value; ... }; а такжеstruct something data = { ... }; а такжеstruct something *value = &data; а такжеvalue->first_value = value;то вы получите тот же результат от*value а также*value->first_value, По этой схеме типы будут одинаковыми. В общем случае тип*value а также*value->first_value бывают разных типов.
 Samy Vilar15 июн. 2012 г., 22:03
вы предполагаете*value->first_value это указатель, может быть, а может и не быть, и да, он будет иметь другой тип, так как он всегда был привязан к соответствующему типу, я задал этот вопрос в образовательных целях, всегда хорошая идея, чтобы понять, как все работает под капотом :). ..

От 6.2.5 / 20:

A structure type describes a sequentially allocated nonempty set of member objects (and, in certain circumstances, an incomplete array), each of which has an optionally specified name and possibly distinct type.

Отвечать:

especially *value instead of value->first_value where value is a pointer to struct, first_value is the very first member, is *value safe?

см. 6.7.2.1/15:

15 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.1

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

В С,malloc( 0 ) определяется реализация. (Как примечание, это одна из тех мелочей, где C и C ++ отличаются.)

[1] Акцент мой.

 15 июн. 2012 г., 22:07
Да, это для соблюдения ограничений выравнивания. Порядковый порядок и выравнивание - две разные вещи. УвидетьData Structure Alignment для деталей.
 Samy Vilar15 июн. 2012 г., 21:49
sequentially allocated nonempty set of member objects так что безопасно для меня предположить, что порядок членов гарантирован? наборы причин не упорядочены, не так ли?
 Samy Vilar15 июн. 2012 г., 22:19
да, я знаю, но смещение -8 байтов застало меня врасплох, я ожидал 4 или -4, так как первый член был int.
 15 июн. 2012 г., 21:55
@ samy.vilar: Да, заказ гарантирован. Здесь терминset используется не в строгом математическом смысле, но для обозначения группировки типизированных членов.
 Samy Vilar15 июн. 2012 г., 22:04
There may however be padding bytes at the end of the structure (but never in-between members). это правда? почему было мое смещение 8 байт? это потому, что его немного порядковый номер? Как насчет выравнивания данных, не будет ли компилятор правильно заполнять каждый элемент для более быстрого доступа?

Важно узнать, как работает размер структуры. например:

struct foo{
  int i;
  char c;
}

struct bar{
  int i;
  int j;
}

struct baz{
  int i;
  char c;
  int j;
}

sizeof(foo) = 8 bytes (32 bit arch)
sizeof(bar) = 8 bytes
sizeof(baz) = 12 bytes

Это означает, что размеры структуры и смещения должны следовать двум правилам:

1. Структура должна быть кратна первому элементу (почему foo равно 8, а не 5 байтам).

2- Элемент структуры должен начинаться с кратного самого себя. (В baz, int j не может начинаться с 6, поэтому байты 6, 7 и 8 - пустое заполнение

 15 июн. 2012 г., 21:58
Ооо Был ли мой класс на ассемблере ложью? Я сейчас читаю, удаляю, если это ложно
 15 июн. 2012 г., 21:57
(Я понимаю, что это не полностью отвечает на ваш вопрос, просто ошибка, которую я часто вижу в коде)
 15 июн. 2012 г., 21:57
Я уверен, что стандарт C не требует выравнивания. Имея в видуint j could начать с 6-го байта. Это архитектура, которая определяет, является ли это законным.
 15 июн. 2012 г., 22:03
Я мог бы упомянуть, что это "обычно" сделано так (что это). Это неnecessarily таким образом, но это достаточно распространено, чтобы упоминать о нем.
 15 июн. 2012 г., 22:00
Сборка не является C. :) И сам C не связан конкретными правилами CPU. Но, как выясняется, x86 допускает и невыровненные значения, во многих случаях ... это просто отстой производительности с ними.

Ваш ответ на вопрос