информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
Атака на InternetЗа кого нас держат?
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Крупный взлом GoDaddy 
 Просроченный сертификат ломает... 
 Phrack #70/0x46 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
если вы видите этот текст, отключите в настройках форума использование JavaScript
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
Все верно, но не совсем. main это 21.11.05 11:50  Число просмотров: 1499
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка> <обсуждение закрыто>
> Мне интересен стандарт, а не придумщики этих языков.
> Часто, то, что мы используем, и то, что норма для
> партикулар компилер - вовсе не стандарт.
> Например, void main (void) - норма для Borland &
> Microsoft. Но это нарушение стандарта.

Все верно, но не совсем. main это
#ifdef __cplusplus
extern "C"
#endif
int main(int argc, char *arvg[]);

То бишь соглашение о линковке - сишное. Таким образом линкер видит только _main, рантайм честно разрезает командную строку на argc/argv и передает в main, только они не используются, а в качестве возвращаемого значения в тот же msvc/x86 (в других компиляторах - аналогично) использует тот мусор, который окажется в eax на момент выхода. Кроме того, стандарт не говорит о том, чтобы функция main была ОБЪЯВЛЕНА где либо в стандартной библиотеке (чтобы повторное объявление с другими типами вызывало ошибку). Короче, ЛЮБОЙ стандартный компилятор не должен давиться от

void main(void)

> > Например для связки AMD/GCC структура, состоящая из
> трех
> > однобайтовых элементов может быть расширена до
> > четырехбайтового размера.

> 1) практическивсегдабудет расширена. выравнивание по
> границе байта очень редко используется; и, думаю, никогда
> по умолчанию.

Не совсем так. Выравнивание для байта - 1, для слова - 2 и т.д., таким образом
struct {char a, b, c};
практически никогда не будет расширяться, если не задать явный алигн. Надо понимать, что место добавляется не ПОСЛЕ элемента, а ПЕРЕД. То бишь байту по фигу куда размещаться, а вот если после байта идет long, то появится дырка для алигна. Причем структура как бы зациклена (как раз чтобы не возникало проблем с массивами). То бишь, после последнего элемента добавляется столько места, чтобы нормально выравнять первый.

#include "stdio.h"

struct test1 {
	char c1, c2, c3;
};

struct test2 {
	long l;
	char c;
};

struct test3{
	long double l;
	char c;
};

int
main() {
	printf("%d\t%d\n", sizeof(struct test1), sizeof(struct test1[10]));
	printf("%d\t%d\n", sizeof(struct test2), sizeof(struct test2[10]));
	printf("%d\t%d\n", sizeof(struct test3), sizeof(struct test3[10]));
	return 0;
}

---

даст
3	30
8	80
16	160

---

> 2)
> struct __declspec(align(32)) a
> {
> byte val3;
> WORD val4;
> };
>
> sizeof вообще вернёт 32 байта ;)

> > Размер структуры должен учитывать все "дырки" для
> > правильного динамического выделения памяти для масива
> > структур.
>
> Ты не прав здесь немного, имхо. Опреции с памятью всегда
> будут коректными, если используешь sizeof.
> sizeof всегда вернёт тебе правильное значение размера
> структуры, независимо от выравнивания структуры по границе
> байта, слова и т.д. Важно здесь другое - не делать сильных
> предположений о размере структуры (типа #define
> STRUCTURE_SIZE .... ) при распределении памяти, или
> навигации внутри массивов.

Навигация внутри массива - вполне безопасное действие, а вот в остальном - согласен. Вместо STRUCTURE_SIZE надо использовать sizeof(structure), а вместо жестко заданного смещения, макрос типа

#define OFFSET_OF(type, field) ((unsigned long)(((type *)0)->field))

> ПС. В контексте топика. amirul прав, на мой взгляд. "дыры"
> всегда будут покрыты 0 memset-ом, если использовать sizeof
> для всей структуры.
<programming>
[C++] Инициализация структуры... 18.11.05 05:12  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка> <обсуждение закрыто>
Уж, как избили тему. Но всёже...

Положим
struct a
{
int val1;
int val2;
...
...
};

Есть некая функция func(), где надо объявить локалную переменную struct a, и инициализировать её нулями.

Обычно делают так:

void func()
{
a inst;
memset( &inst, 0, sizeof(a))

...
...
}

Про подводные камни здесь мы уже говорили....

Более безопасным, и, кстати, гораздо более кратким и выразительным может быть (а может и не быть ;)) ) такой манер:

void func()
{
a inst = {0};
...
...
}

Кстати, a inst = {1,1} тоже вполне работает . Так вот, не могу найти в стандарте С++ это дело. Где про это растолковано? Это и есть мой вопрос.

Спасибо.

