Praktik terbaik dan contoh (SAL)

Berikut adalah beberapa cara untuk mendapatkan hasil maksimal dari Bahasa Anotasi Kode Sumber (SAL) dan menghindari beberapa masalah umum.

_In_

Jika fungsi seharusnya menulis ke elemen , gunakan _Inout_ alih-alih _In_. Ini relevan dalam kasus konversi otomatis dari makro yang lebih lama ke SAL. Sebelum SAL, banyak programmer menggunakan makro sebagai komentar—makro yang diberi nama IN, , OUTIN_OUT, atau varian nama ini. Meskipun kami menyarankan agar Anda mengonversi makro ini ke SAL, kami juga mendorong Anda untuk berhati-hati ketika Anda mengonversinya karena kode mungkin telah berubah sejak prototipe asli ditulis dan makro lama mungkin tidak lagi mencerminkan apa yang dilakukan kode. Berhati-hatilah dengan OPTIONAL makro komentar karena sering ditempatkan dengan tidak benar—misalnya, di sisi yang salah dari koma.

#include <sal.h>

// Incorrect
void Func1(_In_ int *p1)
{
    if (p1 == NULL)
        return;

    *p1 = 1;
}

// Correct
// _Out_opt_ because the function tolerates NULL as a valid argument, i.e.
// no error is returned. If the function didn't check p1 for NULL, then
// _Out_ would be the better choice
void Func2(_Out_opt_ PCHAR p1)
{
    if (p1 == NULL)
        return;

    *p1 = 1;
}

_opt_

Jika pemanggil tidak diizinkan untuk meneruskan penunjuk null, gunakan _In_ atau _Out_ alih-alih _In_opt_ atau _Out_opt_. Ini berlaku bahkan untuk fungsi yang memeriksa parameternya dan mengembalikan kesalahan jika NULL saat seharusnya tidak. Meskipun memiliki fungsi periksa parameternya untuk yang tidak terduga NULL dan kembali dengan anggun adalah praktik pengodean defensif yang baik, itu tidak berarti bahwa anotasi parameter dapat berjenis opsional (_*Xxx*_opt_).

#include <sal.h>

// Incorrect
void Func1(_Out_opt_ int *p1)
{
    *p = 1;
}

// Correct
void Func2(_Out_ int *p1)
{
    *p = 1;
}

_Pre_defensive_ dan _Post_defensive_

Jika fungsi muncul pada batas kepercayaan, kami sarankan Anda menggunakan _Pre_defensive_ anotasi. Pengubah "defensif" memodifikasi anotasi tertentu untuk menunjukkan bahwa, pada titik panggilan, antarmuka harus diperiksa secara ketat, tetapi dalam isi implementasi harus mengasumsikan bahwa parameter yang salah mungkin diteruskan. Dalam hal ini, _In_ _Pre_defensive_ lebih disukai pada batas kepercayaan untuk menunjukkan bahwa meskipun penelepon mendapatkan kesalahan jika mencoba untuk meneruskan NULL, isi fungsi dianalisis seolah-olah parameter mungkin NULL, dan setiap upaya untuk mendereferensikan penunjuk tanpa terlebih dahulu memeriksanya NULL ditandai. Anotasi _Post_defensive_ juga tersedia, untuk digunakan dalam panggilan balik di mana pihak tepercaya diasumsikan sebagai penelepon dan kode yang tidak tepercaya adalah kode yang disebut.

_Out_writes_

Contoh berikut menunjukkan penyalahgunaan _Out_writes_umum .

#include <sal.h>

// Incorrect
void Func1(_Out_writes_(size) CHAR *pb,
    DWORD size
);

Anotasi _Out_writes_ menandakan bahwa Anda memiliki buffer. Ini memiliki cb byte yang dialokasikan, dengan byte pertama diinisialisasi saat keluar. Anotasi ini tidak benar-benar salah dan sangat membantu untuk mengekspresikan ukuran yang dialokasikan. Namun, itu tidak memberi tahu berapa banyak elemen yang diinisialisasi fungsi.

Contoh berikutnya menunjukkan tiga cara yang benar untuk sepenuhnya menentukan ukuran yang tepat dari bagian buffer yang diinisialisasi.

#include <sal.h>

// Correct
void Func1(_Out_writes_to_(size, *pCount) CHAR *pb,
    DWORD size,
    PDWORD pCount
);

void Func2(_Out_writes_all_(size) CHAR *pb,
    DWORD size
);

void Func3(_Out_writes_(size) PSTR pb,
    DWORD size
);

_Out_ PSTR

Penggunaan _Out_ PSTR hampir selalu salah. Kombinasi ini ditafsirkan memiliki parameter output yang menunjuk ke buffer karakter dan buffer dihentikan null.

#include <sal.h>

// Incorrect
void Func1(_Out_ PSTR pFileName, size_t n);

// Correct
void Func2(_Out_writes_(n) PSTR wszFileName, size_t n);

Anotasi seperti _In_ PCSTR itu umum dan berguna. Ini menunjuk ke string input yang memiliki penghentian null karena prasyarat _In_ memungkinkan pengenalan string yang dihentikan null.

_In_ WCHAR* p

