Nol, satu, dua, Freddy akan menjemputmu

Gambar 1

Berikut ini adalah kelanjutan dari serangkaian artikel yang dapat berjudul "kengerian untuk programmer." Kali ini kita akan berbicara tentang pola kesalahan ketik khas yang terkait dengan penggunaan angka 0, 1, 2. Tidak masalah jika Anda menulis dalam C, C ++, C # atau Java. Jika Anda menggunakan konstanta 0, 1, 2, atau jika angka-angka ini terkandung dalam nama variabel, maka, kemungkinan besar, Freddy akan mengunjungi Anda di malam hari. Baca dan jangan katakan nanti bahwa Anda tidak diperingatkan.


pengantar


Saya melanjutkan serangkaian artikel yang ditujukan untuk pola-pola yang diperhatikan dalam cara orang membuat kesalahan. Publikasi sebelumnya:
  1. Efek baris terakhir
  2. Fungsi paling berbahaya di dunia C / C ++
  3. Jahat hidup dalam fungsi perbandingan

Kali ini polanya tidak saya perhatikan, tetapi oleh rekan kerja saya Svyatoslav Razmyslov. Dia menarik perhatian pada fakta bahwa dia terus-menerus menggambarkan dalam artikelnya masalah di mana variabel yang berisi angka 1 dan 2. muncul dalam namanya. Svyatoslav menyarankan agar saya mempelajari topik ini lebih detail dan itu ternyata sangat bermanfaat. Ternyata kumpulan kesalahan kami berisi sejumlah besar fragmen kode yang salah karena kenyataan bahwa orang-orang menjadi bingung dalam indeks 0, 1, 2 atau dalam nama variabel yang berisi angka-angka tersebut. Keteraturan baru yang menarik terungkap, yang akan dibahas di bawah ini. Saya berterima kasih kepada Svyatoslav untuk prompt untuk menyelidiki topik ini dan mendedikasikan artikel ini kepadanya.

Gambar 14

Svyatoslav Razmyslov, manajer, penangkap serangga yang penuh perhatian dan hanya orang yang berbakat.

Apa tujuan dari artikel ini? Tunjukkan betapa mudahnya kita semua membuat kesalahan dan kesalahan ketik. Jika programmer diperingatkan, mereka akan lebih penuh perhatian dalam proses review kode, dengan fokus pada 0, 1, 2. yang bernasib buruk. Selain itu, programmer akan lebih bisa merasakan nilai analisa kode statis yang membantu untuk melihat kesalahan seperti itu. Ini bukan tentang iklan PVS-Studio (meskipun juga :). Sampai sekarang, banyak programmer menemukan analisis statik berlebihan, lebih suka berkonsentrasi pada akurasi dan ulasan kode mereka sendiri. Sayangnya, mencoba menulis kode tanpa kesalahan itu baik, tetapi tidak cukup. Artikel ini sekali lagi akan menunjukkan ini dengan jelas.

Tidak ada yang kebal dari kesalahan seperti itu. Di bawah ini Anda akan melihat bloopers epik bahkan dalam proyek-proyek terkenal seperti Qt, Clang, Hive, LibreOffice, Linux Kernel, Platform .NET Compiler Platform, XNU kernel, Mozilla Firefox. Dan ini bukan beberapa kesalahan langka yang eksotis, tetapi yang paling sering. Tidak meyakinkan? Kalau begitu mari kita mulai!

β€œObrolan tidak berharga! Tunjukkan bug! ”

Β© kutipan yang dimodifikasi oleh Linus Torvalds.

Kesalahan ketik dalam konstanta saat mengindeks array


Sebagai aturan, dalam artikel kami, kami memberikan peringatan dengan bantuan kesalahan yang ditemukan. Kali ini saya akan menghilangkan peringatan ini, karena tanpa mereka kesalahan akan mudah terlihat dan dimengerti. Namun, meskipun kesalahan ini segera terbukti dalam potongan kode pendek, mereka tahu bagaimana cara menyembunyikan dalam kode proyek.

Mari kita mulai dengan kesalahan ketika ada kebingungan dengan literal numerik yang digunakan untuk mengindeks array. Meskipun banality kesalahan ini, ada banyak dari mereka dan mereka tidak terungkap dalam pekerjaan laboratorium siswa.

Proyek kernel XNU, bahasa C.
uint32_t
gss_krb5_3des_unwrap_mbuf(....)
{
  ....
  for (cflag = 1; cflag >= 0; cflag--) {
    *minor = gss_krb5_3des_token_get(
       ctx, &itoken, wrap, &hash, &offset, &length, reverse);
    if (*minor == 0)
      break;
    wrap.Seal_Alg[0] = 0xff;
    wrap.Seal_Alg[0] = 0xff;
  }
  ....
}

Baris telah disalin, tetapi lupa untuk memperbaiki indeks. Seperti yang saya pahami, itu harus ditulis di sini:
wrap.Seal_Alg[0] = 0xff;
wrap.Seal_Alg[1] = 0xff;

Proyek LibreOffice, C ++
Sequence< OUString > FirebirdDriver::
  getSupportedServiceNames_Static() throw (RuntimeException)
{
  Sequence< OUString > aSNS( 2 );
  aSNS[0] = "com.sun.star.sdbc.Driver";
  aSNS[0] = "com.sun.star.sdbcx.Driver";
  return aSNS;
}

Seperti pada kasus sebelumnya, baris disalin, tetapi lupa untuk memperbaiki 0 oleh 1. Hanya string literal yang diperbaiki.

Seseorang mungkin mengajukan pertanyaan filosofis, bagaimana seseorang dapat membuat kesalahan seperti itu dalam fungsi empat baris? Semua itu mungkin. Ini dia, pemrograman.