[C++] ISO/IEC 14882 Раздел 8.5.1 Aggregates [dcl.init.aggr] [update] 18.11.05 11:48  
Автор: amirul <Serge> Статус: The Elderman
Отредактировано 18.11.05 12:03  Количество правок: 1
<"чистая" ссылка> <обсуждение закрыто>
> void func()
> {
> a inst = {0};

Тогда уж лучше

a inst = {};

> ...
> }
>
> Кстати, a inst = {1,1} тоже вполне работает . Так вот, не
> могу найти в стандарте С++ это дело. Где про это
> растолковано? Это и есть мой вопрос.

[skipped]
7 If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized (8.5). [Example:
struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0. ]
[skipped]

Ну и на всякий случай раздел 8.5 Initializers [dcl.init]
[skipped]
5 To zero-initialize an object of type T means:
— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
— if T is a non-union class type, each nonstatic data member and each base-class subobject is zeroinitialized;
— if T is a union type, the object’s first named data member89) is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.

To default-initialize an object of type T means:
— if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, the object is zero-initialized.

To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized
[skipped]

> Спасибо.

--------------

POD, если кто вдруг не знает - это Plain Old Data. То бишь структура в старом C-шном понимании (без методов, конструкторов и прочей фигни)
Спасибо. Блин. Мне стыдно. Как я смотрел.... Ведь смотрел... 19.11.05 08:45  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка> <обсуждение закрыто>
Спасибо. Блин. Мне стыдно. Как я смотрел.... Ведь смотрел (правда "прокручивал" очень быстро ;)) жешь и этот раздел тоже! Не увидел. ;))
В Ричи и Кернигане точно есть. У Страустрапа тоже должно... 18.11.05 11:18  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
Отредактировано 18.11.05 11:24  Количество правок: 2
<"чистая" ссылка> <обсуждение закрыто>
> Кстати, a inst = {1,1} тоже вполне работает . Так вот, не
> могу найти в стандарте С++ это дело. Где про это
> растолковано? Это и есть мой вопрос.

В Ричи и Кернигане точно есть. У Страустрапа тоже должно быть, порыться надо, но смысл?...
Подводные камни не в этом!
Явный акцент делается на массив структур. Упоминается, что компилятор для оптимизации работ строк кэша может размещать элементы массива структур с адреса кратного степени двойки, а размер структуры может быть больше, чем сумма размера ее элементов.
Причем все зависит от компилятора. Во многих есть ключик выравнивания размеров структур.
Например для связки AMD/GCC структура, состоящая из трех однобайтовых элементов может быть расширена до четырехбайтового размера. Тогда массив из двух структур инициализировать при помощи memset() нужно будет надлежащим образом, учитывая эти особенности.
А, вот, к стати по поводу распределения адресного пространства говорится, что оно должно распределяться по порядку (насколько я помню). Не помню только возможны ли "дырки"? То есть
struct {
char a;
long b;
} s;
sizeof( s.a ) + sizeof( s.b ) != sizeof ( struct s );
1 + 4 != 8
&s.a < &s.b
то есть "a" не переедет в конец - смысла нет.

У структуры
struct {
short a;
long b;
short c;
} st;
размер может быть 12 байт, а не 8.
с 0 адреса будет "a", размером 2 байта
с 2 адреса будет "дырка", размером 2 байта
с 4 адреса будет "b", размером 4 байта
с 8 адреса будет "c", размером 2 байта
с 10 адреса опять "дырка", размером 2 байта
Элемент "c" может не переехать на адрес 2, не смотря на глубокий смысл.
Размер структуры должен учитывать все "дырки" для правильного динамического выделения памяти для масива структур.
Некоторые особенности могут зависеть от компиляторов и стандартов.

---

> Спасибо.
>
Спасибо за реакцию. 19.11.05 09:55  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка> <обсуждение закрыто>
Спасибо за реакцию.

> В Ричи и Кернигане точно есть. У Страустрапа тоже должно
> быть, порыться надо, но смысл?...

Мне интересен стандарт, а не придумщики этих языков. Часто, то, что мы используем, и то, что норма для партикулар компилер - вовсе не стандарт.
Например, void main (void) - норма для Borland & Microsoft. Но это нарушение стандарта.


> Подводные камни не в этом!
> Явный акцент делается на массив структур. Упоминается, что
> компилятор для оптимизации работ строк кэша может размещать
> элементы массива структур с адреса кратного степени двойки,
> а размер структуры может быть больше,
> чем сумма размера ее элементов.

<skip>

Как раз подводные камни не в этом. Если ты используешь sizeof(), то это тебя гарантирует, что размер всегда будет определён верный. Проблема (вернее типовая ошибка) в том, если девелопер иcпользует:

1) #define MYSRTUCTSIZE ... вместо sizeof() хоть для структур, хоть для их массивов влюбыхоперациях с памятью.

2) когда пытается адресовать поля внутри структуры используя указатель на структуру и некий офсет, определённый в соответствии с той картой распределения памяти, которая кажется программеру действительной (т.е. когда девелопер делает сильные предположения о карте памяти структуры):

