Възможно ли е да оставите полета унитиализирани с помощта на определени интиализатори?

Помислете за следната структура и функция, за да създадете структурата:

#define MAX_ELEMS 1000

struct stuff {
  double magic;
  bool is_valid[MAX_ELEMS];
  double values[MAX_ELEMS];
};

struct stuff make_stuff(double magic) {
    return (struct stuff){
        .magic = magic
    };
}

В моя случай имам нужда stuff.magic да се инициализира към дадената стойност и масивът stuff.is_valid да бъде инициализиран с нула, но не искам да инициализирам stuff.values (тъй като те се пазят от stuff.is_valid и се инициализират по-късно при поискване).

Мога ли да постигна това с определени инициализатори?

Знам, че мога да го постигна без, но това е по-грозно и податливо на грешки (наред с други причини, тъй като сега трябва изрично да нулирам stuff.is_valid, може би с memset).


person BeeOnRope    schedule 27.07.2020    source източник


Отговори (2)


Всяко поле, което не е специално инициализирано, ще бъде попълнено с нули.

Така че, не, ако инициализирате някакви полета, всички те получават нещо (дори ако са само нули)

person abelenky    schedule 27.07.2020

Може да се направи само чрез нормални присвоявания и memset.

struct stuff make_stuff(double magic) 
{  
    struct stuff s;
    memset(s.is_valid, 0, sizeof(s.is_valid));
    s.magic = magic;
    return s;
} 

Но ако търсите ефективността, връщането на такава голяма структура няма смисъл, тъй като ще ме копира два пъти изцяло, тъй като структурите се предават от стойността

По-добре е да прехвърлите показалеца към структурата.

struct stuff *make_stuff(struct stuff *s, double magic) 
{  
    memset(s -> is_valid, 0, sizeof(s -> is_valid));
    s -> magic = magic;
    return s;
} 
person 0___________    schedule 27.07.2020
comment
Връщането по стойност е добре, стига make_stuff да може да се вгражда. Тогава това е добър начин да напишете обвивка, която можете да използвате за struct stuff foo = make_stuff(1.0);. - person Peter Cordes; 28.07.2020
comment
@PeterCordes - вграждането не винаги е необходимо, поне с разумен ABI. Извикващият ще предаде указател към областта за върната стойност и извикващият ще попълни директно върнатата стойност и не е необходимо да се прави копие в извикващия. Тази оптимизация се случва дори при -O0 за clang и gcc. Вярно е, че не работи във всички сценарии, както илюстрира caller2 (все пак вярвам, че това работи в C++, което има допълнителен език, който го прави възможно). - person BeeOnRope; 28.07.2020
comment
@BeeOnRope: Хм, вграждането също не винаги е достатъчно. godbolt.org/z/5h1cE4 показва, че с аргумент от 1.0 вместо 0., gcc пропуска оптимизация и все още прави 9008 байта memcpy, докато clang не го прави. Така че приемането на изходен аргумент изрично, вместо имплицитно чрез конвенцията за извикване, очевидно е по-безопасно. - person Peter Cordes; 28.07.2020
comment
@PeterCordes - това определено е странно. Във всеки случай моите употреби са във формата struct stuff s = make(...), т.е. присвояване на новодефиниран обект, което изглежда по-малко проблематично. - person BeeOnRope; 28.07.2020
comment
@PeterCordes тривиалността на примера прави вграждането възможно. - person 0___________; 28.07.2020
comment
@P__J__: Да, освен ако не го поставите в друг файл и не успеете да използвате оптимизацията на времето за връзка. Може да съм ги обявил inline, за да стане по-очевидно, трябва да ги поставите в заглавките, освен ако LTO не се използва много широко в наши дни. (Трябва да бъде, но вероятно не е толкова широко разпространено, колкото се надяваме, като се има предвид, че опциите за активирането му са по-малко преносими от -O2.) - person Peter Cordes; 28.07.2020
comment
@PeterCordes inline няма голямо значение в наши дни. За да принудите вграждането, трябва да използвате разширения на компилатора като gcc __attribute__(()). Това е нова тенденция: In LTO we trust но бих бил по-скептичен. Бих искал да видя разглобяване на по-сложния код със структури, върнати по стойност, свързана с помощта на LTO. - person 0___________; 28.07.2020
comment
@P__J__: inline в декларацията в SO отговор служи като намек за читателите да поставят това в .h и/или че трябва по някакъв начин да уредите това да бъде вградено. Съгласен съм, че всъщност няма значение дали използвате LTO, но някои проекти се компилират без (поне понякога). - person Peter Cordes; 28.07.2020
comment
@PeterCordes: Ако LTO се очакваше, когато C беше стандартизиран, той щеше да включва начини, чрез които програмите биха могли да уточнят семантиката, която се подразбираше от извиквания през границите на компилационни единици. Дори само 1% от извикванията на функции да се нуждаят от такава семантика, оптимизирането на тази семантика от функциите, които ги изискват, е рецепта за Heisenbugs. - person supercat; 03.08.2020