Proyek Gempa-III-Arena, bahasa C.
int VL_FindAdjacentSurface(....)
{
  ....
  if (fabs(dir[0]) > test->radius ||
      fabs(dir[1]) > test->radius ||
      fabs(dir[1]) > test->radius)
  {
  ....
}

Di baris yang disalin, mereka lupa mengganti dir [1] dengan dir [2] . Akibatnya, nilai sepanjang sumbu Z tidak terkontrol.

Proyek OpenCOLLADA, C ++
struct short2
{
  short values[2];
  short2(short s1, short s2)
  {
    values[0] = s1;
    values[2] = s2;
  }
  ....
};

Ya, bahkan dalam konstruktor yang begitu singkat, Anda dapat mengatur untuk melampaui batas array saat diinisialisasi.

Angka 8


Proyek Mesin Godot, C ++
Array PhysicsDirectSpaceState::_cast_motion(....)
{
  ....
  Array ret(true);
  ret.resize(2);
  ret[0]=closest_safe;
  ret[0]=closest_unsafe;
  return ret;
}

Tidak diperlukan komentar.

Proyek Asterisk, bahasa C.
static void sip_threadinfo_destructor(void *obj)
{
  struct sip_threadinfo *th = obj;
  struct tcptls_packet *packet;

  if (th->alert_pipe[1] > -1) {            // <=
    close(th->alert_pipe[0]);
  }
  if (th->alert_pipe[1] > -1) {
    close(th->alert_pipe[1]);
  }
  th->alert_pipe[0] = th->alert_pipe[1] = -1;
  ....
}

Saat menulis jenis blok yang sama, kesalahan, sebagai aturan, terletak di yang mendasarinya. Sebelum ini, semua kasus yang dipertimbangkan hanya itu. Di sini kesalahan ketik ada di tempat yang tidak biasa, yaitu di blok pertama. Mengapa itu terjadi sulit dikatakan. Saya tidak punya pilihan selain membawa gambar unicorn mengangkat bahu:

Gambar 9


Buka Proyek Teknologi CASCADE, C ++
inline void Prepend(const Standard_Integer theIndex)
{
  if (myIndex[1] >= 0)
    Standard_OutOfRange::Raise ("BRepMesh_PairOfIndex....");

  myIndex[1] = myIndex[0];
  myIndex[1] = theIndex;
}

Dua kali di sel array yang sama, nilai yang berbeda disalin. Kesalahan yang jelas, tetapi cara memperbaikinya tidak jelas bagi saya, karena kode proyek tidak saya kenal. Jadi saya hanya melihat bagaimana pengembang memperbaiki kode setelah tim kami menunjukkan kesalahan ini kepada mereka. Opsi yang benar:
myIndex[1] = myIndex[0];
myIndex[0] = theIndex;

Proyek Pipa Trans-Proteomik, C ++
void ASAPRatio_getProDataStrct(proDataStrct *data,
                               char **pepBofFiles)
{
  ....
  if (data->indx == -1) {
    data->ratio[0] = -2.;
    data->ratio[0] = 0.;             // <=
    data->inv_ratio[0] = -2.;
    data->inv_ratio[1] = 0.;
    return;
  }
  ....
}

Saya khawatir bahwa paket penelitian mengandung kesalahan seperti itu. Trans-Proteomic Pipeline dirancang untuk menyelesaikan masalah di bidang biologi. Ini dapat diputuskan dan "diselidiki." Paket ini umumnya menemukan banyak hal menarik: check in 2012 , check 2013 . Mungkin Anda dapat mencoba lagi melihat proyek ini.

Proyek ITK, bahasa C ++

Kami dihadapkan dengan proyek lain untuk melakukan penelitian di bidang kedokteran: Segmentasi Insight Obat dan Perangkat Pendaftaran (ITK). Proyeknya berbeda, tetapi kesalahannya sama.
template< typename TCoordRepType >
void
VoronoiDiagram2D< TCoordRepType >::SetOrigin(PointType vorsize)
{
  m_VoronoiBoundaryOrigin[0] = vorsize[0];
  m_VoronoiBoundaryOrigin[0] = vorsize[1];
}

Proyek ITK, C ++


int itkPointSetToSpatialObjectDemonsRegistrationTest(....)
{
  ....
  // Set its position
  EllipseType::TransformType::OffsetType offset;
  offset[0]=50;
  offset[1]=50;
  offset[1]=50;
  ....
}

Bersihkan Copy-Paste.

Proyek ReactOS, C ++
HPALETTE CardWindow::CreateCardPalette()
{
  ....
  //include button text colours
  cols[0] = RGB(0, 0, 0);
  cols[1] = RGB(255, 255, 255);

  //include the base background colour
  cols[1] = crBackgnd;

  //include the standard button colours...
  cols[3] = CardButton::GetHighlight(crBackgnd);
  cols[4] = CardButton::GetShadow(crBackgnd);
  cols[5] = CardButton::GetFace(crBackgnd);
  ....
}

Kemungkinan besar, konstanta crBackgnd harus ditulis ke sel cols [2] .

Proyek Coin3D, C ++
SoVRMLInline::GLRender(SoGLRenderAction * action)
{
  ....
  if ((size[0] >= 0.0f && size[1] >= 0.0f && size[1] >= 0.0f) &&
      ((vis == ALWAYS) ||
       (vis == UNTIL_LOADED && child == NULL))) {
  ....
}

Elemen array ukuran [1] diperiksa dua kali lipat , dan elemen ukuran [2] tidak dicentang. Ini adalah bagaimana artefak aneh muncul pada gambar.

Proyek OpenCV, C ++
bool Jpeg2KDecoder::readHeader()
{
  ....
  cmptlut[0] = ....
  cmptlut[1] = ....
  cmptlut[2] = ....
  if( cmptlut[0] < 0 || cmptlut[1] < 0 || cmptlut[0] < 0 )
    result = false;
  ....
}

Secara langsung dirasakan bahwa ekspresi cmptlut [0] <0 digandakan dua kali dengan menyalin, tetapi dikoreksi nol hanya di satu tempat.

Proyek Visualisasi Toolkit (VTK), C ++
void vtkImageStencilRaster::PrepareForNewData(....)
{
  ....
  if (allocateExtent &&
      allocateExtent[1] >= allocateExtent[1])
  ....
}

Selanjutnya, saya tidak akan mengomentari banyak kesalahan seperti itu. Apa yang bisa dikomentari? Hal utama ketika melihat fragmen kode tersebut adalah untuk merasa bahwa meskipun kesalahannya sederhana, ini tidak berarti bahwa itu akan diperhatikan oleh programmer.

Proyek Visualisasi Toolkit (VTK), C ++
template <class iterT>
void vtkDataSetAttributesCopyValues(....)
{
  ....
  inZPtr +=
    (outExt[0] - outExt[0])*inIncs[0] * data_type_size +
    (outExt[2] - outExt[2])*inIncs[1] * data_type_size +
    (outExt[4] - outExt[4])*inIncs[2] * data_type_size;
  ....
}

Di sini, programmer jelas tergesa-gesa untuk menulis kode lebih cepat. Sulit untuk menjelaskan dengan cara lain bagaimana dia membuat kesalahan tiga kali. Elemen array dikurangi dari dirinya sendiri. Hasilnya adalah kode ini setara:
inZPtr +=
  (0)*inIncs[0] * data_type_size +
  (0)*inIncs[1] * data_type_size +
  (0)*inIncs[2] * data_type_size;

Namun, kode ini dapat dikurangi lebih jauh:
inZPtr += 0;

Dengan mewah. Ada ekspresi panjang dan serius dalam kode yang, pada kenyataannya, tidak melakukan apa pun. Saya suka kasus seperti itu.

Proyek Visualisasi Toolkit (VTK), bahasa C ++

Kasus serupa dari kode penulisan tergesa-gesa.
void vtkPiecewiseControlPointsItem::SetControlPoint(
  vtkIdType index, double* newPos)
{
  double oldPos[4];
  this->PiecewiseFunction->GetNodeValue(index, oldPos);
  if (newPos[0] != oldPos[0] || newPos[1] != oldPos[1] ||
      newPos[2] != oldPos[2] || newPos[2] != oldPos[2])
    {
      this->PiecewiseFunction->SetNodeValue(index, newPos);
    }
}

Perbandingan newPos [2]! = OldPos [2] diulang dua kali .

Proyek Lingkungan Komunikasi (ACE) ADAPTIF, C ++
bool URL_Base::strip_scheme (ACE_CString& url_string)
{
  ....
  ACE_CString::size_type pos = url_string.find (':');
  if (pos > 0 &&
      url_string[pos+1] == '/' &&
      url_string[pos+1] == '/')
  {
    ....
    // skip '<protocol>://'
    url_string = url_string.substr (pos+3);
  }
  ....
}

Kondisi tersebut harus memverifikasi bahwa dua garis miring setelah titik dua ditemukan. Dengan kata lain, substring ": //" dicari. Karena kesalahan ketik, cek "buta" dan siap untuk menghitung karakter apa pun sebagai garis miring kedua.

Proyek Sampel IPP, C ++
void MeBase::MakeVlcTableDecision()
{
  ....
  Ipp32s BestMV =
    IPP_MIN(IPP_MIN(m_cur.MvRate[0],m_cur.MvRate[1]),
                    IPP_MIN(m_cur.MvRate[2],m_cur.MvRate[3]));
  Ipp32s BestAC =
    IPP_MIN(IPP_MIN(m_cur.AcRate[0],m_cur.AcRate[1]),
                    IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[2]));
  ....
}

Kesalahan ketik ada di sini, dalam argumen yang diteruskan ke makro:
IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[2])

