TL;DR Matrix don’t need to be  they can be seen as a simple view on a  array when you have a rectangular array with simple math. And, you can use sparse matrix in other cases too. Which handles 80% of the common cases. A matrix, is an abstraction. It seldom needs to be a .
This is not C, but it the idea, here I implement matrix as either sparse matrix (sets of coordinate, dicts) or using x = offset modulo col_size and y = int(offset)/ col_size on any 1D arrays. Hence since Game Of Life is arrays of state in (Alive (0), Dead (1)) I use an integer as a matrix. I can keep a matrix of 1024 x 1024 states stored in a memory efficient way without the overhead of the boxing model in python.
I keep a 2D API for keeping the code easier to read (for me).
Boundary conditions are thus in pretty simple forms:
offset == (offset % line_size in (line_size -1, 0) ) or (col_size >offset >= col_size -1 * line_size) (testing my cider and being dyslexic my code may not be exact) which makes boundary conditions explorable within a single indexed loop (that CPU very easily can heavy lift (SSE?) and a simple “?:”. And I avoid the burden of double memory allocs (even worse in C).
I also can also copy with single loops (memset in C) 2D arrays.
Does it answer your question?
I thought it was simple, but discovered to my dismay that many CS teachers consider this an heresy in my beautiful elite country and prefer the  approach systematically (even for rectangular 2D matrices, framebuffers, pixmaps…) especially in elite school (sarcasm towards my educational system).
Here is a troll^H rant I made on the topic