To get a trial key
fill out the form below
Team License (standard version)
Enterprise License (extended version)
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Request our prices
New License
License Renewal
--Select currency--
USD
EUR
GBP
RUB
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
I am interested to try it on the platforms:
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Message submitted.

Your message has been sent. We will email you at


If you haven't received our response, please do the following:
check your Spam/Junk folder and click the "Not Spam" button for our message.
This way, you won't miss messages from our team in the future.

>
>
>
Consequences of using the Copy-Paste me…

Consequences of using the Copy-Paste method in C++ programming and how to deal with it

Jan 21 2011
Author:

I create the PVS-Studio analyzer detecting errors in source code of C/C++/C++0x software. So I have to review a large amount of source code of various applications where we detected suspicious code fragments with the help of PVS-Studio. I have collected a lot of examples demonstrating that an error occurred because of copying and modifying a code fragment. Of course, it has been known for a long time that using Copy-Paste in programming is a bad thing. But let's try to investigate this problem closely instead of limiting ourselves to just saying "do not copy the code".

Usually, when saying about the Copy-Paste method in programming, people mean the following case. Some function or a large code fragment is copied and then this copied code is modified. It causes large amounts of similar code to appear in the program, which complicates its maintenance. You have to replace the same fragments of an algorithm in different functions, so you may easily forget to fix something.

In this case, it is really appropriate to advise not to copy code. If you have some function and want to create a function with similar behavior, you should make a refactoring and arrange the common code in separate methods/classes [1], or use templates and lambda-functions. We will not dwell upon the question how to avoid doubling code because it does not relate to the main issue. What is the most important, you should avoid doubling code in different functions wherever possible. It has been written a lot about this and most programmers are familiar with recommendations.

Now let's focus on the thing authors of books and articles on writing quality code usually do not speak of. Actually, programming is impossible without Copy-Paste.

We all copy small code fragments when we need to write something like this:

GetMenu()->CheckMenuItem(IDC_ LINES_X, MF_BYCOMMAND | nState);
GetMenu()->CheckMenuItem(IDC_ LINES_Y, MF_BYCOMMAND | nState);

In good conscience, we always feel reluctant to type a line that differs from another line only in the 'Y' character used instead of 'X'. And this is right and reasonable. It is faster to copy and edit text than type a second line from the very beginning even with the help of special tools such as Visual Assist and IntelliSence.

Note that it is unreasonable to speak about doubling code here: you cannot make it simpler anyway. There are a lot of such examples in every program. If you don't like that we deal with GUI in the sample above, well, take some other task - you will get the same thing:

int texlump1 = Wads.CheckNumForName("TEXTURE1", ns_global, wadnum);
int texlump2 = Wads.CheckNumForName("TEXTURE2", ns_global, wadnum);

The problem is that an error is also highly probable when using this "microcopying". Since you copy such small code fragments much more often than large blocks, it is really a crucial issue. It is not clear how to deal with it, so they try not to speak about it. You cannot prohibit programmers from copying code.

Many of such errors are detected at the first launch of the program and are eliminated quickly and painlessly. But a lot of them remain in code and live for years waiting for their time to show up. Such errors are rather difficult to detect because a person has to review similar code lines and gradually gets less attentive. The probability of Copy-Paste related errors does not depend on programmer's skill. Any person might make a misprint and miss something. Defects of this type occur even in very famous and quality products.

To make it clearer what errors we mean, let's consider several code samples taken from open-source projects. As advertising: I detected errors described in this article using the general analyzer included into PVS-Studio [2].

The following code is taken from the Audacity application intended for sound recording and editing.

sampleCount VoiceKey::OnBackward (...) {
  ...
  int atrend = sgn(
    buffer[samplesleft - 2]-buffer[samplesleft - 1]);
  int ztrend = sgn(
    buffer[samplesleft - WindowSizeInt-2]-
      buffer[samplesleft - WindowSizeInt-2]);
  ...
}

