Our CloneDR tool can do this for large code bases with many files (or just one file if you insist). It finds:
- exact clones. Using the language structure (ASTs) to guide the matching process, ensuring that clones detected are sensible blocks of code. This also means that it will find clones in any language structure (whole functions, statement sequences, big expressions, ...) in spite of changed code layout (indentation, whitespace, presence/absence of comments, constants of same value but different radix, ...). If there is a set of several (or many!) blocks of code that are clones, the set gets reported, instead of pairs. Exact clones are reported by exact source location.
- near miss clones, including expressions substituted for variable names and vice-versa, as OP gives as examples, and in many cases places where statements have been inserted or deleted. (This often detects broken clones, where a fix was not propagated, as discussed by Steve Barnes in his answer). Near-miss clones are reported by location, with parameters showing how they differ, and how the parameters can be bound to produce the exact clones in the source code. This is almost a subroutine declaration for the commonality.
CloneDR typically finds 10+% duplicated code in software that is relatively well engineered. These numbers can be significantly larger in sloppy source code (we have a real 100K SLOC COBOL example that is 55%+ duplicated code, yikes).
CloneDR uses a language-precise parser to discover the code structure. There are versions for many languages, including various dialects of C (through C11) and C++ (through C++14 with C++17 coming along) and especially including MS-specific dialects. [We try to keep our langauge front ends up to speed. CloneDR works for C# 6, including the LINQ commands mentioned by another answer].
Setting CloneDR up to run is fairly easy: you construct a project file listing the source files you would like analyzed, plus a few parameters that define what "near miss" means (x+y is a clone of a+1 with a variable substution, but you really don't want to see those).
Because of complications with parsing source code with preprocessor directives without expanding them, for C and C++, you may have to configure CloneDR for your specific code base, by telling it about your more egregious macros or conditionals that make standalone parsing difficult. This can be done for million line systems with pretty modest effort. C usually requires a bit of effort here. C++ often does not; C++ programmers don't use the preprocessor much. Other languages supported by CloneDR without preprocessors don't need this special step.
There are example reports of detected clones at the site for a variety of languages. The download demo will determine all clones (even in a big system) and provides an analysis of how clony the analyzed software is; it will also show some of the clones.
Since this is my tool, don't take this as a recommendation; just documenting its existence. It is based on my long experience writing code, and there's a technical paper describing essentially how it works: Clone Detection Using Abstract Syntax Trees.