_In_ WCHAR* p mengatakan bahwa ada penunjuk input yang menunjuk p ke satu karakter. Namun, dalam kebanyakan kasus, ini mungkin bukan spesifikasi yang dimaksudkan. Sebaliknya, yang mungkin dimaksudkan adalah spesifikasi array yang dihentikan null; untuk melakukan itu, gunakan _In_ PWSTR.

#include <sal.h>

// Incorrect
void Func1(_In_ WCHAR* wszFileName);

// Correct
void Func2(_In_ PWSTR wszFileName);

Kehilangan spesifikasi penghentian null yang tepat adalah umum. Gunakan versi yang sesuai STR untuk mengganti jenis, seperti yang ditunjukkan dalam contoh berikut.

#include <sal.h>
#include <string.h>

// Incorrect
BOOL StrEquals1(_In_ PCHAR p1, _In_ PCHAR p2)
{
    return strcmp(p1, p2) == 0;
}

// Correct
BOOL StrEquals2(_In_ PSTR p1, _In_ PSTR p2)
{
    return strcmp(p1, p2) == 0;
}

_Out_range_

Jika parameter adalah penunjuk dan Anda ingin mengekspresikan rentang nilai elemen yang ditujukkan oleh penunjuk, gunakan _Deref_out_range_ alih-alih _Out_range_. Dalam contoh berikut, rentang *pcbFilled dinyatakan, bukan pcbFilled.

#include <sal.h>

// Incorrect
void Func1(
    _Out_writes_bytes_to_(cbSize, *pcbFilled) BYTE *pb,
    DWORD cbSize,
    _Out_range_(0, cbSize) DWORD *pcbFilled
);

// Correct
void Func2(
    _Out_writes_bytes_to_(cbSize, *pcbFilled) BYTE *pb,
    DWORD cbSize,
    _Deref_out_range_(0, cbSize) _Out_ DWORD *pcbFilled
);

_Deref_out_range_(0, cbSize) tidak benar-benar diperlukan untuk beberapa alat karena dapat disimpulkan dari _Out_writes_to_(cbSize,*pcbFilled), tetapi ditunjukkan di sini untuk kelengkapan.

Konteks yang salah dalam _When_

Kesalahan umum lainnya adalah menggunakan evaluasi pasca-status untuk prasyarat. Dalam contoh berikut, _Requires_lock_held_ adalah prasyarat.

#include <sal.h>

// Incorrect
_When_(return == 0, _Requires_lock_held_(p->cs))
int Func1(_In_ MyData *p, int flag);

// Correct
_When_(flag == 0, _Requires_lock_held_(p->cs))
int Func2(_In_ MyData *p, int flag);

Ekspresi return mengacu pada nilai pasca-status yang tidak tersedia dalam pra-status.

TRUE di _Success_

Jika fungsi berhasil ketika nilai pengembalian bukan nol, gunakan return != 0 sebagai kondisi keberhasilan alih-alih return == TRUE. Nonzero tidak selalu berarti kesetaraan dengan nilai aktual yang disediakan pengkompilasi untuk TRUE. Parameter ke _Success_ adalah ekspresi, dan ekspresi berikut dievaluasi sebagai setara: return != 0, , return != falsereturn != FALSE, dan return tanpa parameter atau perbandingan.

// Incorrect
_Success_(return == TRUE) _Acquires_lock_(*lpCriticalSection)
BOOL WINAPI TryEnterCriticalSection(
  _Inout_ LPCRITICAL_SECTION lpCriticalSection
);

// Correct
_Success_(return != 0) _Acquires_lock_(*lpCriticalSection)
BOOL WINAPI TryEnterCriticalSection(
  _Inout_ LPCRITICAL_SECTION lpCriticalSection
);

Variabel referensi

Untuk variabel referensi, versi SAL sebelumnya menggunakan pointer tersirat sebagai target anotasi dan memerlukan penambahan anotasi __deref ke anotasi yang dilampirkan ke variabel referensi. Versi ini menggunakan objek itu sendiri dan tidak memerlukan _Deref_.

#include <sal.h>

// Incorrect
void Func1(
    _Out_writes_bytes_all_(cbSize) BYTE *pb,
    _Deref_ _Out_range_(0, 2) _Out_ DWORD &cbSize
);

// Correct
void Func2(
    _Out_writes_bytes_all_(cbSize) BYTE *pb,
    _Out_range_(0, 2) _Out_ DWORD &cbSize
);

Anotasi pada nilai yang dikembalikan

Contoh berikut menunjukkan masalah umum dalam anotasi nilai pengembalian.

#include <sal.h>

// Incorrect
_Out_opt_ void *MightReturnNullPtr1();

// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();

Dalam contoh ini, _Out_opt_ mengatakan bahwa pointer mungkin NULL sebagai bagian dari prasyarat. Namun, prasyarat tidak dapat diterapkan ke nilai pengembalian. Dalam hal ini, anotasi yang benar adalah _Ret_maybenull_.

Baca juga

Menggunakan anotasi SAL untuk mengurangi cacat kode C/C++
Memahami SAL
Menganotasi parameter fungsi dan mengembalikan nilai
Menganotasi perilaku fungsi
Membuat anotasi struktur dan kelas
Menganotasi perilaku penguncian
Menentukan kapan dan di mana anotasi berlaku
Fungsi intrinsik