struct a {
byte v1;
WORD v2;
};

a inst1;
a* pt = & inst1;
byte * pt2 = (byte*) pt;

// сомнительное присваивание v2 = 1
* ( ++pt2 ) = 1;

Пример выше почтиникогдане будет правильно работать на современных системах (мы никогда таким образом не изменим inst1.v2, кроме, как хакнем сами себя;)) ),
если только не поставить силой:

#pragma pack(1)


> Например для связки AMD/GCC структура, состоящая из трех
> однобайтовых элементов может быть расширена до
> четырехбайтового размера.

1) практическивсегдабудет расширена. выравнивание по границе байта очень редко используется; и, думаю, никогда по умолчанию.

2)
struct __declspec(align(32)) a
{
byte val3;
WORD val4;
};

sizeof вообще вернёт 32 байта ;)

> инициализировать при помощи memset() нужно будет надлежащим
> образом, учитывая эти особенности.
> А, вот, к стати по поводу распределения адресного
> пространства говорится, что оно должно распределяться по
> порядку (насколько я помню).

Нет, чуть-чуть не так... гарантируется только выделение сплошного (непрерывного) куска памяти.
карта памятине_гарантируется Порядок в смыле очерёдности будет, но про расположение отдельных полей - см.выше (нет гарантии). Максимум, что ты можешь сделать - изменять align для всего проекта, модуля или отдельной структуры.

> "дырки"? То есть

Конечно есть.


> Размер структуры должен учитывать все "дырки" для
> правильного динамического выделения памяти для масива
> структур.

Ты не прав здесь немного, имхо. Опреции с памятью всегда будут коректными, если используешь sizeof.
sizeof всегда вернёт тебе правильное значение размера структуры, независимо от выравнивания структуры по границе байта, слова и т.д. Важно здесь другое - не делать сильных предположений о размере структуры (типа #define STRUCTURE_SIZE .... ) при распределении памяти, или навигации внутри массивов.

ПС. В контексте топика. amirul прав, на мой взгляд. "дыры" всегда будут покрыты 0 memset-ом, если использовать sizeof для всей структуры.
Меня в свое время этот момент не сильно удивил. Тот момент,... 21.11.05 12:01  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
Отредактировано 21.11.05 12:04  Количество правок: 2
<"чистая" ссылка> <обсуждение закрыто>
> Спасибо за реакцию.
>
> > В Ричи и Кернигане точно есть. У Страустрапа тоже
> должно
> > быть, порыться надо, но смысл?...
>
> Мне интересен стандарт, а не придумщики этих языков.
> Часто, то, что мы используем, и то, что норма для
> партикулар компилер - вовсе не стандарт.
> Например, void main (void) - норма для Borland &
> Microsoft. Но это нарушение стандарта.

Меня в свое время этот момент не сильно удивил. Тот момент, когда в компиллере с оболочкой (который я недолюбливаю), в опциях компиляции можно было выбрать стандарт компиляции. И было их там штук пяток минимум.
Может употреблять слово "стандарт" и некорректно, но оно наиболее для этого подходит и все мы поймем о чем идет речь.
Меня несколько удивило, что в списке первым стоял "Ричи и Керниган". Был там и АНСИ, и ИСО, и, помнится, Страустрап тоже был уже.
Почему же "Р&К" не может быть стандартом? Ну придумали они его, описали, отладили. Язык, конечно, развивается, а стандарты изменяются. Глупо как-то может получится - Р&К описали все ключевые слова, программер написал программу, руководствуясь именно тем стандартом, именно в то время. Более новые компилер ругается на неправильное использование ключевого слова void, а Р&К об этом ключевом слове и не упоминали, позже оно появилось, много других позже появилось, вот программист и использовал его для названия переменной. Чтож теперь старые исходники и компильнуть нельзя или перелопачивать под новый стандарт надо. А если они огромные. Все-таки это тоже "стандарт" по-моему.
Все верно, но не совсем. main это 21.11.05 11:50  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка> <обсуждение закрыто>
> Мне интересен стандарт, а не придумщики этих языков.
> Часто, то, что мы используем, и то, что норма для
> партикулар компилер - вовсе не стандарт.
> Например, void main (void) - норма для Borland &
> Microsoft. Но это нарушение стандарта.

Все верно, но не совсем. main это
#ifdef __cplusplus
extern "C"
#endif
int main(int argc, char *arvg[]);

То бишь соглашение о линковке - сишное. Таким образом линкер видит только _main, рантайм честно разрезает командную строку на argc/argv и передает в main, только они не используются, а в качестве возвращаемого значения в тот же msvc/x86 (в других компиляторах - аналогично) использует тот мусор, который окажется в eax на момент выхода. Кроме того, стандарт не говорит о том, чтобы функция main была ОБЪЯВЛЕНА где либо в стандартной библиотеке (чтобы повторное объявление с другими типами вызывало ошибку). Короче, ЛЮБОЙ стандартный компилятор не должен давиться от

void main(void)

> > Например для связки AMD/GCC структура, состоящая из
> трех
> > однобайтовых элементов может быть расширена до
> > четырехбайтового размера.

> 1) практическивсегдабудет расширена. выравнивание по
> границе байта очень редко используется; и, думаю, никогда
> по умолчанию.

