Function to add two binary numbers in C
Do you see any flaws in my code and anything that could be improved?


Segregate code

Splitting into bin_add.c, bin_add.h, main.c would help delineate what is the code, its user interface and test code.

No compilation errors

As posted now, I did not notice any warnings either- good.

Some comments would help

gpp() would benefit with at least a line comment about its goal, expected input, output, etc.  Same for bin_add() - which should alert that the return pointer needs to be free'd.  This becomes even more important when the user only has access to the declaration in a .h file.

Commenting some of the block of code would help too.

When to shift

When there is not a final carry, code shifts Ol[].  As a final carry with this FP-like code is more rare, I'd shift when there is a carry.

Collapsing

With floating point strings, I expect code to drop trailing zero digits to the right of the '.'.  

Leading zero digits are possible based on input.  Perhaps eat those too with an early while (*a == '0') a++; and with b. - depends on coding goals though. 

Inconsistent bracket style

//                                v ?? 
pp[i] = pp[i] ? ll[i] - pp[i] : 0;}


Hopefully code is auto-formatted.

Inconsistent indentation

if((Ol[0] = '0' + xc) == '1') return Ol;
//  v   Why indented here?
    for (size_t i = 0; i &lt;= ML - lO + 1; ++i)
        Ol[i] = Ol[lO + i];


This implies code is not auto formatted.  Save time, and use auto-formatting. 

Terse digit like object names lose clarity

The short object names OO, lO, O, Ol, ll look too much like 00, 10, 0, 01, 11.  Consider more clear alternatives.

Other examples:
int xc as the carry bit looks more clear as int carry.
size_t ML more meaningful as MaxLength.

Input error detection

I'd suggest a separate bool bin_valid(const char *s) and let bin_add() assume  valid strings a,b.  This would help simplify - a NULL return would only indicate out-of-memory.

Good use of cast to ward off warnings

return n  ? (size_t)(n - s + 1) : 0;
//          ^------^


Misc.

ops[2], cc[2] could be local to for (size_t i = ML; i; --i) {

Good use of const.

Good use of size_t.

Personal preference: Consider char *Ol = calloc(ML + 2, sizeof *Ol);



Main

Do not assume EOF is -1

Simply test if the scanf() result is 2.

// while (scanf(" %80[0.1] %80[0.1]", a, b) &amp; 1 &lt;&lt; 1) {
while (scanf(" %80[0.1] %80[0.1]", a, b) == 2) {


ENOMEM

ENOMEM is not part of the standard C.

Test cases

Some specific example test cases would be useful.


  maybe even C90/C89


Not quite.

Lots of error: 'for' loop initial declarations are only allowed in C99 or C11 mode problems

warning: control reaches end of non-void function [-Wreturn-type]

error: redefinition of 'i'