Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tady je několik způsobů, jak získat maximum z jazyka SAL (Source Code Annotation Language) a vyhnout se některým běžným problémům.
_In_
Pokud má funkce psát do elementu, použijte _Inout_ místo _In_. To je relevantní v případech automatizovaného převodu ze starších maker na sal. Před sal, mnoho programátorů použilo makra jako komentáře – makra s názvem IN, OUT, IN_OUTnebo varianty těchto názvů. I když doporučujeme převést tato makra na SAL, doporučujeme, abyste byli při převodu opatrní, protože kód se mohl od napsání původního prototypu změnit a staré makro už nemusí odrážet, co kód dělá. Dávejte pozor hlavně na OPTIONAL makro komentáře, protože se často nesprávně umísťuje – například na špatnou stranu čárky.
#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_
Pokud volajícímu není povoleno předávat ukazatel null, použijte _In_ nebo _Out_ místo _In_opt_ nebo _Out_opt_. To platí i pro funkci, která kontroluje její parametry a vrací chybu, pokud je NULL tam, kde by neměla být. I když má funkce kontrolu jeho parametru neočekávaný NULL a bezproblémový návrat je dobrým obranném programováním, neznamená to, že poznámka k parametru může být volitelného typu (_*Xxx*_opt_).
#include <sal.h>
// Incorrect
void Func1(_Out_opt_ int *p1)
{
*p = 1;
}
// Correct
void Func2(_Out_ int *p1)
{
*p = 1;
}
_Pre_defensive_ a _Post_defensive_
Pokud se funkce zobrazí na hranici důvěryhodnosti, doporučujeme použít poznámku _Pre_defensive_ . Modifikátor "obranného" upravuje určité poznámky tak, aby značily, že v okamžiku volání by mělo být rozhraní kontrolováno přísně, ale v těle implementace by se mělo předpokládat, že mohou být předány nesprávné parametry. V takovém případě je upřednostňovaná hranice důvěryhodnosti, která označuje, _In_ _Pre_defensive_ že i když volající obdrží chybu, pokud se pokusí předat NULL, tělo funkce je analyzováno, jako by parametr mohl být NULL, a všechny pokusy o dereference ukazatele bez první kontroly NULL , že jsou označeny příznakem. K _Post_defensive_ dispozici je také poznámka, která se používá v zpětných voláních, kde se předpokládá, že důvěryhodná strana je volajícím a nedůvěryhodný kód je volaný kód.
_Out_writes_
Následující příklad ukazuje běžné zneužití _Out_writes_.
#include <sal.h>
// Incorrect
void Func1(_Out_writes_(size) CHAR *pb,
DWORD size
);
Poznámka _Out_writes_ označuje, že máte vyrovnávací paměť.
cb Má přidělené bajty s prvním bajtem inicializovaným při ukončení. Tato poznámka není přísně špatná a je užitečné vyjádřit přidělenou velikost. Neřekne ale, kolik prvků funkce inicializuje.
Následující příklad ukazuje tři správné způsoby, jak plně určit přesnou velikost inicializované části vyrovnávací paměti.
#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
Použití _Out_ PSTR je téměř vždy špatné. Tato kombinace se interpretuje jako výstupní parametr, který odkazuje na vyrovnávací paměť znaků a vyrovnávací paměť je ukončena hodnotou null.
#include <sal.h>
// Incorrect
void Func1(_Out_ PSTR pFileName, size_t n);
// Correct
void Func2(_Out_writes_(n) PSTR wszFileName, size_t n);
Anotace je _In_ PCSTR běžná a užitečná. Odkazuje na vstupní řetězec, který má ukončení hodnoty null, protože předběžná podmínka _In_ umožňuje rozpoznat řetězec ukončený hodnotou null.
_In_ WCHAR* p
_In_ WCHAR* p říká, že je vstupní ukazatel p , který odkazuje na jeden znak. Ve většině případů to ale pravděpodobně není specifikace, která je určena. Místo toho je pravděpodobně určena specifikace pole s ukončenou hodnotou null; k tomu použijte _In_ PWSTR.
#include <sal.h>
// Incorrect
void Func1(_In_ WCHAR* wszFileName);
// Correct
void Func2(_In_ PWSTR wszFileName);
Chybí správná specifikace ukončení hodnoty null. K nahrazení typu použijte odpovídající STR verzi, jak je znázorněno v následujícím příkladu.
#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_
Pokud je parametr ukazatelem a chcete vyjádřit rozsah hodnoty prvku, na který odkazuje ukazatel, použijte _Deref_out_range_ místo _Out_range_. V následujícím příkladu je rozsah *pcbFilled vyjádřen, nikoli 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) není pro některé nástroje nezbytně nutné, protože ho lze odvodit z _Out_writes_to_(cbSize,*pcbFilled), ale je zde uvedena pro úplnost.
Nesprávný kontext v _When_
Další běžnou chybou je použít závěrečné vyhodnocení předpokladů. V následujícím příkladu _Requires_lock_held_ je předběžná podmínka.
#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);
Výraz return odkazuje na hodnotu po stavu, která není k dispozici v představovém stavu.
TRUE v _Success_
Pokud je funkce úspěšná, pokud je návratová hodnota nenulová, použijte return != 0 jako podmínku úspěchu místo return == TRUE. Nenulová neznamená nutně ekvivalenci se skutečnou hodnotou, kterou kompilátor poskytuje TRUE. Parametr, který _Success_ má být výrazem, a následující výrazy jsou vyhodnoceny jako ekvivalentní: return != 0, return != false, return != FALSEa return bez parametrů nebo porovnání.
// 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
);
Referenční proměnná
U referenční proměnné použila předchozí verze SAL implicitní ukazatel jako cíl poznámky a vyžadovala přidání __deref poznámek připojených k referenční proměnné. Tato verze používá samotný objekt a nevyžaduje _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
);
Poznámky k vrácených hodnotám
Následující příklad ukazuje běžný problém s návratovou hodnotou poznámek.
#include <sal.h>
// Incorrect
_Out_opt_ void *MightReturnNullPtr1();
// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();
V tomto příkladu se říká, _Out_opt_ že ukazatel může být NULL součástí předběžné podmínky. Předběžné podmínky však nelze použít na vrácenou hodnotu. V tomto případě je _Ret_maybenull_správná poznámka .
Viz také
Použití poznámek SAL ke snížení vad kódu C/C++
Porozumění SAL
Přidávání poznámek k parametrům funkce a vráceným hodnotám
Přidávání poznámek k chování funkce
Přidávání poznámek ke strukturám a třídám
Přidávání poznámek k chování uzamčení
Určení, kdy a kde se má anotace použít
Vnitřní funkce