Не совсем так. Выравнивание для байта - 1, для слова - 2 и т.д., таким образом
struct {char a, b, c};
практически никогда не будет расширяться, если не задать явный алигн. Надо понимать, что место добавляется не ПОСЛЕ элемента, а ПЕРЕД. То бишь байту по фигу куда размещаться, а вот если после байта идет long, то появится дырка для алигна. Причем структура как бы зациклена (как раз чтобы не возникало проблем с массивами). То бишь, после последнего элемента добавляется столько места, чтобы нормально выравнять первый.

#include "stdio.h"

struct test1 {
	char c1, c2, c3;
};

struct test2 {
	long l;
	char c;
};

struct test3{
	long double l;
	char c;
};

int
main() {
	printf("%d\t%d\n", sizeof(struct test1), sizeof(struct test1[10]));
	printf("%d\t%d\n", sizeof(struct test2), sizeof(struct test2[10]));
	printf("%d\t%d\n", sizeof(struct test3), sizeof(struct test3[10]));
	return 0;
}

---

даст
3	30
8	80
16	160

---

> 2)
> struct __declspec(align(32)) a
> {
> byte val3;
> WORD val4;
> };
>
> sizeof вообще вернёт 32 байта ;)

> > Размер структуры должен учитывать все "дырки" для
> > правильного динамического выделения памяти для масива
> > структур.
>
> Ты не прав здесь немного, имхо. Опреции с памятью всегда
> будут коректными, если используешь sizeof.
> sizeof всегда вернёт тебе правильное значение размера
> структуры, независимо от выравнивания структуры по границе
> байта, слова и т.д. Важно здесь другое - не делать сильных
> предположений о размере структуры (типа #define
> STRUCTURE_SIZE .... ) при распределении памяти, или
> навигации внутри массивов.

Навигация внутри массива - вполне безопасное действие, а вот в остальном - согласен. Вместо STRUCTURE_SIZE надо использовать sizeof(structure), а вместо жестко заданного смещения, макрос типа

#define OFFSET_OF(type, field) ((unsigned long)(((type *)0)->field))

> ПС. В контексте топика. amirul прав, на мой взгляд. "дыры"
> всегда будут покрыты 0 memset-ом, если использовать sizeof
> для всей структуры.
Получается, что все от компилятора зависит и нет стандартов... 21.11.05 12:21  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
Отредактировано 21.11.05 12:26  Количество правок: 1
<"чистая" ссылка> <обсуждение закрыто>
> Не совсем так. Выравнивание для байта - 1, для слова - 2 и
> т.д., таким образом
> struct {char a, b, c};
> практически никогда не будет расширяться, если не задать
> явный алигн. Надо понимать, что место добавляется не ПОСЛЕ
> элемента, а ПЕРЕД. То бишь байту по фигу куда размещаться,
> а вот если после байта идет long, то появится дырка для
> алигна. Причем структура как бы зациклена (как раз чтобы не
> возникало проблем с массивами). То бишь, после последнего
> элемента добавляется столько места, чтобы нормально
> выравнять первый.

Получается, что все от компилятора зависит и нет стандартов на выравнивание...
(Хотя программисту это не очень то и надо знать.)
Для чего нужно выравнивание - для увеличения скорости работы за счет увеличения скорости доступа к памяти. Елиб не было архитектур на которых выигрыш есть, то никто о выравнивании границ и не говорил, поскольку не было бы пользы кроме вреда - нерачительного использования памяти.
Если после какой-то степени двойки эффект отпадает, то и нечего позря эту память выделять. То есть если эффект после 4 байт пропадает, то нет смысла выравнивать на 8 байтовцю границу, не смотря на "lond double". Глубоко убежден, что мало какие компиляторы доберуться до 8 байтового выравнивания. И что самое удивительное есть такие, которые плюют на слово "long" в сочетании "long double".
Код в циклах многие компиляторы тоже выравнивают на 4-х байтовую границу.
При выравнивании на 8 байтовую может возникнуть только потеря быстродействия из-за трансфера ненужной информации, когда структура передается в качестве аргумента.

Что касается примера, то структуру из трех байтовых элементов очень даже имеет смысл выравнить до слова - ЗНАЧИТЕЛЬНО быстрее будет обрабатываться ее возврат из функции.
Именно. В стандарте очень много оставлено либо полностью на... 21.11.05 13:43  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка> <обсуждение закрыто>
> Получается, что все от компилятора зависит и нет стандартов
> на выравнивание...
> (Хотя программисту это не очень то и надо знать.)