Ternyata minimal dua nilai identik dipilih. Bahkan, itu harus ditulis:
IPP_MIN(m_cur.AcRate[2],m_cur.AcRate[3])

By the way, kode ini dapat menunjukkan kegunaan perpustakaan standar. Jika Anda menulis seperti ini:
Ipp32s BestMV = std::min_element(begin(m_cur.MvRate), end(m_cur.MvRate));
Ipp32s BestAC = std::min_element(begin(m_cur.AcRate), end(m_cur.AcRate));

Kode itu akan menjadi lebih pendek dan lebih tidak rentan terhadap kesalahan. Sebenarnya, semakin sedikit jenis kode yang sama, semakin besar kemungkinan akan ditulis dengan benar.

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

Ekspresi yang benar adalah:
int ztrend = sgn(buffer[samplesleft - WindowSizeInt-2]-
                 buffer[samplesleft - WindowSizeInt-1]);

Proyek PDFium, bahasa C ++
void sycc420_to_rgb(opj_image_t* img) {
  ....
  opj_image_data_free(img->comps[0].data);
  opj_image_data_free(img->comps[1].data);
  opj_image_data_free(img->comps[2].data);
  img->comps[0].data = d0;
  img->comps[1].data = d1;
  img->comps[2].data = d2;
  img->comps[1].w = yw;                 // 1
  img->comps[1].h = yh;                 // 1
  img->comps[2].w = yw;                 // 1
  img->comps[2].h = yh;                 // 1
  img->comps[1].w = yw;                 // 2
  img->comps[1].h = yh;                 // 2
  img->comps[2].w = yw;                 // 2
  img->comps[2].h = yh;                 // 2
  img->comps[1].dx = img->comps[0].dx;
  img->comps[2].dx = img->comps[0].dx;
  img->comps[1].dy = img->comps[0].dy;
  img->comps[2].dy = img->comps[0].dy;
}

Serangkaian tindakan untuk menginisialisasi struktur digandakan. Baris-baris yang ditandai dengan komentar // 2 dapat dihapus, dan tidak ada yang akan berubah. Saya ragu apakah akan memasukkan potongan kode ini dalam artikel. Ini bukan kesalahan, dan tidak cukup dengan indeks. Namun, kode tambahan ini kemungkinan besar muncul justru karena programmer bingung dalam semua anggota kelas ini dan indeks 1, 2. Jadi, saya pikir kode ini cocok untuk menunjukkan betapa mudahnya menjadi bingung dalam jumlah.

Proyek CMake, C

Kode yang dibahas di bawah ini tidak ditulis oleh pengembang CMake, tetapi dipinjam. Dilihat oleh komentar di awal file, fungsi utf8_encodeItu ditulis oleh Tim Kientzle pada tahun 2007. Sejak itu, fungsi ini telah mengembara dari proyek ke proyek, dan ada banyak dari mereka. Saya tidak mempelajari masalah sumber aslinya, karena ini bukan intinya sekarang. Karena kode ini ada dalam proyek CMake, maka kesalahan berlaku untuk CMake.
static char *
utf8_encode(const wchar_t *wval)
{
  ....
  p[0] = 0xfc | ((wc >> 30) & 0x01);
  p[1] = 0x80 | ((wc >> 24) & 0x3f);
  p[1] = 0x80 | ((wc >> 18) & 0x3f);
  p[2] = 0x80 | ((wc >> 12) & 0x3f);
  p[3] = 0x80 | ((wc >> 6) & 0x3f);
  p[4] = 0x80 | (wc & 0x3f);
  p += 6;
  ....
}

Seperti yang Anda lihat, ada beberapa jenis kebingungan dengan indeks. Dua kali ada catatan dalam elemen array p [1] . Jika Anda mempelajari kode di lingkungan, menjadi jelas bahwa kode yang benar harus seperti ini:
p[0] = 0xfc | ((wc >> 30) & 0x01);
p[1] = 0x80 | ((wc >> 24) & 0x3f);
p[2] = 0x80 | ((wc >> 18) & 0x3f);
p[3] = 0x80 | ((wc >> 12) & 0x3f);
p[4] = 0x80 | ((wc >> 6) & 0x3f);
p[5] = 0x80 | (wc & 0x3f);
p += 6;

Catatan

Harap dicatat bahwa semua kesalahan yang dibahas dalam bab ini terkait dengan kode C atau C ++. Tidak ada C # atau Kode Java!

Ini sangat menarik, saya tidak mengharapkan ini. Menurut pendapat saya, kesalahan ketik dianggap tidak tergantung pada bahasa. Dan dalam bab-bab berikut, kesalahan dalam kode dalam bahasa lain memang akan muncul. Saya pikir ini hanya kebetulan. Alat analisa PVS-Studio mulai jauh kemudian untuk mendukung bahasa C # / Java dibandingkan dengan C / C ++, dan kami sama sekali tidak berhasil mengumpulkan contoh kesalahan yang sesuai dalam database.

Namun, pengamatannya masih menarik. Rupanya, programmer C dan C ++ suka menggunakan angka 0, 1 dan 2 lebih banyak ketika bekerja dengan array :).

Salah mengeja dalam nama


Ini akan menjadi bagian terbesar. Sangat mudah bagi orang untuk bingung dengan nama seperti a1 dan a2 . Sepertinya Anda bisa bingung di sini? Bisa. Mudah. Dan sekarang pembaca akan dapat memverifikasi ini.

Proyek Sarang, Jawa
@Override
public List<ServiceInstance> getAllInstancesOrdered() {
  List<ServiceInstance> list = new LinkedList<>();
  list.addAll(instances.values());
  Collections.sort(list, new Comparator<ServiceInstance>() {
    @Override
    public int compare(ServiceInstance o1, ServiceInstance o2) {
      return o2.getWorkerIdentity().compareTo(o2.getWorkerIdentity());
    }
  });
  return list;
}

Fungsi membandingkan membandingkan mengambil dua objek: o1 dan o2 . Tetapi karena kesalahan ketik, hanya o2 yang digunakan lebih lanjut .

Menariknya, berkat Salin-Tempel, kesalahan ini bermigrasi ke fungsi lain:
@Override
public List<ServiceInstance> getAllInstancesOrdered() {
  List<ServiceInstance> list = new LinkedList<>();
  readLock.lock();
  try {
    list.addAll(instances.values());
  } finally {
    readLock.unlock();
  }
  Collections.sort(list, new Comparator<ServiceInstance>() {
    @Override
    public int compare(ServiceInstance o1, ServiceInstance o2) {
      return o2.getWorkerIdentity().compareTo(o2.getWorkerIdentity());
    }
  });
  return list;
}

Gambar 10


Proyek Infer.NET, bahasa C #
private void MergeParallelTransitions()
{
  ....
  if (double.IsInfinity(transition1.Weight.Value) &&    
      double.IsInfinity(transition1.Weight.Value))
  ....
}

Proyek Doom 3, C ++
uint AltOp::fixedLength()
{
  uint l1 = exp1->fixedLength();
  uint l2 = exp1->fixedLength();

  if (l1 != l2 || l1 == ~0u)
    return ~0;

  return l1;
}

Jika seseorang tidak segera melihat kesalahan ketik, maka Anda perlu melihat baris di mana variabel l2 diinisialisasi . Harus menggunakan exp2 .

Proyek Sumber Mesin SDK, C ++
void GetFPSColor( int nFps, unsigned char ucColor[3] )
{
  ....
  int nFPSThreshold1 = 20;
  int nFPSThreshold2 = 15;

  if (IsPC() &&
      g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 95)
  {
    nFPSThreshold1 = 60;
    nFPSThreshold1 = 50;
  }
  ....
}

Benar:
nFPSThreshold1 = 60;
nFPSThreshold2 = 50;

Proyek Kernel Linux, bahasa C

By the way, kesalahan ketik bisa tidak hanya dalam nama variabel, tetapi juga dalam nama makro. Sekarang akan ada beberapa contoh seperti itu.
int private_ioctl(struct vnt_private *pDevice, struct ifreq *rq)
{
  ....
  if (sStartAPCmd.byBasicRate & BIT3) {
    pMgmt->abyIBSSSuppRates[2] |= BIT7;
    pMgmt->abyIBSSSuppRates[3] |= BIT7;
    pMgmt->abyIBSSSuppRates[4] |= BIT7;
    pMgmt->abyIBSSSuppRates[5] |= BIT7;
  } else if (sStartAPCmd.byBasicRate & BIT2) {
    pMgmt->abyIBSSSuppRates[2] |= BIT7;
    pMgmt->abyIBSSSuppRates[3] |= BIT7;
    pMgmt->abyIBSSSuppRates[4] |= BIT7;
  } else if (sStartAPCmd.byBasicRate & BIT1) {  // <=
    pMgmt->abyIBSSSuppRates[2] |= BIT7;
    pMgmt->abyIBSSSuppRates[3] |= BIT7;
  } else if (sStartAPCmd.byBasicRate & BIT1) {  // <=
    pMgmt->abyIBSSSuppRates[2] |= BIT7;
  } else {
    /* default 1,2M */
    pMgmt->abyIBSSSuppRates[2] |= BIT7;
    pMgmt->abyIBSSSuppRates[3] |= BIT7;
  }
  ....
}

Seperti yang Anda lihat, topeng yang disebut BIT1 digunakan dua kali , yang membuat pemeriksaan kedua tidak berguna. Badan pernyataan bersyarat komentar kedua tidak akan pernah dieksekusi.

Proyek CMaNGOS, C ++
void AttackedBy(Unit* pAttacker) override
{
  ....
  DoScriptText(urand(0, 1) ?
               SAY_BELNISTRASZ_AGGRO_1 :
               SAY_BELNISTRASZ_AGGRO_1,
               m_creature, pAttacker);
  ....
}

Perilaku acak sudah direncanakan dalam game, tetapi SAY_BELNISTRASZ_AGGRO_1 konstan yang sama selalu dipilih .

Vangers Project: One For The Road, C ++
const char* iGetJoyBtnNameText(int vkey,int lang)
{
  ....
  if (vkey >= VK_STICK_SWITCH_1 && vkey <= VK_STICK_SWITCH_9)
  {
     ret = (lang)
      ? iJoystickStickSwitch2[vkey - VK_STICK_SWITCH_1]
      : iJoystickStickSwitch2[vkey - VK_STICK_SWITCH_1];
    return ret;
  }
  ....
}

Menilai dengan kode yang ditulis di sebelahnya, opsi yang benar harus seperti ini:
ret = (lang)
  ? iJoystickStickSwitch2[vkey - VK_STICK_SWITCH_1]
  : iJoystickStickSwitch1[vkey - VK_STICK_SWITCH_1];

Proyek RT-Thread, bahasa C.
uint8_t can_receive_message_length(uint32_t can_periph,
                                   uint8_t fifo_number)
{
  uint8_t val = 0U;

  if(CAN_FIFO0 == fifo_number){
    val = (uint8_t)(CAN_RFIFO0(can_periph) & CAN_RFIFO_RFL0_MASK);
  }else if(CAN_FIFO0 == fifo_number){
    val = (uint8_t)(CAN_RFIFO1(can_periph) & CAN_RFIFO_RFL0_MASK);
  }else{
    /* illegal parameter */
  }
  return val;
}

RT-Thread adalah sumber terbuka, sistem operasi waktu-nyata untuk perangkat yang disematkan. Di sini kita melihat kebingungan antara FIFO 0 dan FIFO 1. Dan di suatu tempat, seseorang akan menemukan perangkat kereta.

Gambar 11


