Такие финты ничем не лучше goto и я не уверен, что лучше читаются при сопровождении. А если целью циклов было найти эту пару i,j - тогда что? Заводить ещё две переменные и возле error = true запоминать сначала туда найденные знаения?
А если после того if ещё код нужен?
Да, можно так:
for (i=0; i < n; i++) {
for (j=0; j < m; j++) {
statements1;
if (M[i][j] < 0) {
error = true;
i = n, j = m;
} else {
statements2;
}
}
}
но так легко убежать за правый край экрана (или отступы делать по 2 пробела, что тоже, на мой взгляд, нехорошо).
Конечно, можно и так:
for (i=0, done=0; !error && i < n; i++) {
for (j=0; !error && j < m; j++) {
statements1;
if (M[i][j] < 0) {
error = true;
} else {
statements2;
}
}
}
Но это лишние проверки и я вижу у этого варианта одно-единственное преимущество по сравнению с:
for (i=0, done=0; !error && i < n; i++) {
for (j=0; !error && j < m; j++) {
statements1;
if (M[i][j] < 0) goto Done;
statements2;
}
}
Done: ;
это следование догме "goto - это всегда и безусловно категорически плохо"
Конечно, можно и без goto обойтись и не выстраивать лишние лесенки
bool func()
{
for (i=0, done=0; !error && i < n; i++) {
for (j=0; !error && j < m; j++) {
statements1;
if (M[i][j] < 0) return true;
statements2;
}
}
return false;
}
Но это
1) не всегда удобно - если на вход функции не надо много разного подавать или она не должна много разного возвращать, то я так и делаю (тем более, что функцию можно объявить static inline), но часто отдельная функция "не лепится" (или надо иметь возможность локальных функций, коей в стандартном С нет).
2) те же, кто говорит "goto НИКОГДА нельзя применять" - говорят и "у функции должна быть ОДНА точка выхода" - т.е. один "смертный грех" заменяется на другой :-)