Именно. В стандарте очень много оставлено либо полностью на откуп реализации либо заданы какие то условия. Например: по фигу как будет реализован std::list, главное, чтобы он предоставлял операции вставки/удаления элементов в любое место за константное время, предоставлял прямые и реверсивные итераторы, операция произвольного доступа - необязательна. То же самое об остальных контейнерах библиотеки. Или к примеру стандарт ничего не говорит об абсолютных размерах базовых типов, вводится только ограничение
sizeof(char) == 1 (все остальные длины меряются в размерах char-а, а не в байтах)
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)

Таких примеров до фига

> Для чего нужно выравнивание - для увеличения скорости
> работы за счет увеличения скорости доступа к памяти. Елиб
> не было архитектур на которых выигрыш есть, то никто о
> выравнивании границ и не говорил, поскольку не было бы
> пользы кроме вреда - нерачительного использования памяти.

Полностью согласен.

> Если после какой-то степени двойки эффект отпадает, то и
> нечего позря эту память выделять. То есть если эффект после
> 4 байт пропадает, то нет смысла выравнивать на 8 байтовцю
> границу, не смотря на "lond double". Глубоко убежден, что

Почему то msvc выравнивает. Может какое то преимущество и есть - не знаю

> мало какие компиляторы доберуться до 8 байтового
> выравнивания. И что самое удивительное есть такие, которые
> плюют на слово "long" в сочетании "long double".

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

sizeof(float) <= sizeof(double) <= sizeof(long double)

Никто не ЗАСТАВЛЯЕТ делать long double 10-байтовым на всех архитектурах.

> Код в циклах многие компиляторы тоже выравнивают на 4-х
> байтовую границу.
> При выравнивании на 8 байтовую может возникнуть только
> потеря быстродействия из-за трансфера ненужной информации,

Не совсем понял. Имеется в виду выравнивание по началу кеш-строки? (16 байт)

> когда структура передается в качестве аргумента.

Передавать большие данные по значению - вообще достаточно неразумно. В частности именно поэтому в C НЕВОЗМОЖНО передать массив по значению. Даже если явно указано

void f(int arr[10]);

вызов

int a[10];
f(a)

будет передан по указателю (проверял по ассемблерным листингам). И это задано в стандарте.

> Что касается примера, то структуру из трех байтовых
> элементов очень даже имеет смысл выравнить до слова -
> ЗНАЧИТЕЛЬНО быстрее будет обрабатываться ее возврат из
> функции.

А принимаемые/возвращаемые значения всегда выравниваются по 4 байта (по крайней мере на msvc). Например в функции

char f(char c1, char c2, char c3);

Аргументы в стековом фрейме будут занимать не 3 байта (и даже не 4), а 12. И возвращаться будет через eax (опять таки для msvc/x86)
Я здесь про структуру из трех байт, которую имеет смысл... 21.11.05 18:14  
Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
<"чистая" ссылка> <обсуждение закрыто>
> > Что касается примера, то структуру из трех байтовых
> > элементов очень даже имеет смысл выравнить до слова -
> > ЗНАЧИТЕЛЬНО быстрее будет обрабатываться ее возврат из
> > функции.
>
> А принимаемые/возвращаемые значения всегда выравниваются по
> 4 байта (по крайней мере на msvc). Например в функции
>
> char f(char c1, char c2, char c3);
>
> Аргументы в стековом фрейме будут занимать не 3 байта (и
> даже не 4), а 12. И возвращаться будет через eax (опять
> таки для msvc/x86)

Я здесь про структуру из трех байт, которую имеет смысл выравнить до размера 4 байта, поскольку возвращаться она будет через АХ, а невыравненую структуру тяжелее будет сохранить.

И вообще, какая разница как распределяется память между элементами структуры. Если очень надо, то все это можно узнать динамически через разность адресов элементов.
Дык sizeof учтет все "дырки". Потому как sizeof делается на... 18.11.05 11:59  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка> <обсуждение закрыто>
> > Кстати, a inst = {1,1} тоже вполне работает . Так вот,
> не
> > могу найти в стандарте С++ это дело. Где про это
> > растолковано? Это и есть мой вопрос.
>
> В Ричи и Кернигане точно есть. У Страустрапа тоже должно
> быть, порыться надо, но смысл?...
> Подводные камни не в этом!
> Явный акцент делается на массив структур. Упоминается, что
> компилятор для оптимизации работ строк кэша может размещать
> элементы массива структур с адреса кратного степени двойки,
> а размер структуры может быть больше,
> чем сумма размера ее элементов.