The programmer was courageous and wrote initialization of the 'atrend' variable correctly. Then he started to write initialization of the 'ztrend' variable. He wrote "sgn(buffer[samplesleft - WindowSizeInt-2]", gave a sigh and copied the line fragment which he then forgot to edit. As a result, the 'sgn' function gets 0 as an argument.

The following scenario is the same. The programmer writes a long condition in 3D SDK Crystal Space:

inline_ bool Contains(const LSS& lss)
{
  // We check the LSS contains the two 
  // spheres at the start and end of the sweep
  return
    Contains(Sphere(lss.mP0, lss.mRadius)) && 
    Contains(Sphere(lss.mP0, lss.mRadius));
}

One cannot resist the urge to copy "Contains(Sphere(lss.mP0, lss.mRadius))" and replace the name 'mP0' with 'mP1'. But it is so easy to forget about it.

Perhaps you noticed sometimes that program windows started behaving in a strange way. For instance, many programmers will remember the search window in the first edition of Visual Studio 2010. I think such strange things occur due to luck and code like this:

void COX3DTabViewContainer::OnNcPaint() 
{
  ...
  if(rectClient.top<rectClient.bottom &&
     rectClient.top<rectClient.bottom)
  {
    dc.ExcludeClipRect(rectClient);
  }
  ...
}

This code was taken from a famous class set Ultimate ToolBox. Whether the control is drawn correctly or not depends upon its location.

And in eLynx Image Processing SDK, programmers copied a whole line therefore spreading the misprint throughout the code.

void uteTestRunner::StressBayer(uint32 iFlags)
{
  ...
  static EPixelFormat ms_pfList[] = 
    { PF_Lub, PF_Lus, PF_Li, PF_Lf, PF_Ld };
  const int fsize = sizeof(ms_pfList) / sizeof(ms_pfList);

  static EBayerMatrix ms_bmList[] = 
    { BM_GRBG, BM_GBRG, BM_RGGB, BM_BGGR, BM_None };
  const int bsize = sizeof(ms_bmList) / sizeof(ms_bmList);
  ...
}

The pointer dereferencing operation missing here causes the 'fsize' variable to equal 1. Then this code was adapted for initializing 'bsize'. I do not believe that one can make such a mistake twice without copying the code.

In the EIB Suite project, it was the line "if (_relativeTime <= 143)" which was copied and edited. But they forgot to change it in the last condition:

string TimePeriod::toString() const
{
  ...
  if (_relativeTime <= 143)
    os << ((int)_relativeTime + 1) * 5 << _(" minutes");
  else if (_relativeTime <= 167)
    os << 12 * 60 + ((int)_relativeTime - 143) * 30 << _(" minutes");
  else if (_relativeTime <= 196)
    os << (int)_relativeTime - 166 << _(" days");
  else if (_relativeTime <= 143)
    os << (int)_relativeTime - 192 << _(" weeks");
  ...
}

It means that the code "os << (int)_relativeTime - 192 << _(" weeks");" will never get control.

Even programmers in Intel company are only programmers and not demigods. Here is a bad copying in the TickerTape project:

void DXUTUpdateD3D10DeviceStats(...)
{
  ...
  else if( DeviceType == D3D10_DRIVER_TYPE_SOFTWARE )
    wcscpy_s( pstrDeviceStats, 256, L"WARP" );
  else if( DeviceType == D3D10_DRIVER_TYPE_HARDWARE )
    wcscpy_s( pstrDeviceStats, 256, L"HARDWARE" );
  else if( DeviceType == D3D10_DRIVER_TYPE_SOFTWARE )
    wcscpy_s( pstrDeviceStats, 256, L"SOFTWARE" );
  ...
}

The "DeviceType == D3D10_DRIVER_TYPE_SOFTWARE" condition is repeated twice.

Well, it is quite easy to miss an error in the jungle of conditional statements. In the implementation Multi-threaded Dynamic Queue, one and the same branch of the code will be executed regardless of the value returned by the IsFixed() function:

BOOL CGridCellBase::PrintCell(...)
{
  ...
  if(IsFixed())
    crFG = (GetBackClr() != CLR_DEFAULT) ?
      GetTextClr() : pDefaultCell->GetTextClr();
  else
    crFG = (GetBackClr() != CLR_DEFAULT) ?
      GetTextClr() : pDefaultCell->GetTextClr();
  ...
}

