Goto Statements and Alternatives in C

In the program below it is supposed to find the first whole zero row, if any, of n × n-matrices. Is this the correct approach to the problem and is there another good structured approach to this code in C or in any language in general? I am trying to learn more about goto operations and their respective applications / alternatives. I appreciate any help!

int first_zero_row = -1; /* none */ int i, j; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (A[i][j]) goto next; } first_zero_row = i; break; next: ; } 
+4
source share
7 answers

I am not opposed to using goto in some situations, but I think it can be rewritten as follows:

 for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (A[i][j]) break; } if (j==n) { /* the row was all zeros */ first_zero_row = i; break; } } 
+4
source

How to use a small helper function to simplify the code in a loop:

 bool is_zero_row(int* Row, int size) { int j; for (j = 0; j < size; j++) if (Row[j] != 0) return false; return true; } 

and then

 int first_zero_row = -1; /* none */ int i; for (i = 0; i < n; i++) { if (is_zero_row(A[i], n)) { first_zero_row = i; break; } } 
+4
source

First: Yes, your approach looks right to me. You can rewrite it as follows:

 int first_zero_row = -1; /* none */ int i, j; for (i = 0; i < n; i++) { int all_zeroes = 1; for (j = 0; j < n; j++) { if (A[i][j]) { all_zeroes = 0; break; } } if (all_zeroes) { first_zero_row = i; break; } } 

but I think this is actually less clear than the goto version. In Perl, which provides tag labels, you can do this:

 my $first_zero_row = -1; # none ROW: for my $i (0 .. $n-1) { for my $j (0 .. $n-1) { next ROW if $A[$i][$j]; } $first_zero_row = $i; last ROW; } 
+1
source

Your code looks quite readable to me. The indiscriminate goto witch hunt is not very good. Go and read Donald E. Knuths Structured Programming with a transition to applications for interesting users on this subject.

My version with goto is for maximum efficiency and is quite readable:

 int i, j; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) if (A[i][j]) goto not_this_row; goto row_found; not_this_row: } /* Not found case here */ row_found: /* Found case, i is the first zero row. */ 
+1
source

You can use one variable that will work like flag . For the condition if (A[i][j]) goto next; change the flag value to another and not to goto next .

And then check that the flag value later performs other operations, which after the next label in your case.

 int flag = 0; for ( i = 0; i < n; i++ ) { for ( j = 0; j < n; j++ ) { if ( A[i][j] ){ flag = 1; break; } } if( flag == 1 ){ // do your stuff continue; } if( flag == 0 ){ first_zero_row = i; break; } 

NOTE. To remove goto from your code, you may need to adjust the position of some lines of code.

0
source

Since I used my first goto to break out of nested loops in ... well ... someday, today, I will weigh it. I felt bad, so I read a little why goto is considered evil.

The reason Dykstra (and others) thinks goto is evil is because the code is created in blocks, where each block has preconditions and postconditions. A transition (or a break) means that you cannot guarantee that a block of code will generate a valid postcondition for the next block as a precondition (i.e. you cannot "prove your code correctly" because break / goto may skip important parts code)

HOWEVER, the reason why I think goto is great in your example: you are actually setting up a fully valid postcondition (first_zero_row = -1 ... i.e., "no all zero line found") up. Thus, there is no chance that your block will go into the state of an invalid postcondition.

However, the problem of readability is another thing. Goto makes the code pretty unreadable. Therefore, there may be better alternatives for your specific example.

0
source

In my opinion ALL GOTO IS EVIL. If you need them anyway, you'd better consider the Extract Method refactoring.

The need to use "GOTO" is a very good indicator, showing that your method (or function) is too complex for you to divide it into different parts.

For example, this example question can be (and should be) reorganized into:

 bool is_zero_row(int *row, int size){ for(i = 0; i<size; ++i){ if(row[i]) return false; } return true; } bool find_zero_row_in_array(int **A, int rows, int row_size){ int i,j; for(i = 0; i < rows; ++i) { if(is_zero_row(A[i], row_size)) return true; } return false; } ... if(find_zero_row_in_array(A, n, n)){ //Found } else { //Not found } .... 

It is much more flexible and readable than the original. At least now we have a function that can work for any arrays with different row and column numbers, but suppose they are the same.

-2
source

All Articles