Дык sizeof учтет все "дырки". Потому как sizeof делается на всю структуру, а не суммируются sizeof-ы элементов. В ходе обсуждения мы когда то выяснили, что самых страшных подводных камней два: 1) Можно ничтоже сумняшися постирать уже проинициализированные данные базового класса (если такой финт выполняется в конструкторе дочернего класса) 2) Можно постирать неявные указатели (vtable, vbase), которые behind the scenes добавляет компилятор, если класс является полиморфным (содержит виртуальные функции).
Согласен с тобой, только... 19.11.05 08:43  
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка> <обсуждение закрыто>
Согласен с тобой, только...
> Дык sizeof учтет все "дырки". Потому как sizeof делается на
> всю структуру, а не суммируются sizeof-ы элементов.
согласен 100 %.

Только есть единственная неприятность может быть с полным покрытием и последующим memset, а именно, sizeof() никогда не возращает 0 для структур и классов, даже для пустых (нет членов):

struct a {
};
sizeof(a) !=0; // равно всегда 1 для пустой сруктуры, если только не от чего не наследуем.

тогда
memset( & inst, 0, sizeof(a))
... скопирует один байт... но куда? Есть ли гарантия, что этот байт уже распределён под структуру?


> В ходе обсуждения мы когда то выяснили, что самых страшных
> подводных камней два: 1) Можно ничтоже сумняшися постирать
> уже проинициализированные данные базового класса (если
> такой финт выполняется в конструкторе дочернего класса) 2)
> Можно постирать неявные указатели (vtable, vbase), которые
> behind the scenes добавляет компилятор, если класс является
> полиморфным (содержит виртуальные функции).

В принципе, то что он говорит (хотя и не совсем про те вещи, что мы обсуждали) правильно.
Наибольшие косяки возникают именно для массивов структур. Хотя и там, дело не в sizeof... Как раз sizeof - то сработает верно.
Да, кстати, хорошо что напомнил про пустые структуры 21.11.05 11:04  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка> <обсуждение закрыто>
> Только есть единственная неприятность может быть с полным
> покрытием и последующим memset, а именно, sizeof() никогда
> не возращает 0 для структур и классов, даже для пустых (нет
> членов):
>
> struct a {
> };
> sizeof(a) !=0; // равно всегда 1 для пустой сруктуры, если
> только не от чего не наследуем.

Мы уже когда то говорили об этом. Я тоже замечал, что под объекты в MSVC++ (для MSVC насколько я помню это не верно) ВСЕГДА выделяется память. Если объект пустой, то ему выделяется один байт. На досуге надо будет поковырять стандарт на предмет его отношения к данному вопросу.

> тогда
> memset( & inst, 0, sizeof(a))
> ... скопирует один байт... но куда? Есть ли гарантия, что
> этот байт уже распределён под структуру?

Да. В MSVC++ этот байт распределен под структуру. Когда то столкнулся с подобным - использовал класс просто как контейнер для функций членов, но не задал ни одного члена-данных - некоторое время ковырялся в асмовом листинге, пытаясь выяснить глюк ли это. В конце концов понял, что там действительно резервируется один байт под объект. Ну не любит msvc++ пустых объектов.

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

Да вроде нет. Надо конечно стандарт глянуть, но смещение элемента в массиве определяется как (index * sizeof(type)), то бишь элементы в МАССИВЕ не выравниваются (хотя последний элемент в структуре может быть выровнян)

> Хотя и там, дело не в sizeof... Как раз sizeof - то
> сработает верно.
По логике вещей с пустой структурой делать нечего. Она вообще не должна использоваться. 21.11.05 14:52  
Автор: kstati <Евгений Борисов> Статус: Elderman
<"чистая" ссылка> <обсуждение закрыто>
Доброго здоровья!
Относительно пустых структур... разные компиляторы ведут себя совершенно по-разному. Основная проблема пустых структур заключается в, как это не странно указателях на них. На что будет нацелен указатель, если вообще нет данных? NULL ?
malloc(sizeof(type)); при размере типа равным нулю вернет значение NULL. Это создает неоднозначность: можно ли использовать результат нуль, или он появился вследствие невозможности выделения памяти.

Например, MSVC спокойно компилирует следующий код:
#include <stdio.h>
struct	empty {};
int main(int argc, char* argv[])
{
	empty a;
	empty c[10];
	empty b;

	printf("sizeof & pointers\nvar\tsizeof\tpointer\n");
	printf("a\t%d\t%x\n", sizeof(a), &a);
	printf("c\t%d\t%x\n", sizeof(c), &c);
	printf("b\t%d\t%x\n\n", sizeof(b), &b);
	
	printf("alignment\nexpression\tresult\n");
	printf("&a - &b\t\t0x%x\n", (&a - &b) );
	printf("&a - c\t\t0x%x\n", (&a - c) );
	printf("c - &b\t\t0x%x\n", (c - &b) );

	printf("&c[9] - c\t0x%x\n", (&c[9] - c) );
	printf("&c[9] - &c[0]\t0x%x\n", (&c[9] - &c[0]) );
	return 0;
}

