Best Practices for Bulletproofing C/C++ Code

C/C++ programmers face somewhat different challenges than programmers working in other languages and domains. Truly new projects are rare; today’s projects usually involve modifying or extending an existing system, refactoring available code for a new application, or integrating existing modules in new ways. Most commonly, a team inherits a pile of C or C++ code from a different group, an outside vendor/contractor, or the open source community. Such projects inevitably require modifying some amount of unfamiliar code (while getting to understand it in the process) and maintaining it going forward.

This paper will examine several techniques that, when properly and consistently applied and monitored, will reduce the risks of errors when a diverse team of programmers develops code on top of an existing code base, as well as improve the stability and quality of the project’s evolving code base. It explains how to:

  • Use static analysis to automatically identify structurally bad code upfront to reduce the risks of your work being impacted by unknowns in the legacy code.
  • Establish a sufficient regression suite with code coverage metrics before attempting any code changes to reduce the risks of your changes breaking the code.
  • Validate the code changes in detail with unit and functional tests based on specifications and make these tests part of the regression suite to ensure that you’ve done the right thing and you can always verify whether it still works.
  • Perform incremental static analysis and code review on the code changes to ensure your code is structurally and logically clean.

All four techniques can be automated to promote a consistent implementation and allow your team to reap the potential benefits without disrupting your development efforts or adding overhead to your already hectic schedule. This paper focuses on explaining and elaborating on the general four-step strategy outlined above. When appropriate, this paper gives implementation examples to illustrate how Parasoft C++test, an integrated solution for automating a broad range of best practices proven to improve software development team productivity and software quality, facilitates the recommended steps. It focuses on explaining the general four-step strategy, not discussing C++test features or usage in depth. If you would like a general overview of C++test or specific details on how to use it, visit http://www.parasoft.com.

It is important to note that the strategy and practices discussed here are designed for team-wide application. Unless they are applied consistently across an entire development team, they will not significantly improve the software that the team is building. Having a development team inconsistently apply software development standards and best practices as it implements code is like having a team of electricians wire a new building's electrical system with multiple voltages and incompatible outlets. In both cases, the team members' work will interact to form a single system. Consequently, any hazards, problems, or even quirks introduced by one "free spirit" team member who ignores the applicable guidelines and best practices can make the entire system unsafe, unreliable, or difficult to maintain and upgrade.

Use static analysis against an established coding policy to find defects, ambiguities, and other “gotchas” in C/C++ code without executing it

Figuring out a bug, or at least getting some understanding of the code’s properties without executing it, has always had a great deal of significance for programmers. This, in fact, is a cornerstone of code reviews – using a human brain to read and understand the code, looking for defects with a fresh eye. Peer code reviews remain the best approach for finding code defects – on average, 60% of defects can be removed via code reviews, with high numbers reaching into the 90% percentile (Boehm and Basili, “Top Ten Software Defect Reduction List,” Computer, January 2001). This should not be surprising in the least, since code reviews use the finest analysis instrument: the human brain. A quest for code review automation, so that reviewers could concentrate on tasks of higher intelligence, rather than validating conformance to coding conventions, drove the development of automated static analysis tools, which has made tremendous strides – especially in the last few years.

Properly-implemented automated static analysis effectively identifies the code’s soft spots and possible bugs in a relatively short time – something that can’t be achieved manually. For example, a commonly-encountered error is failing to recognize that floating numbers cannot be compared with the == operator, unlike, say, integers or classes with implemented operator==

bool SetClient::contains(double d, SetType & h)   {       for (SetType::const_iterator it = h.begin(); it != h.end(); ++it) {           // Pointer to a double of value d?           if (*((double*)((*it)->getValue())) == d) {               return true;           }       }       return false;   }

This is one of the more common checks supported by static analysis tools. More sophisticated algorithms are required to spot data- or control-dependent errors, such as potentially using null pointers to objects or files, or locally leaking memory...



Continue reading Best Practices for Bulletproofing C/C++ Applications