By the way, how easy and pleasant it is to copy code! You can afford one more line. :)

void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) {
  ...
  unsigned char invModulate[3];
  ...
  invModulate[0] = 255 - backEnd.currentEntity->e.shaderRGBA[0];
  invModulate[1] = 255 - backEnd.currentEntity->e.shaderRGBA[1];
  invModulate[2] = 255 - backEnd.currentEntity->e.shaderRGBA[2];
  invModulate[3] = 255 - backEnd.currentEntity->e.shaderRGBA[3];
  ...
}

It does not matter that the 'invModulate' array consists only of three items. This code is taken from the legendary game Wolfenstein 3D.

And here is a more complicated sample in the end. This code is taken from a rather useful tool Notepad++.

void KeyWordsStyleDialog::updateDlg() 
{
  ...
  Style & w1Style =
    _pUserLang->_styleArray.getStyler(STYLE_WORD1_INDEX);
  styleUpdate(w1Style, _pFgColour[0], _pBgColour[0],
    IDC_KEYWORD1_FONT_COMBO, IDC_KEYWORD1_FONTSIZE_COMBO,
    IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK,
    IDC_KEYWORD1_UNDERLINE_CHECK);

  Style & w2Style =
    _pUserLang->_styleArray.getStyler(STYLE_WORD2_INDEX);
  styleUpdate(w2Style, _pFgColour[1], _pBgColour[1],
    IDC_KEYWORD2_FONT_COMBO, IDC_KEYWORD2_FONTSIZE_COMBO,
    IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK,
    IDC_KEYWORD2_UNDERLINE_CHECK);

  Style & w3Style =
    _pUserLang->_styleArray.getStyler(STYLE_WORD3_INDEX);
  styleUpdate(w3Style, _pFgColour[2], _pBgColour[2],
    IDC_KEYWORD3_FONT_COMBO, IDC_KEYWORD3_FONTSIZE_COMBO,
    IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK,
    IDC_KEYWORD3_UNDERLINE_CHECK);

  Style & w4Style =
    _pUserLang->_styleArray.getStyler(STYLE_WORD4_INDEX);
  styleUpdate(w4Style, _pFgColour[3], _pBgColour[3],
    IDC_KEYWORD4_FONT_COMBO, IDC_KEYWORD4_FONTSIZE_COMBO,
    IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK,
    IDC_KEYWORD4_UNDERLINE_CHECK);
  ...
}

You have to strain your eyes greatly trying to find an error here. So let me abridge this code to make it clearer:

styleUpdate(...
  IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK,
  ...);
styleUpdate(...
  IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK,
  ...);
styleUpdate(...
  IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK,
  ...);
styleUpdate(...
  IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK,
  ...);

The developer's hand shook and he copied a wrong resource's name.

I can give you other defect code fragments in this article, but it is not interesting. I just wanted to say by all these examples that such errors can be found in various projects and both novice programmers and skilled programmers make them. Now let's discuss what we should do with all that stuff.

Well, to be frank, I do not have a complete answer. At least, I never read about such situations in books but often came across consequences of small Copy-Paste's in practice, including my own applications. So I will have to improvise while answering the question.

Let's proceed from the following suggestion:

Programmers are copying code fragments and will continue doing this because it is convenient. So, these errors will always occur in programs.

My conclusion is:

You cannot prevent such errors completely but you may try to make them less probable.

I see two ways of how we could make errors of this type fewer. First, we should use static code analyzers. They allow us to detect many errors of this class at the very early stages. It is cheaper and easier to find and fix an error right after writing the code than handle the same error detected during the testing.

The second method to make errors fewer in some cases is to discipline oneself and edit the code being copied in a special way. For example:

int ztrend = sgn(
  buffer[samplesleft - WindowSizeInt-2]-buffer[samplesleft 
- WindowSizeInt-2]);

It is much easier to notice an error when the code is written in the following way:

