Game of Life generation procession algorithm
Your algorithms looks correct, I could not find any obvious mistakes.

On the other hand, I have a suggestion towards the readability of your code:
Use more functions that tell the reader what you're doing right now. For example, instead of saying 

if (grid[i][j] == 0) {
    if (neighbors == 3) {
        tempGrid[i][j] = 1;
    } else {
        tempGrid[i][j] = 0;
    }
}


you could write:

int cellIsDead(int x, int y) { return grid[i][j] == 0; }
int hasEnoughNeighboursToComeToLife(int x, int y) { return countNeighbors(x, y) == 3 }
void makeCellComeAlive(int x, int y) { tempGrid[i][j] = 1; }


if (cellisDead(i, j) &amp;&amp; hasEnoughNeighboursToComeToLife(i, j)) {
    makeCellComeAlive(i, j);
}


Yes, this takes more time and also probably some global variables (although grid already looks global, so no issue), but it is a) easier to read (see how it almost reads like the rules of the game?) and b) easier to prove to be correct using unit tests. And any decent compiler will inline these function calls at compile time, so no runtime overhead.