---
Результатом является:
sizeof & pointers
var     sizeof  pointer
a       1       12ff7c
c       10      12ff70
b       1       12ff6c

alignment
expression      result
&a - &b         0x10
&a - c          0xc
c - &b          0x4
&c[9] - c       0x9
&c[9] - &c[0]   0x9

---

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

Пример - компиляция маленького файла:
struct empty {};
int main(int argc, char* argv[])
 {
    empty a;
    return &a;
 }

---
Она завершается с ошибкой
gcc.c: In function `main':
gcc_c:5: `empty' undeclared (first use in this function)

Может быть, кому-либо не понравится такое решение, но оно явно лучше, чем в MSVC - программа не захламляется ненужными конструкциями. По логике вещей с пустой структурой делать нечего. Она вообще не должна использоваться.
Это синтаксическая ошибка 21.11.05 16:43  
Автор: amirul <Serge> Статус: The Elderman
<"чистая" ссылка> <обсуждение закрыто>
> Доброго здоровья!
> Относительно пустых структур... разные компиляторы ведут
> себя совершенно по-разному. Основная проблема пустых
> структур заключается в, как это не странно указателях на
> них. На что будет нацелен указатель, если вообще нет
> данных? NULL ?

А на что нацелен (void *)?

> malloc(sizeof(type)); при размере типа
> равным нулю вернет значение NULL. Это создает
> неоднозначность: можно ли использовать результат нуль, или
> он появился вследствие невозможности выделения памяти.

А по фигу.

> Например, MSVC спокойно компилирует
> следующий код:

[skipped]
gcc его тоже преотлично компилирует
С примерно тем же результатом (указатели другие, но они есть):

sizeof & pointers
var	sizeof	pointer
a	1	22ff6f
c	10	22ff50
b	1	22ff4f

alignment
expression	result
&a - &b		0x20
&a - c		0x1f
c - &b		0x1
&c[9] - c	0x9
&c[9] - &c[0]	0x9

---

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

> Пример - компиляция маленького файла:
>
> struct empty {};
> int main(int argc, char* argv[])
>  {
>     empty a;
>     return &a;
>  }
> 

---
> Она завершается с ошибкой
> gcc.c: In function `main':
> gcc_c:5: `empty' undeclared (first use in this function)

Это синтаксическая ошибка. Некоторые расширения C позволяют опускать ключевое слово struct (union, enum), но стандартно этого делать нельзя.

код
struct empty {};
int main(int argc, char* argv[])
 {
    struct empty a;
    return &a;
 }

---

