четверг, 5 июня 2008 г.

Трудноуловимые ошибки...

Нашел сегодня одну ошибку в своем коде. Ну очень интересная на мой взгляд.
const char *exception[] = {
"Division Error",
"Debug",
"NMI Interrupt",
"Breakpoint"
"Overflow",
"BOUND Range Exceeded",
"Invalid/Undefined Opcode",
"Device Not Available (No Math Coprocessor)",
"Double Fault"
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack-Segment Fault",
"General Protection",
"Page Fault",
"Intel Reserved",
"x87 FPU Floating Point Error (Math Fault)",
"Alignment Check",
"Machine Check",
"SIMD Floating Point"
};
Поскольку кусок кода не очень большой, опытный читатель сразу раскусит в чем дело. А я пока продолжу...

Этот массивчик помогает мне наглядно выводить информацию о произошедшем нарушении. По моему чем больше информации на экране - тем меньше надо ее искать для выяснения обстоятельств. Но злоупотреблять в этом плане тоже не стоит - информация должна быть концентрированная. Но речь не о том.

Отлаживаю я значит свои задачи, и тут у меня происходит исключение с дескрипшином "Intel Reserved". Я слишком давно вожусь с IA32, чтобы понимать, что такое исключение не может возникнуть. Тем более что номер у него почему-то 13...

Считать строки было лень, поэтому я сразу переписал код более конкретно:
const char *exception[] = {
[0] = "Division Error",
[1] = "Debug",
[2] = "NMI Interrupt",
[3] = "Breakpoint"
[4] = "Overflow",
...
И тут же получил две ошибки. Ошибки в принципе просты, в двух местах забыл поставить запятые. Только приводит это к тому что в тех местах, где пропущена запятая, строки сливаются.

Не знаю кто придумал сливать строки, помоему это слишком редкая необходимость, можно было бы ее и усложнить немного (сделать сливание явным). А здесь получается, что мало того, что сообщения содержат не то, что надо (съезжает и содержание и индексы), Но и весь массив оказывается короче чем рассчитывалось. И что самое обидное - никаких нареканий компилятора это не вызывает.

Хорошо, что в gcc есть такие замечательные возможности как явная инициализация. Насколько я помню msvc не имеет таких возможностей, поправьте меня если я не прав. Новыми полезными фичами надо пользоваться.

PS: Кто нибудь сталкивался с чем нибудь подобным, или я один забываю ставить запятые?

PPS: Кстати вот тоже неоднозначность, после последнего элемента запятая не обязательна...

7 коммент.:

Олег Комов комментирует...

У меня такое раз было (название gui-элементов заносились в массив), но нашел быстро довольно. Теперь так же массивы со строками инициализирую :)

Unknown комментирует...

Мне почему-то не довелось так инициализировать массив строк ни разу. Про то-что строки идущие подряд сливаются в первый раз услышал..
В общем - explicit is better than implicit :)

Yuri Volkov комментирует...

[0] = "Division Error"
это ж кажись gcc specific? в смысле такой способ инициализации? не встречал ранее подобного )), разве что при работе со структурами, точнее при их инициализации:
struct StTest {
int i;
char c;
double d;
};
....
struct StTest st1={.d=0.1, .i=1, .c='a'},
st2={.c='b',.d=0.2,.i=2};

Андрей Валяев комментирует...

2yuriy volkov: Да данная форма вероятно gcc-specific. Причем надо сказать что поддерживается довольно давно. Я знаю что в linux ядре настойчиво рекоммендуется все инициализировать только так.

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

gcc поддерживает инициализации полей - .a=1, .b=2, так и инициализацию массивов как написал в посте.

В свое время пытался найти подобную фичу для msvc, но не нашел. Правда я с msvc2008 не ковырялся, может там что-то появилось? Судя по MSDN так ничего собственно и не изменилось.

А возможность, надо сказать, очень нужная и полезная. Может быть инициализация массивов нужна реже, но инициализация структур - дело весьма частое, инициализация мемберов по порядку вопервых требует наличия перед глазами списка полей, который может быть вообще в другом файле описан, а во вторых очень чувствительна к изменению этого списка.

Хотя в плане C++ это менее актуально всвязи с применением конструкторов.

Андрей Валяев комментирует...

Забыл написать...

Причем это не стандарт c99 тоже. linux ядро не следует этому стандарту однако данный специфик полноценно юзает.

Самое интересное то, что такие ошибки могут существовать долго, пока не наткнешься случайно. Моя спокойно прожила несколько месяцев, пока не случился #GP. :)

morbo комментирует...

Я довольно часто пользовался слиянием строк. Например так:

fprint(stderr, "Usage:\n"
"program <options>\n>"
);

Андрей Валяев комментирует...

Это можно юзать, но едва захочешь что-то добавить, как логика начинает хромать на обе ноги...

fprint(stderr, "Usage: %s\n"
"%s <options$gt;\n", str1, str2
);

Взгляд выхватывает строку
"%s <options>\n", str1, str2
И программистская красная лампочка сразу начинает мигать. :)

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

Надо экономить свои и чужие умственные усилия.