Вали - звонить бесплатно с указателем на первого члена?

Можно ли вызывать free для указателя, который указывает на первый член структуры (а структура связана с malloc)? Я знаю, что в принципе указатель в любом случае указывает на то, что нужно ...

struct s {int x;};
//in main
struct s* test;
test = (struct s*) malloc(sizeof(*test));
int* test2;
test2 = &(test->x);
free(test2); //is this okay??

Кроме того, изменится ли ответ, если int x заменить структурой?

Обновление: зачем мне писать такой код?

struct s {int x;};
struct sx1 {struct s test; int y;}; //extending struct s
struct sx2 {struct s test; int z;}; //another
// ** some functions to keep track of the number of references to each variable of type struct s 
int release(struct s* ptr){
  //if the number of references to *ptr is 0 call free on ptr
}
int main(){
    struct sx1* test1;
    struct sx2* test2;
    test1 = (sx1*) malloc(sizeof(*sx1));
    test2 = (sx2*) malloc(sizeof(*sx2));
    //code that changes the number of references to test1 and test2, calling functions defined in **
    release(test1);
    release(test2);
}

person user71815    schedule 13.04.2016    source источник
comment
@ user71815 Не считаю нужным, лучше скомпилируйте и проверьте. (и дайте нам знать)   -  person Akash Chandwani    schedule 13.04.2016
comment
Зачем вам вообще писать такой код?   -  person Lundin    schedule 13.04.2016
comment
Это определенно странное занятие. Я бы никогда не стал писать такой код. Почему бы просто не освободить исходный указатель вместо того, чтобы пытаться вывести его из адреса члена структуры?   -  person Tom Karzes    schedule 13.04.2016
comment
@TomKarzes да - что происходит, когда кто-то с самого начала вставляет что-то другое?   -  person Martin James    schedule 13.04.2016
comment
Удалите этот вопрос, если он уже задавался. Нет. Другие люди могут счесть это полезным несколько лет спустя.   -  person nalzok    schedule 13.04.2016
comment
@ user71815 При редактировании вы, кажется, освобождаете тот же указатель, который получил от malloc? Если да, то это не тот же случай, что и ваш исходный вопрос.   -  person Lundin    schedule 13.04.2016
comment
@ user71815 Хорошо, я понимаю, что вы пытаетесь сделать, т.е. использовать префикс общей структуры для структур переменных. Но вы все равно должны иметь возможность использовать указатель верхнего уровня, не обращаясь к первому члену для получения адреса.   -  person Tom Karzes    schedule 13.04.2016
comment
@TomKarzes: код, который должен получать один из нескольких типов структур с общим префиксом, воздействовать на переданный объект, а затем освобождать его, может получить только указатель на структуру с общим префиксом, а не указатель на содержащую структуру .   -  person supercat    schedule 13.04.2016
comment
Возможный дубликат в C, всегда ли указатель на структуру указывает на ее первый член?   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 08.05.2016


Ответы (2)


Да, это нормально.

6.7.2.1

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

Это означает, что это определяется:

