Minimal stack from unsigned int to track boolean values wtih bits
Reading this:


enum { LEFT, RIGHT, STKMAX = 4 };



I expect to see use LEFT and STKMAX interchangeably however they're different things you packed together for convenience. Split it and readers won't need to browse all your code to understand that they can't use STKMAX where FALSE or TRUE are expected. Is stack_push (&amp;s, STKMAX); valid and meaningful?



You already know you may use uint32_t instead of unsigned int but, at least, you should really use unsigned, the extra int in the declaration adds nothing to your code (but that's just an opinion then take it or leave it).




unsigned int kp[STKMAX], idx;



I prefer, for clarity, to have one single variable declaration for each line. I'd also change names to something more meaningful. For example, why idx can't be indexOfNextAvailableBit? We don't need to obfuscate our code and I do not neet to read stack_push() to understand how it's used to determine what it is.



stack_clear() can be simplified to use memset(), any decent compiler will (probably) recognize it and use the best algorithm to perform the task (loop, unrolled loop, plain memset(), something more esoteric/fancy).

memset(s-&gt;kp, 0, sizeof(unsigned)*STKMAX);


As noted by chux you better use memset(s-&gt;kp, 0, sizeof s-&gt;kp[0] *STKMAX) "as it reduces a dependency on matching a variable with a type - which may evolve". 



In stack_push() you should again declare one variable each time, no need to save few keystrokes and it makes declaration easier to read. Also here you should use more meaningful names, ideally I would like to read the variable name and go to the next line, there should be no need to read and understand the expression to determine what it is going on. I'm stressing on this but it's a very important concept when after 1 year you go back to  your 500k/1M LOC codebase to fix a bug.



It's not a big compile-time protection but you have an enum, give it a name and use it for the parameter v (which may be value). Callers are still able to push 41 instead of TRUE and FALSE but you're documenting your function interface (and see next paragraph).



You may want to validate inputs.



Do not print error messages to stdout. For two reasons: first because stdout is for program output and errors should go to stderr, second because it's not your code responsibility to inform the user, you're just handling a stack of booleans and an appropriate return code is enough.




/* set bit at index */



If you feel you need a comment for this then you're missing the opportunity to introduce a separate self-explicative setBitAtIndex() method.



Do you really need to return the index of the next available bit? It's never a parameter for your functions then caller is probably not interested in it (and it's available in selstack). You may want to use return value as error code (eventually, from C11, consider to use errno_t).



Above applies to stack_pop() and stack_last() as well.