Such as? White space changes can be ignored, comments can be stripped pre diff, both version can be run through the same formatter before hand. What you're then left with are the actual changes.
Anything removed or modified is a breaking change. Anything added to a public struct is a breaking change, which can and arguably should be hidden behind an interface. Adding functions is about all I can think of that's left.
Even if it's not perfect, it's a simple 90% solution with 40 year old tools.
Good points, the second one in particular I don't think could be fixed without a full parser. Function order changes could possibly be worked around by a formatter/linter that can reorder functions, at the risk of creating more issues. The last could be handled be passing the code through the preprocessor (the -E flag in gcc) first.
By this point it's probably gone beyond the "perfect is the enemy of good" threshold though.