struct s {int x;};
struct s* test;
test = (struct s*) malloc(sizeof(*test));
int* p = &(test->x);
free(p);
person 2501    schedule 13.04.2016
comment
Да, это нормально в том смысле, что поведение определяется стандартом и, вероятно, было задумано. Но нет, это не в том смысле, что это плохой стиль, поскольку он открывает возможности для неожиданных сбоев в дальнейшем, в случае изменения определения структуры. - person John Bollinger; 13.04.2016
comment
Стандарт не гарантирует, что указатель на один тип идентичен указателю на другой тип, за исключением char *. Таким образом, нет, это не нормально. Это просто работает (почти всегда) со всеми последними реализациями, потому что они используют единое адресное пространство. - person too honest for this site; 13.04.2016
comment
Даже если эти два указателя s и p имеют разный размер, разное выравнивание и разное представление, они все равно указывают на один и тот же адрес и тот же объект, и, таким образом, использование любого из них для освобождения памяти строго соответствует. - person 2501; 13.04.2016
comment
Они определенно не указывают на один и тот же объект. Причина должна быть ясна: типы различаются. Я с вами по поводу адреса. 7.22.3.3p2 тоже актуален. Учитывая преобразования в / из void *, вы, кажется, правы, но это разрешено. Я все еще считаю, что это плохая практика и может сбить с толку средства проверки статического кода. В любом случае, я снимаю свое возражение, но в вашем ответе должно быть больше информации. - person too honest for this site; 13.04.2016
comment
@Olaf: Принятие адреса первого элемента структуры эквивалентно преобразованию адреса этой структуры в этот тип. Эта операция может в некоторых реализациях включать изменение битового представления указателя, но если один начинается с ненулевого void *, возвращаемого из malloc, преобразование указателя через любую последовательность типов указателя на данные и обратно в void * является гарантированно даст исходный указатель. - person supercat; 13.04.2016
comment
@supercat: Именно поэтому я передумал. Но спасибо, что доработали немного больше, новички это оценят. Я бы все равно отклонил такой код, потому что он (без необходимости) запутывает намерение. - person too honest for this site; 14.04.2016
comment
@Olaf: если у кого-то есть несколько структур, которые имеют общий префикс, и ему нужна функция, которая может работать с любой из них, у вас может не быть способа узнать исходный тип структуры по указателю (особенно если общий префикс включает указатели на функции, которые работают с рассматриваемым типом - в этом случае тип мог даже не существовать, когда был написан код, получающий указатель). - person supercat; 14.04.2016
comment
@supercat: В чем будет разница при передаче указателя на сам struct? Функция, принимающая, например, int *, но free использование struct неявно - это очень плохой стиль кодирования. Речь идет не о поиске исходной структуры, а об использовании другого типа указателя для освобождения объекта. Я бы пнул любого, кто покажет мне такой код там, где это действительно больно. - person too honest for this site; 14.04.2016
comment
@Olaf: Как я уже сказал, обычно это не делается с примитивами, а скорее с вложенными структурами, используемыми для достижения той же семантики, которую предлагают объектно-ориентированные языки с наследованием. В таких языках, как Java, C # и т. Д., Если один определяет тип Animal и имеет типы Cat и Dog, производные от животного, код, ожидающий ссылки на Animal, может принимать ссылку на Cat или Dog. Для достижения этой функциональности в C типы Cat и Dog должны начинаться с Animal в качестве своего первого поля и кода, который ожидает получить указатель на какую-то структуру, производную от _10 _... - person supercat; 14.04.2016
comment
... примет *Animal. Альтернативный подход использует тот факт, что компиляторы интерпретировали общее правило начальной подпоследовательности как применяемое к указателям приведения типов точно так же, как оно применяется к объединениям, но такая обработка компиляторами стала немодной. Передавать указатель на вложенную структуру неприятно, но это единственный способ, которым гиперсовременные компиляторы позволяют коду получать указатель на структуру, точный тип которой неизвестен, но чья начальная последовательность известна. - person supercat; 14.04.2016
comment
@supercat Взгляните на gcc -fplan9-extensions. Это может показаться вам очень интересным. Нет необходимости явно использовать первый член. - person too honest for this site; 14.04.2016
comment
@Olaf: Это не является частью Стандарта. Передавать указатель на первый член неприятно, но это разрешено в строго соответствующем коде. Лично я бы хотел видеть директивы для определения модели псевдонимов; одна модель, которую должен поддерживать каждый компилятор, предполагает, что что-либо может быть псевдонимом (поэтому программы, включающие такую ​​директиву, будут указаны для работы независимо от обычных предположений компилятора о псевдониме), и модель, которая обычно намного строже, чем стандартный , но будет включать встроенные функции, с помощью которых программисты могут определять места, где требуется псевдоним. - person supercat; 14.04.2016
comment
@Olaf: Исключение типов символов из правил псевдонима - это ужасный ужасный прием, который с самого начала должен был быть признан устаревшим, поскольку он может значительно ухудшить производительность кода, который должен работать с большим количеством реальных символов- тип данных, поэтому директива об отказе от этого исключения может значительно улучшить производительность такого кода. Кроме того, код, который использует символьные указатели или memcpy внутри цикла, будет блокировать компилятор от кэширования глобальных переменных через цикл; позволяя программисту указать перед циклом, что вещи типа X ... - person supercat; 14.04.2016
comment
... будет использоваться исключительно, поскольку тип Y внутри цикла позволит компилятору гарантировать, что любые глобальные переменные типа X синхронизируются с памятью до начала цикла и снова после его завершения, но позволит избежать необходимости какой-либо синхронизации внутри цикла. - person supercat; 14.04.2016
comment
Позвольте нам продолжить это обсуждение в чате. - person too honest for this site; 14.04.2016

Согласно стандарту C11, глава §6.7.2.1

[...] Внутри объекта структуры может быть безымянное заполнение, но не в его начале.

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

free() требуется указатель, который ранее был возвращен malloc() или семейством.

В вашем случае вы передаете тот же адрес, который вернул malloc(). Итак, все готово.

person Sourav Ghosh    schedule 13.04.2016