Kesalahan ada di sini:
if      (CAN_FIFO0 == fifo_number){
....
}else if(CAN_FIFO0 == fifo_number){

Pemeriksaan kedua selalu memberikan false. Benar:
if      (CAN_FIFO0 == fifo_number){
....
}else if(CAN_FIFO1 == fifo_number){

Proyek Sarang, Jawa
private void
generateDateTimeArithmeticIntervalYearMonth(String[] tdesc) throws Exception {
  String operatorName = tdesc[1];
  String operatorSymbol = tdesc[2];
  String operandType1 = tdesc[3];
  String colOrScalar1 = tdesc[4];
  String operandType2 = tdesc[5];
  String colOrScalar2 = tdesc[6];
  ....
  if (colOrScalar1.equals("Col") && colOrScalar1.equals("Column")) {
    ....
  } else if (colOrScalar1.equals("Col") && colOrScalar1.equals("Scalar")) {
    ....
}

Alat analisa PVS-Studio segera menunjukkan 2 kesalahan:
  1. Sebuah string yang disimpan dalam colOrScalar1 tidak dapat sama dengan string Kolom dan Kolom;
  2. String yang disimpan dalam colOrScalar1 tidak bisa sama dengan string Col dan Scalar pada saat yang sama.

Jelas ada kebingungan tentang nama variabel.

Proyek Shareaza, bahasa C ++
void CDownloadWithSources::MergeMetadata(const CXMLElement* pXML)
{
  CQuickLock pLock( Transfers.m_pSection );

  CXMLAttribute* pAttr1 =
    m_pXML->GetAttribute(CXMLAttribute::schemaName);
  CXMLAttribute* pAttr2 =
    pXML->GetAttribute(CXMLAttribute::schemaName);

  if (pAttr1 && pAttr2 &&
      !pAttr1->GetValue().CompareNoCase(pAttr1->GetValue()))
    ....
}

Benar:
pAttr1->GetValue().CompareNoCase(pAttr2->GetValue())

Catatan

Mari kita berhenti sejenak. Ada rasa takut bahwa melihat melalui gunung kesalahan dangkal, kita akan lupa mengapa kita melakukan ini.

Tugasnya bukan menertawakan kode orang lain. Semua ini bukan alasan untuk menusuk jari Anda dan berkata: "Ha ha, yah, kamu harus." Alasan untuk berpikir!

Publikasi tim kami dirancang untuk menunjukkan bahwa tidak ada dari kita yang kebal dari kesalahan. Kesalahan yang dijelaskan dalam artikel muncul dalam kode jauh lebih sering daripada yang Anda harapkan. Penting juga bahwa kemungkinan tersesat dalam 0, 1, 2 hampir tidak tergantung pada kualifikasi programmer.

Sangat berguna untuk menyadari bahwa orang cenderung membuat kesalahan. Tanpa ini, Anda tidak dapat mengambil langkah selanjutnya dalam meningkatkan kualitas dan keandalan kode. Memahami bahwa kita semua salah, orang-orang mulai mencoba mengidentifikasi kesalahan pada tahap paling awal, menggunakan standar pengkodean, ulasan kode, uji unit, analisis statis dan dinamis. Ini sangat bagus.

Mengapa hal-hal demikian dapat dimengerti ditulis? Sayangnya, berkomunikasi dengan sejumlah besar pengembang, kami terpaksa menyatakan bahwa itu tidak selalu jelas bagi semua orang. Banyak yang memiliki harga diri yang terlalu tinggi dan mereka tidak membiarkan pikiran bahwa mereka mampu membuat kesalahan sederhana. Sedih.

Jika Anda seorang pemimpin tim / manajer, maka saya mengundang Anda untuk membiasakan diri dengan catatan ini secara bersamaan .

Proyek Qt, C ++
AtomicComparator::ComparisonResult
IntegerComparator::compare(const Item &o1,
                           const AtomicComparator::Operator,
                           const Item &o2) const
{
  const Numeric *const num1 = o1.as<Numeric>();
  const Numeric *const num2 = o1.as<Numeric>();

  if(num1->isSigned() || num2->isSigned())
  ....
}

Benar:
const Numeric *const num2 = o2.as<Numeric>();

Proyek Android, bahasa C ++
static inline bool isAudioPlaybackRateEqual(
  const AudioPlaybackRate &pr1,
  const AudioPlaybackRate &pr2)
{
    return fabs(pr1.mSpeed - pr2.mSpeed) <
             AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
           fabs(pr1.mPitch - pr2.mPitch) <
             AUDIO_TIMESTRETCH_PITCH_MIN_DELTA &&
           pr2.mStretchMode == pr2.mStretchMode &&
           pr2.mFallbackMode == pr2.mFallbackMode;
}

Dua kesalahan ketik sekaligus, karena variabel pr2.mStretchMode dan pr2.mFallbackMode dibandingkan dengan mereka sendiri.

Tingkatkan Proyek, C ++
point3D operator/(const point3D &p1, const point3D &p2)
{
  return point3D(p1.x/p2.x, p1.y/p2.y, p1.z/p1.z);
}

Pada akhirnya, mereka menyegel dan membagi variabel p1.z itu sendiri.

Proyek Dentang, C ++
bool haveSameType(QualType Ty1, QualType Ty2) {
  return (Context.getCanonicalType(Ty1) ==
          Context.getCanonicalType(Ty2) ||
          (Ty2->isIntegerType() &&
           Ty2->isIntegerType()));
}

Ya, ya, penganalisa PVS-Studio menemukan kesalahan yang sama dalam kompiler. Benar:
(Ty1->isIntegerType() &&
 Ty2->isIntegerType())

Proyek Dentang, C ++
Instruction *InstCombiner::visitXor(BinaryOperator &I) {
  ....
  if (Op0I && Op1I && Op0I->isShift() &&
      Op0I->getOpcode() == Op1I->getOpcode() &&
      Op0I->getOperand(1) == Op1I->getOperand(1) &&
      (Op1I->hasOneUse() || Op1I->hasOneUse())) {
  ....
}

Benar:
(Op0I->hasOneUse() || Op1I->hasOneUse())

Proyek Qt, C ++
inline bool qCompare(QImage const &t1, QImage const &t2, ....)
{
  ....
  if (t1.width() != t2.width() || t2.height() != t2.height()) {
  ....
}

Proyek Meja Kerja Genome NCBI, C ++
static bool s_PCRPrimerSetLess(const CPCRPrimerSet& s1, const CPCRPrimerSet& s2)
{
  if (!s1.IsSet() && s1.IsSet()) {
    return true;
  } else if (s1.IsSet() && !s2.IsSet()) {
    return false;
  } else if (!s1.IsSet() && !s2.IsSet()) {
    return false;
  } else if (s1.Get().size() < s2.Get().size()) {
    return true;
  } else if (s1.Get().size() > s2.Get().size()) {
    return false;
  } else {
  .....
}

Kesalahan pada pemeriksaan pertama. Itu harus ditulis:
if (!s1.IsSet() && s2.IsSet()) {

Proyek Meja Kerja Genome NCBI, C ++
CRef<CSeq_align> CNWAligner::Run(CScope &scope, const CSeq_loc &loc1,
                                 const CSeq_loc &loc2, bool trim_end_gaps)
{
  if ((!loc1.IsInt() && !loc1.IsWhole()) ||
      (!loc1.IsInt() && !loc1.IsWhole()))
  {
    NCBI_THROW(CException, eUnknown,
               "Only whole and interval locations supported");
  }
  ....
}

Baris pertama dari kondisi itu diperbanyak, tetapi kemudian programmer mempercepat dan lupa untuk mengganti loc1 dengan loc2 .

Proyek FlashDevelop, C #
public void SetPrices(....)
{
  UInt32 a0 = _choice.GetPrice0();
  UInt32 a1 = _choice.GetPrice1();
  UInt32 b0 = a1 + _choice2.GetPrice0();   // <=
  UInt32 b1 = a1 + _choice2.GetPrice1();
  ....
}

Proyek FreeCAD, C ++
inline void insEdgeVec(std::map<int,std::set<int> > &map,
                       int n1, int n2)
{
  if(n1<n2)
    map[n2].insert(n1);
  else
    map[n2].insert(n1);
};

Terlepas dari kondisinya, tindakan yang sama dilakukan. Tampaknya kasus yang sederhana. Bagaimana Anda bisa menyalin garis dan tidak memperbaikinya? Bisa.

Proyek LibreOffice, C ++
class SVX_DLLPUBLIC SdrMarkView : public SdrSnapView
{
  ....
  const Point& GetRef1() const { return maRef1; }
  const Point& GetRef2() const { return maRef1; }
  ....
};

Kesalahan Salin-Tempel Klasik. Benar:
const Point& GetRef2() const { return maRef2; }

Proyek LibreOffice, C ++
bool CmpAttr(
  const SfxPoolItem& rItem1, const SfxPoolItem& rItem2)
{
  ....
  ::boost::optional<sal_uInt16> oNumOffset1 =
        static_cast<const SwFmtPageDesc&>(rItem1).GetNumOffset();
  ::boost::optional<sal_uInt16> oNumOffset2 =
        static_cast<const SwFmtPageDesc&>(rItem1).GetNumOffset();
  ....
}

Dan satu lagi kesalahan Copy-Paste klasik :). Di satu tempat 1 hingga 2 diperbaiki, dan di tempat lain mereka lupa.

Proyek LibreOffice, C ++
XMLTransformerOOoEventMap_Impl::XMLTransformerOOoEventMap_Impl(
        XMLTransformerEventMapEntry *pInit,
        XMLTransformerEventMapEntry *pInit2 )
{
  if( pInit )
    AddMap( pInit );
  if( pInit )
    AddMap( pInit2 );
}

Tidak ada kesalahan dalam mengganti 1 dengan 2, tetapi hanya tidak menambahkan 2 ke kondisi kedua.

Gambar 12


Anda mungkin sedikit lelah. Karena itu, saya mengusulkan untuk membuat teh atau kopi, dan kami akan terus berkenalan dengan dunia angka 0, 1 dan 2.

Proyek perangkat lunak Geant4, bahasa C ++
void G4VTwistSurface::GetBoundaryLimit(G4int areacode,
                                       G4double limit[]) const
{
  ....
  if (areacode & sC0Min1Max) {
     limit[0] = fAxisMin[0];
     limit[1] = fAxisMin[1];
  } else if (areacode & sC0Max1Min) {
     limit[0] = fAxisMax[0];
     limit[1] = fAxisMin[1];
  } else if (areacode & sC0Max1Max) {
     limit[0] = fAxisMax[0];
     limit[1] = fAxisMax[1];
  } else if (areacode & sC0Min1Max) {
     limit[0] = fAxisMin[0];
     limit[1] = fAxisMax[1];
  }
  ....
}

Saya harap Anda menerima saran dan beristirahat. Siap menemukan kesalahan dalam kode ini?

Selamat kepada pembaca yang telah melihat kesalahan. Kamu hebat!

Mereka yang terlalu malas untuk mencari, saya juga mengerti itu. Peninjauan kode semacam itu sangat membosankan dan ada keinginan untuk segera memeriksa sesuatu yang lebih menarik. Di sinilah analis statis banyak membantu karena mereka tidak lelah.

Kesalahannya adalah bahwa kedua cek ini sama:
if        (areacode & sC0Min1Max) {
} else if (areacode & sC0Min1Max) {

Jika Anda mempelajari kode, menjadi jelas bahwa pemeriksaan pertama salah. Benar:
if        (areacode & sC0Min1Min) {
} else if (areacode & sC0Max1Min) {
} else if (areacode & sC0Max1Max) {
} else if (areacode & sC0Min1Max) {

Proyek CryEngine V, C ++
bool
CompareRotation(const Quat& q1, const Quat& q2, float epsilon)
{
  return (fabs_tpl(q1.v.x - q2.v.x) <= epsilon)
      && (fabs_tpl(q1.v.y - q2.v.y) <= epsilon)
      && (fabs_tpl(q2.v.z - q2.v.z) <= epsilon) // <=
      && (fabs_tpl(q1.w - q2.w) <= epsilon);
}

Proyek TortoiseGit, C ++
void CGitStatusListCtrl::OnContextMenuList(....)
{
  ....
  if( (!this->m_Rev1.IsEmpty()) ||
      (!this->m_Rev1.IsEmpty()) )
  ....
}

Proyek perangkat lunak Geant4, bahasa C ++
G4double G4MesonAbsorption::
GetTimeToAbsorption(const G4KineticTrack& trk1,
                    const G4KineticTrack& trk2)
{
  ....
  if(( trk1.GetDefinition() == G4Neutron::Neutron() ||
       trk1.GetDefinition() == G4Neutron::Neutron() ) &&
       sqrtS>1.91*GeV && pi*distance>maxChargedCrossSection)
    return time;
  ....
}

Proyek MonoDevelop, C #
private bool MembersMatch(ISymbol member1, ISymbol member2)
{
  ....
  if (member1.DeclaredAccessibility !=
      member1.DeclaredAccessibility
   || member1.IsStatic != member1.IsStatic)
  {
    return false;
  }
  ....
}

Seperti yang Anda lihat, sementara fragmen kode tidak disertai penjelasan. Sebenarnya, tidak ada yang bisa dijelaskan di sini. Anda hanya bisa menghela nafas.

Proyek Dolphin Emulator, C ++
bool IRBuilder::maskedValueIsZero(InstLoc Op1, InstLoc Op2) const
{
  return (~ComputeKnownZeroBits(Op1) &
          ~ComputeKnownZeroBits(Op1)) == 0;
}

RunAsAdmin Explorer Shim Project, C ++
bool IsLuidsEqual(LUID luid1, LUID luid2)
{
  return (luid1.LowPart == luid2.LowPart) &&
         (luid2.HighPart == luid2.HighPart);
}

Proyek IT ++, bahasa C ++
Gold::Gold(const ivec &mseq1_connections,
           const ivec &mseq2_connections)
{
  ....
  it_assert(mseq1.get_length() == mseq1.get_length(),
            "Gold::Gold(): dimension mismatch");
}

Proyek QuantLib, C ++
Distribution ManipulateDistribution::convolve(
  const Distribution& d1, const Distribution& d2) {
  ....
  QL_REQUIRE (d1.xmin_ == 0.0 && d1.xmin_ == 0.0,
              "distributions offset larger than 0");
  ....
}

Proyek Samba, C ++
static bool samu_correct(struct samu *s1, struct samu *s2)
{
  ....
  } else if (s1_len != s1_len) {
    DEBUG(0, ("Password history not written correctly, "
              "lengths differ, want %d, got %d\n",
          s1_len, s2_len));
  ....
}

Proyek Mozilla Firefox, C ++
static PRBool IsZPositionLEQ(nsDisplayItem* aItem1,
                             nsDisplayItem* aItem2,
                             void* aClosure) {
  if (!aItem1->GetUnderlyingFrame()->Preserves3D() ||
      !aItem1->GetUnderlyingFrame()->Preserves3D()) {
    return IsContentLEQ(aItem1, aItem2, aClosure);
  }
  ....
}

Proyek Sistem Operasi Haiku, C ++
void trans_double_path::reset()
{
  m_src_vertices1.remove_all();
  m_src_vertices2.remove_all();
  m_kindex1 = 0.0;               // <=
  m_kindex1 = 0.0;               // <=
  m_status1 = initial;
  m_status2 = initial;
}

Proyek Qt, C ++

Ok, sekarang mari kita sedikit lebih rumit. Untuk bersenang-senang, coba temukan kesalahannya di sini:
static ShiftResult shift(....)
{
  ....
  qreal l = (orig->x1 - orig->x2)*(orig->x1 - orig->x2) +
            (orig->y1 - orig->y2)*(orig->y1 - orig->y1) *
            (orig->x3 - orig->x4)*(orig->x3 - orig->x4) +
            (orig->y3 - orig->y4)*(orig->y3 - orig->y4);
  ....
}

Sebuah gambar, agar tidak melihat jawabannya segera, dan berkesempatan berpikir.

Gambar 13


Itu benar, alih-alih orig-> y1 - orig-> y1 harus ditulis orig-> y1 - orig-> y2 .

Proyek Platform .NET Compiler, Bahasa C #
public void IndexerMemberRace()
{
  ....
  for (int i = 0; i < 20; i++)
  {
    ....
    if (i % 2 == 0)
    {
      thread1.Start();
      thread2.Start();
    }
    else
    {
      thread1.Start();
      thread2.Start();
    }
    ....
  }
  ....
}

Kasus yang menarik. Untuk tujuan pengujian, Anda perlu menjalankan utas dalam urutan yang berbeda. Namun, karena kesalahan ketik, utas selalu memulai dengan cara yang sama, sebagai akibatnya pengujian kurang dari yang seharusnya.

Benar:
if (i % 2 == 0)
{
  thread1.Start();
  thread2.Start();
}
else
{
  thread2.Start();
  thread1.Start();
}

Proyek Samba, bahasa C.
static int compare_procids(const void *p1, const void *p2)
{
  const struct server_id *i1 = (struct server_id *)p1;
  const struct server_id *i2 = (struct server_id *)p2;

  if (i1->pid < i2->pid) return -1;
  if (i2->pid > i2->pid) return 1;
  return 0;
}

Fungsi perbandingan tidak akan pernah mengembalikan 1, karena kondisi i2-> pid> i2-> pid tidak masuk akal.

Secara alami, ini adalah kesalahan ketik biasa, dan sebenarnya harus ditulis:
if (i1->pid > i2->pid) return 1;

Proyek ChakraCore, C ++

Kasus terakhir dalam bab ini. Hore!
bool Lowerer::GenerateFastBrSrEq(....,
                                 IR::RegOpnd * srcReg1,
                                 IR::RegOpnd * srcReg2,
                                 ....)
{
  ....
  else if (srcReg1 && (srcReg1->m_sym->m_isStrConst))
  ....
  else if (srcReg1 && (srcReg1->m_sym->m_isStrConst))
  ....
}


Kesalahan lainnya


Sekarang mari kita bicara tentang banyak pola kesalahan yang terkait dengan penggunaan angka 0, 1, 2.

Kesalahan ketik dalam kondisi di mana konstanta 0/1/2 secara eksplisit digunakan


Proyek ROOT, C ++
Int_t TProofMonSenderML::SendSummary(TList *recs, const char *id)
{
  ....
  if (fSummaryVrs == 0) {
    if ((dsn = recs->FindObject("dataset"))) recs->Remove(dsn);
  } else if (fSummaryVrs == 0) {
  ....
}

Sangat aneh membandingkan fSummaryVrs dengan 0. dua kali

.NET CoreCLR project, C #
void PutIA64Imm22(UINT64 * pBundle, UINT32 slot, INT32 imm22)
{
  if (slot == 0)             // <=
  {
    ....
  }
  else if (slot == 1)
  {
    ....
  }
  else if (slot == 0)        // <=
  {
    .... 
  }
  ....
}

Proyek FFmpeg, bahasa C.
static int imc_decode_block(....)
{
  ....
  if (stream_format_code & 0x1)
    imc_decode_level_coefficients_raw(....);
  else if (stream_format_code & 0x1)
    imc_read_level_coeffs_raw(....);
  ....
}


Zip / Nama


Sebelumnya kami mempertimbangkan kasus saat indeks atau nama salah. Dan inilah situasi di mana Anda tidak akan segera memberi tahu cara mengklasifikasikan kesalahan. Contoh ini dapat dikaitkan dengan satu dan bab lain. Karena itu, saya memutuskan untuk membawanya secara terpisah.

Proyek Perpustakaan Grafik 3D Mesa, C ++
bool
ir_algebraic_visitor::reassociate_constant(....)
{
  ....
  if (ir1->operands[0]->type->is_matrix() ||
      ir1->operands[0]->type->is_matrix() ||
      ir2->operands[1]->type->is_matrix() ||
      ir2->operands[1]->type->is_matrix())
   return false;
  ....
}

Kode ini dapat diperbaiki seperti ini:
if (ir1->operands[0]->type->is_matrix() ||
    ir1->operands[1]->type->is_matrix() ||
    ir2->operands[0]->type->is_matrix() ||
    ir2->operands[1]->type->is_matrix())

Dan Anda dapat memperbaikinya seperti ini:
if (ir1->operands[0]->type->is_matrix() ||
    ir2->operands[0]->type->is_matrix() ||
    ir1->operands[1]->type->is_matrix() ||
    ir2->operands[1]->type->is_matrix())


Ekstra 0


Terkadang 0 berlebihan dan berbahaya. Karena itu, jumlahnya dapat berubah menjadi oktal di mana tidak diperlukan. Atau merusak format string.

Kesalahan yang disebutkan tidak sesuai untuk artikel ini, tetapi saya pikir patut untuk disebutkan. Saya tidak akan memberikan kode dengan kesalahan ini di artikel, tetapi jika tertarik, Anda bisa melihatnya di sini:
  • V536 Perhatikan bahwa nilai konstan yang digunakan diwakili oleh bentuk oktal, contoh ;
  • V638 Terminal null hadir di dalam sebuah string. Karakter '\ 0xNN' ditemukan. Mungkin berarti: '\ xNN', contoh .


Lupa menulis +1


Proyek Sistem Operasi Haiku, C ++
int
UserlandFS::KernelEmu::new_path(const char *path, char **copy)
{
  ....
  // append a dot, if desired
  if (appendDot) {
    copiedPath[len] = '.';
    copiedPath[len] = '\0';
  }
  ....
}

Opsi yang benar:
copiedPath[len] = '.';
copiedPath[len + 1] = '\0';

Catatan. Situasi ketika mereka lupa menambahkan unit sama sekali tidak jarang. Saya ingat persis bahwa lebih dari sekali saya bertemu kasus-kasus seperti itu. Namun, ketika saya ingin mengetikkan contoh serupa untuk artikel tersebut, saya hanya menemukan contoh ini. Maaf saya tidak bisa membuat Anda takut dengan kesalahan ini lebih lanjut. Saya minta maaf.

Kesalahan Format (C #)


Paling sering, fungsi untuk membangun string beroperasi dengan sejumlah kecil argumen. Jadi ternyata kesalahan yang paling sering dikaitkan dengan penggunaan {0}, {1} atau {2}.

Proyek Azure PowerShell, C #
protected override void ProcessRecordInternal()
{
  ....
  if (this.ShouldProcess(this.Name,
    string.Format("Creating Log Alert Rule '{0}' in resource group {0}",
      this.Name, this.ResourceGroupName)))
  {
    ....
  }
  ....
}

Tertutup dan menulis {0} dua kali. Akibatnya, nama this.Name akan dimasukkan ke dalam string dua kali . Tetapi nama this.ResourceGroupName tidak akan masuk ke string yang dibuat.

Proyek Mono, C #
void ReadEntropy ()
{
  if (reader.IsEmptyElement)
    throw new XmlException (
      String.Format ("WS-Trust Entropy element is empty.{2}",
                      LineInfo ()));
  ....
}

Ini umumnya aneh. Anda harus memasukkan yang bukan. Kemungkinan besar, kode ini tidak berhasil melakukan refactoring dan ternyata rusak.

Proyek Xenko, C #
public string ToString(string format,
                                IFormatProvider formatProvider)
{
  if (format == null)
    return ToString(formatProvider);

  return string.Format(
                      formatProvider,
                      "Red:{1} Green:{2} Blue:{3}",
                      R.ToString(format, formatProvider),
                      G.ToString(format, formatProvider),
                      B.ToString(format, formatProvider));
}

Pemrogram lupa bahwa penomoran dimulai dengan {0}, bukan {1}. Kode yang benar adalah:
return string.Format(
                    formatProvider,
                    "Red:{0} Green:{1} Blue:{2}",
                    R.ToString(format, formatProvider),
                    G.ToString(format, formatProvider),
                    B.ToString(format, formatProvider));

Proyek Platform .NET Compiler, C #
private void DumpAttributes(Symbol s)
{
  ....
  Console.WriteLine("{0} {1} {2}", pa.ToString());
  ....
}

Argumen jelas tidak cukup.

Kesimpulan dan rekomendasi


Saya harus menunjukkan begitu banyak contoh untuk menunjukkan bahwa kesalahan ketik yang terkait dengan 0, 1, dan 2 pantas mendapatkan perhatian khusus.

Jika saya hanya mengatakan: "Mudah untuk membingungkan o1 dan o2", Anda akan setuju, tetapi tidak melampirkan padanya arti yang Anda lampirkan sekarang, setelah membaca atau setidaknya menggulir artikel.

Sekarang Anda diperingatkan, dan itu bagus. Diperingatkan lebih dulu. Sekarang Anda akan lebih memperhatikan ulasan kode dan memberi perhatian ekstra pada variabel, dengan nama yang akan Anda lihat 0, 1, 2.

Sulit untuk memberikan beberapa rekomendasi tentang bagaimana kode dirancang untuk menghindari kesalahan tersebut. Seperti yang telah Anda lihat, kesalahan ditemukan bahkan dalam kode sederhana seperti itu, di mana tidak ada masalah.

Oleh karena itu, saya tidak akan mendesak Anda untuk menghindari 0, 1, 2 dan memberikan nama variabel panjang. Jika alih-alih angka Anda mulai menulis Pertama / Kedua / Kiri / Kanan dan sebagainya, godaan untuk menyalin nama atau ekspresi akan lebih besar. Mungkin rekomendasi seperti itu pada akhirnya tidak akan mengurangi, tetapi menambah jumlah kesalahan.

Namun demikian, ketika Anda menulis banyak jenis kode yang sama, rekomendasi "desain kode tabular" masih relevan. Pemformatan tabular tidak menjamin tidak adanya kesalahan ketik, tetapi membuatnya lebih mudah dan lebih cepat untuk diperhatikan. Lihat bab N13 dalam mini-book " Pertanyaan Utama tentang Pemrograman, Refactoring, dan Semua Itu ."

Ada satu lagi kabar baik. Semua kesalahan yang dibahas dalam artikel ini ditemukan menggunakan penganalisis kode statis PVS-Studio .. Oleh karena itu, dengan memperkenalkan alat analisis statis ke dalam proses pengembangan, Anda dapat mengidentifikasi banyak kesalahan ketik pada tahap awal.

Terimakasih atas perhatiannya. Saya harap Anda tertarik dan takut. Saya berharap Anda memiliki kode yang andal dan mengurangi kesalahan dengan 0, 1, 2, sehingga Freddy tidak mendatangi Anda.



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Andrey Karpov. Nol, satu, dua, Freddy datang untukmu .

All Articles