Прекрасно компилируется на gcc:
test.c: In function `main':
test.c:5: warning: return makes integer from pointer without a cast
test.c:5: warning: function returns address of local variable

> Может быть, кому-либо не понравится такое решение, но оно
> явно лучше, чем в MSVC - программа не захламляется
> ненужными конструкциями. По логике вещей с пустой
> структурой делать нечего. Она вообще не должна
> использоваться.

И еще одно. Скорее всего где то в стандарте это оговорено. Ибо

$ cat >test.c
#include <stdio.h>

struct empty {};

int
main() {
	printf("%d\n", sizeof(struct empty));
}

$ gcc -x c -o test.exe test.c; test.exe
0

$ gcc -x c++ -o test.exe test.c; test.exe
1

$ cl -nologo -Tc test.c; test.exe
test.c
test.c(3) : error C2059: syntax error : '}'
test.c(7) : error C2027: use of undefined type 'empty'
        test.c(3) : see declaration of 'empty'

$ cl -nologo -Tp test.c; test.exe
test.c
1

---

Я так и не заставил vc скомпилять этот текст в C-шном режиме (а в cpp он его съел с удовольствием)
Это - стандарт ; 22.11.05 00:08  
Автор: kstati <Евгений Борисов> Статус: Elderman
Отредактировано 22.11.05 00:09  Количество правок: 1
<"чистая" ссылка> <обсуждение закрыто>
> А на что нацелен (void *)?
Сам по себе -- ни на что - это тип. А объект типа (void *) нацелен на область нетипизированных данных. Другими словами - на некую ячейку памяти, не контролируя длинны данных. Это немного другая песня ).
Например, sizeof(void * ) == sizeof(char * ) == sizeof(int * ) == sizeof(any_type * ) -- это ведь указатель. И, по сути, всё равно, куда он указывает. Он имеет фиксированный размер.
В то же время, пустая структура по логике вещей должна иметь нулевой размер, но, к сожалению эта логика нарушается. Хотя... с другой стороны, можно написать пустую функцию void func(void){}.
Бестолковое, конечно, это занятие, но работает (

> > malloc(sizeof(type)); при размере типа
> > равным нулю вернет значение NULL. Это создает
> > неоднозначность: можно ли использовать результат нуль, или
> > он появился вследствие невозможности выделения памяти.
> А по фигу.
В принципе, верно, но есть более точный вопрос. не можно ли, а нафиг?

Порылся-таки в стандарте. Каюсь. Надо было сразу глянуть, а не гадать. (ISO/IEC 14882)

9. Classes
[...]
A class-name is inserted into the scope in which it is declared immedaitely afret the class-name is seen. The class-nama is also inserted into the scope of the class itself. For purposes of access checking, the inserted class name is treated as if it were a public member name. A class-specifer is commonly referred to as a class defenition. A class is considered defined after the closing brace of its class-specifier has been seen even though its member functions are in general not yet defined.

Complete objects and member subobjects of class type shall have nonzero size. [Note: class objects can be assigned, passed as arguments to functions, and returned by functions (except objects of classes for witch copying has been restricted; see 12.8). Other plausible operators, such as equality comparsion can be defined by user; see 13.5)
[...]
Кратко - по-русски. В стандарте c++ заявлено, что классы могут быть пустыми, а их объекты всегда должны быть ненулевого размера. (напомню, что в c++ ключевыми словами объявления класса являюстся class, structure, union).

Так что, это является задокументированным стандартом для C++. Относительно си - не знаю.
Это синтаксическая ошибка :-) 22.11.05 12:07  
Автор: amirul <Serge> Статус: The Elderman
Отредактировано 22.11.05 12:08  Количество правок: 1
<"чистая" ссылка> <обсуждение закрыто>
> > А на что нацелен (void *)?
> Сам по себе -- ни на что - это тип. А объект типа (void *)
> нацелен на область нетипизированных данных. Другими словами
[skipped]
> сути, всё равно, куда он указывает. Он имеет фиксированный
> размер.

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

class base {};
class derived: public base {int a;}

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

> Порылся-таки в стандарте. Каюсь. Надо было сразу глянуть, а
> не гадать. (ISO/IEC 14882)

Не в том стандарте рылся. :-) У тебя синтаксическая ошибка только в рамках СТАНДАРТНОГО синтаксиса C (стандарт ISO/IEC 9899:1999). Некоторые расширения понимают имя структуры без ключевого слова struct, но это не соответствует стандарту. Приведу отрывки из грамматики:

A.2.2 Declarations

(6.7) declaration:
declaration-specifiers init-declarator-listopt ;

(6.7) declaration-specifiers:
storage-class-specifier declaration-specifiersopt
type-specifier declaration-specifiersopt
type-qualifier declaration-specifiersopt
function-specifier declaration-specifiersopt

[skipped]

(6.7.2) type-specifier:
void
char
short
int
long
float
double
signed
unsigned
_Bool
_Complex
_Imaginary
struct-or-union-specifier
enum-specifier
typedef-name

[skipped]

(6.7.2.1) struct-or-union-specifier:
struct-or-union identifieropt { struct-declaration-list }
struct-or-union identifier

(6.7.2.1) struct-or-union:
struct
union

(6.7.2.2) enum-specifier:
enum identifieropt { enumerator-list }
enum identifieropt { enumerator-list , }
enum identifier

---

Немого побилась разметка, так что не видно где терминалы, а где нет, но думаю это и так понятно из контекста. Если человеческим языком, то после объявления struct, union и enum ОБЯЗАТЕЛЬНЫ при использовании объявленных типов. То бишь после объявления

struct my_struct {
int a;
};

Обращаться к новосозданному типу можно ТОЛЬКО через struct my_struct. Все потому, что стандарт C не вводит имя структуры в глобальное пространство имен. Можешь попробовать сделать так:

#include <stdio.h>

struct test {
	int	a;
};

struct test test; // Объявление переменной test с типом struct test

int
main() {
	return 0;
}

---

Любой компилятор C, который не скомпилирует этот код нарушает стандарт.

> Кратко - по-русски. В стандарте c++ заявлено, что классы
> могут быть пустыми, а их объекты всегда должны быть
> ненулевого размера. (напомню, что в c++ ключевыми словами
> объявления класса являюстся class, structure, union).

> Так что, это является задокументированным стандартом для
> C++. Относительно си - не знаю.

Это то все понятно. Я тоже нашел это в стандарте, только в разделе 1.8 C++ Object Model [intro.object]

5 Unless it is a bit-field (9.6), a most derived object shall have a non-zero size and shall occupy one or more bytes of storage. Base class sub-objects may have zero size. An object of POD4) type (3.9) shall occupy contiguous bytes of storage.
) я просто уточнил " sizeof(struct empty) ". По поводу ключевого слова struct - абсолютно согласен. 22.11.05 13:17  
Автор: kstati <Евгений Борисов> Статус: Elderman
<"чистая" ссылка> <обсуждение закрыто>
1






Rambler's Top100
Рейтинг@Mail.ru


  Copyright © 2001-2021 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach