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
, , OUT
IN_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 != false
return != 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
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk