Você já deve ter lido por aí que usar ASSERT e VERIFY nos seus programas garante um aumento da qualidade do software substancialmente.
ASSERT é uma macro que só faz algo de útil quando o código é compilado com um determinado #define (_DEBUG, NDEBUG, etc) e este possui um valor conhecido (ou simplesmente está (in)definido).
Um ponto 'perigoso' com relação ao ASSERT é que nas versões de release, o argumento recebido é completamente ignorado. Um exemplo de ASSERT (adaptado de afx.h):
#ifdef _DEBUG
#define ASSERT(x) ((void) (x) || !::AfxDebugBreak())
#else
#define ASSERT(x) ((void)0)
#endif
Na versão de debug, se avaliar a expressão 'x' resultar num valor considerado falso o programa será interrompido. Porém na versão de release, a expressão 'x' não é sequer avaliada!
Outro fato bastante importante é prestar atenção e usar somente uma expressão por cláusula de ASSERT, para facilitar no diagnóstico do erro. Se você tiver 3 condições num ASSERT e ele falhar, qual das três teve valor falso? Evite tiros no pé gratuitos!
Já a macro VERIFY possui a característica de não ignorar seus argumentos em tempo de execução:
#ifdef _DEBUG
#define VERIFY(x) ASSERT(x)
#else
#define VERIFY(x) ((void)(x))
#endif
Ela mantém seu código instrumentado e ao mesmo tempo permite construções mais consisas, como por exemplo, ao invés de checar duas vezes se o ponteiro é valido antes de usar:
ASSERT(p);
if (p) {
printf("(%d, %d)\n", p->x, p->y);
}
usando VERIFY podemos fazer o teste na mesma linha do if:
if (VERIFY(p)) {
printf("(%d, %d)\n", p->x, p->y);
}
A meu ver tornando o código mais legível e igualmente instrumentado!
Podemos inclusive inverter a lógica (pra quem prefere checar assertivas de entrada):
if (!VERIFY(p)) {
goto error;
}
ASSERT e VERIFY são ferramentas indispensáveis para escrever código com menos bugs que a média :)