Вопрос по Си
Mar. 4th, 2018 09:25 pmРасскажите, насколько легальна в Си вот такая штука:
Есть некий массив сопоставляющий имя и значение, и есть еще возможность значения по умолчанию, которое имени не имеет.
Чувак предлагает делать так:
#define NAME_DEFAULT ((const char *) -1) /* pseudo-name for default value */
А потом проверять
if(Chto_to->name == NAME_DEFAULT)
Где name -- это char*
Для меня это какое-то страшное сишное конлдунство, я не могу оценить насколько оно корректно и переносимо между компиляторами.
Потому как я бы право слово скорее #define NAME_DEFAULT "" сделал бы. Ибо пустого имени все равно не бывает. И потом при помощи сравнения строк его проверял бы. Но может это у меня просто нету правильной сишной закалки.
no subject
Date: 2018-03-04 07:04 pm (UTC)Если пустого имени не бывает, то я бы для значения использовал NULL.
no subject
Date: 2018-03-04 07:10 pm (UTC)NULL -- уже занят. Там массив сопоставления по сути NULL-terminated.
Ну я понимаю, что указатели сравниваются... и видимо в NAME_DEFAULT фактически попадает 0xFFF..FF Но как-то это все мне интуитивно не нравится...
Я могу допустить что может быть это какая-то общепринятая сишная практика так делать. Но мне о такой пректике ничего неизвестно...
Как инициализируется что? Массив?
no subject
Date: 2018-03-04 07:45 pm (UTC)Но я бы не стал на этом месте экономить. Потому что никто не обещал что этот хитрый трюк который сейчас позволяет сэкономить целое разыменование, завтра, версий этак через 10 компилятора, не превратится в уязвимость.
Помнится в catdoc мне через 20 лет после написания много какие подобные трюки пришлось править.
no subject
Date: 2018-03-04 10:09 pm (UTC)Спасибо!
no subject
Date: 2018-03-04 07:59 pm (UTC)Не, это как-то будет работать, пока не поменяется инициализация. Ей-богу, проще поставить честное значение по умолчанию и им инициализировать..,
no subject
Date: 2018-03-04 10:12 pm (UTC)Мысль про инициализацию я правда так и не понял. Но картина в целом ясна. Это не общепринятый хак, о котором я не знаю, это просто хак, подвергаемой сомнению чистоты...
no subject
Date: 2018-03-04 08:03 pm (UTC)поэтому лучше пустая строка, или не пустая, какая-нть "MY_DEFAULT_NAME"
no subject
Date: 2018-03-04 10:10 pm (UTC)Спасибо :-)
no subject
Date: 2018-03-04 08:35 pm (UTC)Предлагаемое - заведомо плохое колдунство, ибо имеет синтаксическую корректность, но разный семантически результат в разных компиляторах.
Что будет где-то приводить к нормальному сравнению, где-то - к неверному при неких входных данных (что особенно неприятно), где-то и к сегфолту (впрочем, вот щас так не могу придумать точно где, хехе).
no subject
Date: 2018-03-04 08:48 pm (UTC)Если имя в массиве найдено - возвращаем соотв ему значение (char * на строку)
Если имя в массиве не найдено - возвращаем NULL
Если значение в массиве найдено - возвращаем соотв ему имя (char * на строку)
Если значение в массиве не найдено - возвращаем NULL
Когда и где нужен пресловутый "default name"?
no subject
Date: 2018-03-04 08:59 pm (UTC)no subject
Date: 2018-03-04 11:12 pm (UTC)"элементы конфига" - это одна сущность.
"массив" - это другая сущность.
Соблазн в plain C одну сущность строго описать другой, велик, но мне кажется неразумным.
no subject
Date: 2018-03-04 10:03 pm (UTC)Массив пар имя-значение должен задавать поведение этого enum'а. Для задания вот этого самого случая "не установленно" в этом массиве автор предложит использовать сравнение с вот этим вот define'ом.
Но вопрос не в том насколько вообще такая реализация удачна или нет. Она не удачна по многим причинам. Меня вообще правомочность использования такого define'а очень сильно озадачила.
Как я понял из дискуссии озадачила не зря.
no subject
Date: 2018-03-04 11:09 pm (UTC)С моей точки зрения, тем не менее, использовать "особое значение" в подобном сравнении можно, и это будет "менее взрывчато". Но только одним способом.
Не преобразуя целую константу в char *, а используя для ОБОИХ сравниваемых элементов union с целочисленным значением соответствующей длины. То есть что-то типа:
#if sizeof(char *) == 2
#define DEFAULT1 (int)-1
#define DEFAULT2 (int)-2
union { int itemintvalue, char *item }
#elif sizeof(char *) == 4
#define DEFAULT1 (long int)-1
#define DEFAULT2 (long int)-2
union { long int itemintvalue, char *item }
...
#endif
...
if (itemintvalue == NULL) { } /* тут можно и item */
if (itemintvalue == DEFAULT1) { }
if (itemintvalue == DEFAULT2) { }
В этом случае "компиляторозависимые" фишки окажутся строго в "правильном месте", их будет легче править, а в основном потоке операций не будет лишних ворнингов и рантайм ерроров...
Пока не случится так, что строка массива "ляжет" на адрес, который окажется DEFAULT1 или DEFAULT2 ;)
Но в нынешнем мире я бы не стал так делать. :)
no subject
Date: 2018-03-04 09:55 pm (UTC)Отвечая на все незаданные вопросы сразу (включая и пустую строку тоже, она НЕ исключение): помнишь народную мудрость про зарплатную ведомость и сотрудника с фамилией Итого? Если в одном словаре могут оказаться и данные от пользователей (имена ключей) и данные от программиста (MY_DEFAULT_KEY, пустая строка), то помечаться как специальные должны первые, а не вторые. В одном проекте, в котором я принимал участие, к именам ключей от пользователей приклеивалась тильда: "~Иванов", "~Петров", "Итого", но ни в коем случае не "Иванов", "Петров", "~Итого" -- при втором подходе рано или поздно кто-то впишет эту тильду к себе в паспорт. Но такой подход тормозит, да. Если важно, чтобы ещё и не тормозило -- ну, тогда думать надо. :-)
Можно иметь несколько разных ссылок на разные пустые (или даже не пустые) строки для разных целей, но тогда нужно очень внимательно смотреть, когда ты сравниваешь ссылки, а когда значения. Рано или поздно кто-нибудь ошибётся. :-( Но зато быстро.
... И мировой получает 2400, и председатель 2400 ...
no subject
Date: 2018-03-04 10:06 pm (UTC)По поводу первого абзаца -- спасибо :-)
no subject
Date: 2018-03-05 07:28 am (UTC)no subject
Date: 2018-03-05 01:58 pm (UTC)Типичный случай поврежденных данных. ;-)
no subject
Date: 2018-03-07 12:44 am (UTC)