Есть ли в структуре нулевые байты после символа?

Я пытаюсь убедиться, что понимаю, какие здесь скрытые предположения.

Этот код здесь дает правильные результаты.

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

struct branch
{
    char flag;      //value
    struct branch *l; //left child
    struct branch *r; //right child
};

struct branch c={'c',NULL,NULL};
struct branch e={'e',NULL,NULL};
struct branch f={'f',NULL,NULL};
struct branch b={'b',&c,NULL};
struct branch d={'d',&e,&f};
struct branch a={'a',&b,&d};

void preorder(struct branch* t)
{
    printf(&t->flag);    //Seems ugly and errorprone
    if(t->l) preorder(t->l);
    if(t->r) preorder(t->r);
}

int main()
{
    preorder(&a);
}

Результат, как и ожидалось, abcdef

Может ли кто-нибудь подтвердить мои подозрения, что это работает только потому, что две вещи:

  1. члены структуры выравниваются по n-байтовым границам (n! = 1) (n = 4, кажется, при запросе sizeof () - s)
  2. байты, не используемые первым членом (который является char) до n-байтовой границы, обнуляются.

Я не вижу другого объяснения правильной работы printf в противном случае, так как он ожидает символ char [] с завершающим нулем.

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

Первая неделя периодических возня с C, так что я довольно зеленый.


person Jostikas    schedule 05.10.2014    source источник
comment
printf(&t->flag); действительно уродлив. (Он полагается на то, что символ после поля .flag равен нулю; что не гарантируется) printf("%c", t->flag);, вероятно, будет стандартным способом.   -  person wildplasser    schedule 05.10.2014
comment
Мой первый рефлекс коленного рефлекса заключался в том, что он немного быстрее (я пробежал оба раза, а версия% c закончила в 4 раза дольше). Я попробовал еще раз, и оказалось, что предыдущий результат был ложным. Думаю, в этом нет ничего хорошего.   -  person Jostikas    schedule 05.10.2014
comment
Из любопытства: откуда этот код? Я не вижу причин не использовать printf правильно (как предлагает Wildplasser) или использовать putchar.   -  person mafso    schedule 05.10.2014
comment
Код в этой форме не существует нигде, кроме моего ПК. Это часть домашнего задания, когда, записывая его, я еще не понимал, что я делаю, что такое & и * на самом деле, или в чем разница между адресом и указателем. Итак, я бездельничал, и комбинация Могу ли я передать один символ в printf и безумное предположение привело к этому рабочему коду. Затем, когда я попытался понять, почему это сработало, я понял, что не могу быть уверен, пока не знаю, как действуют отдельные части. Но это не первый мой пост. Мой первый пост был вчера, ответов нет.   -  person Jostikas    schedule 05.10.2014
comment
@mafso - ох, putchar. Мой опыт показывает, что я не знал об этом.   -  person Jostikas    schedule 05.10.2014
comment
Используйте offsetof(), чтобы определить возможное заполнение.   -  person 2501    schedule 07.10.2014


Ответы (2)


Чтобы проверить свои предположения, вы можете проверить код во время выполнения с помощью отладчика или с помощью некоторого printf.

Например, с:

  char *ptr=(char *)&t;
  printf("%02X %02X %02X %02X\n",ptr[0],ptr[1],ptr[2],ptr[3]);

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

  printf(&t->flag);

явно ошибочен, потому что он основан на предположениях, которые не гарантируются стандартом .tt

person Remo.D    schedule 05.10.2014
comment
Я могу проверить только доступные мне архитектуры, и если они распространены, я, скорее всего, найду их верными. Иногда хорошо иметь опытное мнение о таких вещах. Я подумал, что может быть некоторая возможность для скорости, но, похоже, все выходит так же. - person Jostikas; 05.10.2014
comment
@ Remo.D, так что это не является явно неправильным, потому что он полагается на предположения, которые не гарантируются стандартом, но он не переносится и явно неверен, если кто-то хочет писать код, зависящий от стандарта. Если бы он всю жизнь писал только для этой конкретной среды, то, может быть, это было бы совсем не неправильно? - person 4pie0; 05.10.2014
comment
@ 0d0a ну, единственный (едва ли) приемлемый случай, если он будет снова скомпилирован с той же версией компилятора в той же ОС (опять же с той же версией) и т. Д. Если он будет жить в двоичной форме после компиляции и тестирования, я бы не хмуриться :) - person Remo.D; 06.10.2014

Вы можете сами доказать 1 и 2 (я использую x64, поэтому все выровнены по 8 байтам в структурах)

objdump ./main -s -j .data

Contents of section .data:
 601030 00000000 00000000 00000000 00000000  ................
 601040 63000000 00000000 00000000 00000000  c...............
 601050 00000000 00000000 00000000 00000000  ................
 601060 65000000 00000000 00000000 00000000  e...............
 601070 00000000 00000000 00000000 00000000  ................
 601080 66000000 00000000 00000000 00000000  f...............
 601090 00000000 00000000 00000000 00000000  ................
 6010a0 62000000 00000000 40106000 00000000  b.......@.`.....
 6010b0 00000000 00000000 00000000 00000000  ................
 6010c0 64000000 00000000 60106000 00000000  d.......`.`.....
 6010d0 80106000 00000000 00000000 00000000  ..`.............
 6010e0 61000000 00000000 a0106000 00000000  a.........`.....
 6010f0 c0106000 00000000                    ..`.....   
person Alex Hoppus    schedule 05.10.2014
comment
Спасибо! Я не знал об этом! Также извините, я помечаю ответ Ремо Д. как принятый, так как он также ответил на вторую часть. Я бы поддержал, но, похоже, у меня пока недостаточно репутации даже для этого :) - person Jostikas; 05.10.2014
comment
@Jostikas теперь у вас есть - person 4pie0; 05.10.2014