int ztrend = sgn(
  buffer[samplesleft - WindowSizeInt-2] -
  buffer[samplesleft - WindowSizeInt-2]);

You should edit the code so that fragments which must differ from each other are visually arranged in a column. It is much more difficult to make an error if you use this method. Of course, it will not save you in many cases - I have mentioned such samples above. But still it is better than nothing.

Unfortunately, I do not know any other ways to reduce the number of Copy-Paste related errors. You may use tools to search for repeated and similar code but it rather refers to my advice concerning using static analyzers.

So, I appeal to you readers. I will appreciate if you share some of your ideas concerning this issue with me and offer some other methods of avoiding Copy-Paste related errors. Perhaps we will get nice ideas that will help many programmers.

Please, send your feedback to this address karpov[@]viva64.com and I will be glad if I manage to extend this article with your help.

References

  • Steve McConnell, "Code Complete, 2nd Edition" Microsoft Press, Paperback, 2nd edition, Published June 2004, 914 pages, ISBN: 0-7356-1967-0. (Part 24.3. Reasons to Refactor)
  • Presentation "PVS-Studio, a complex solution for developers of modern resource-intensive applications". /en/pvs-studio-presentation/

Popular related articles
Free PVS-Studio for those who develops open source projects

Date: Dec 22 2018

Author: Andrey Karpov

On the New 2019 year's eve, a PVS-Studio team decided to make a nice gift for all contributors of open-source projects hosted on GitHub, GitLab or Bitbucket. They are given free usage of PVS-Studio s…
Technologies used in the PVS-Studio code analyzer for finding bugs and potential vulnerabilities

Date: Nov 21 2018

Author: Andrey Karpov

A brief description of technologies used in the PVS-Studio tool, which let us effectively detect a large number of error patterns and potential vulnerabilities. The article describes the implementati…
How PVS-Studio Proved to Be More Attentive Than Three and a Half Programmers

Date: Oct 22 2018

Author: Andrey Karpov

Just like other static analyzers, PVS-Studio often produces false positives. What you are about to read is a short story where I'll tell you how PVS-Studio proved, just one more time, to be more atte…
The Last Line Effect

Date: May 31 2014

Author: Andrey Karpov

I have studied many errors caused by the use of the Copy-Paste method, and can assure you that programmers most often tend to make mistakes in the last fragment of a homogeneous code block. I have ne…
The Evil within the Comparison Functions

Date: May 19 2017

Author: Andrey Karpov

Perhaps, readers remember my article titled "Last line effect". It describes a pattern I've once noticed: in most cases programmers make an error in the last line of similar text blocks. Now I want t…
Appreciate Static Code Analysis!

Date: Oct 16 2017

Author: Andrey Karpov

I am really astonished by the capabilities of static code analysis even though I am one of the developers of PVS-Studio analyzer myself. The tool surprised me the other day as it turned out to be sma…
PVS-Studio for Java

Date: Jan 17 2019

Author: Andrey Karpov

In the seventh version of the PVS-Studio static analyzer, we added support of the Java language. It's time for a brief story of how we've started making support of the Java language, how far we've co…
The Ultimate Question of Programming, Refactoring, and Everything

Date: Apr 14 2016

Author: Andrey Karpov

Yes, you've guessed correctly - the answer is "42". In this article you will find 42 recommendations about coding in C++ that can help a programmer avoid a lot of errors, save time and effort. The au…
PVS-Studio ROI

Date: Jan 30 2019

Author: Andrey Karpov

Occasionally, we're asked a question, what monetary value the company will receive from using PVS-Studio. We decided to draw up a response in the form of an article and provide tables, which will sho…
Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries, 10-15% of False Positives

Date: Jul 31 2017

Author: Andrey Karpov

After I wrote quite a big article about the analysis of the Tizen OS code, I received a large number of questions concerning the percentage of false positives and the density of errors (how many erro…

Comments (0)

Next comments

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
This website uses cookies and other technology to provide you a more personalized experience. By continuing the view of our web-pages you accept the terms of using these files. If you don't want your personal data to be processed, please, leave this site.
Learn More →
Accept