N-Queens without recursion (but with one goto) Compared to the recursive version e.g. on Rosetta, this is 5-10% faster and uses 50% less instructions (for N=12).
Only problem is the goto. Can it be avoided without using a function?
The two outer for-loops don't do much in terms of loop control. But this is how I could make it work.
Is there a more convincing non-recursive version? I searched quite a bit and most are recursive and/or longer.
/* N-Queens, non-recursive, with goto */
#include &lt;stdio.h&gt;
//const int N = 8;  //92 solutions
const int N = 12;   //14200 in 0.100 s

/* Print excluding last row, plus last col */
void pr_solution(int *bq, int col) {
    for (int i = 0; i &lt; N - 1; i++)
        printf(&quot;%d:&quot;, bq[i]);
    printf(&quot;%d\n&quot;, col);
}   
void init_board(int bq[], int v) {
    for (int i = 0; i &lt; N; i++)
        bq[i] = v;
}
int main() {
    int bq[N];
    init_board(bq, -1); 
    int row, col, rd, old;
    int cnt = 0;
    for (row = 0; row &gt;= 0;) {
        
        for (col = bq[row];;) {
            NEXT_col:
            if (++col == N) 
                bq[row--] = -1;
            else {
                for (rd = 1; rd &lt;= row; rd++)
                    if ((old = bq[row-rd]) == col || old == col+rd || old == col-rd)
                        goto NEXT_col;
                if (row == N - 1) {
                    cnt++;
                    row--;
                    //pr_solution(bq, col); 
                }
                else  
                    bq[row++] = col;
            }
            break;
        }
    }
    printf(&quot;%d SOLUTIONS (%dx%d)\n&quot;, cnt, N, N);
    return 0;
}
Only problem is the goto. Can it be avoided without using a function?

Candidate goto replacement
    for (col = bq[row];;) {
        //NEXT_col: // Delete
        if (++col == N) 
            bq[row--] = -1;
        else {
            for (rd = 1; rd &lt;= row; rd++)
                if ((old = bq[row-rd]) == col || old == col+rd || old == col-rd)
                    // goto NEXT_col;  // Delete
                    break; //ADD
            if (rd &lt;= row) continue; // ADD
            if (row == N - 1) {
                cnt++;
                row--;
                //pr_solution(bq, col); 
            }
            else  
                bq[row++] = col;
        }
        break;
    }

if (rd &lt;= row) continue; takes advantage that for (col = bq[row];;) lacks a test and increment.

Is there a more convincing non-recursive version?

I'd re-write the loop (perhaps a state machine)  - need some time to think about it.

Later:
Code as written is quite tight.  A very nice simple non-recursive solution.
Yet (at the risk of the ire of OP) lacks clarity making it unnecessarily  difficult to optimize.
The style uses terse variable names like rd and bq which do not well convey their meaning.  It is as if short variable names make faster code (it does not).  init_board() does not initialize a board, but a single dimension of data.  Use more meaningful names.
Use of incomplete for loops like for (col = bq[row];;) seems like a code golf goal than a clearer col = bq[row]; while (1).
Helper functions like pr_solution() rely on the global N, which instead could be passed in, facilitating clearer function flow and allowing use with more than one size.
Local variable instantiation would help see their limited impact.
For clarity, use {} even for one-line blocks.
Consider rectangles, not only squares.  It adds clarity to code and offers other investigations for eventual optimization.
Some other ideas in code below too.
A first round cut:
int main(void) {
  for (int N = 1; N &lt;= 8; N++) {
    const int ROW_N = N;
    const int COL_N = ROW_N;
    int queen_column[ROW_N];
    init_queen_column(ROW_N, queen_column, -1);
    long long count = 0;
    int parity[2] = { 0,0};
    int row = 0;
    while (row &gt;= 0) {
      int col = queen_column[row];
      while (1) {
        if (++col == COL_N) {
          queen_column[row--] = -1;
        } else {
          int row_offset;
          for (row_offset = 1; row_offset &lt;= row; row_offset++) {
            int old = queen_column[row - row_offset];
            int diff = old - col;
            if ((diff == 0) || diff == row_offset || diff == -row_offset) {
              break;
            }
          }
          if (row_offset &lt;= row) { // From early break above
            continue; // ADD
          }
          if (row == ROW_N - 1) {
            count++;
            // print_solution(ROW_N, queen_column, col); // Print, then change state.
            row--;
          } else {
            parity[(row ^ col)&amp;1]++; // Count parity for later optimization (stub code)
            queen_column[row++] = col;
          }
        }
        break;
      }
    }
    printf(&quot;%lld SOLUTIONS (%dx%d)\n&quot;, count, ROW_N, COL_N);
  }
  return 0;
}
1 SOLUTIONS (1x1)
0 SOLUTIONS (2x2)
0 SOLUTIONS (3x3)
2 SOLUTIONS (4x4)
10 SOLUTIONS (5x5)
4 SOLUTIONS (6x6)
40 SOLUTIONS (7x7)
92 SOLUTIONS (8x8)


Other idea to speed things up

Keep track of the count of queen placement on black (and/or white) squares.  Once that exceeds about half of N, time to unwind as subsequent forward iterations will all fail.

Look for patterns.  Example: Symmetry.  A solution that starts with the first queen on column 0 will have as many solutions as column N-1.  Looks like run time can be halved in some fashion.