Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Poniżej przedstawiono kilka sposobów, aby jak najlepiej wykorzystać język adnotacji kodu źródłowego (SAL) i uniknąć niektórych typowych problemów.
_In_
Jeśli funkcja ma zapisywać w elemecie , użyj polecenia _Inout_ zamiast _In_. Jest to istotne w przypadku automatycznej konwersji ze starszych makr do SAL. Przed SAL wielu programistów używa makr jako komentarzy — makr o nazwie IN, OUT, IN_OUTlub wariantów tych nazw. Mimo że zalecamy przekonwertowanie tych makr na SAL, zalecamy również zachowanie ostrożności podczas ich konwersji, ponieważ kod mógł ulec zmianie od czasu zapisania oryginalnego prototypu, a stare makro może już nie odzwierciedlać tego, co robi kod. Należy szczególnie uważać na OPTIONAL makro komentarza, ponieważ jest on często umieszczany niepoprawnie — na przykład po niewłaściwej stronie przecinka.
#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_
Jeśli obiekt wywołujący nie może przekazać wskaźnika o wartości null, użyj polecenia _In_ lub _Out_ zamiast _In_opt_ lub _Out_opt_. Dotyczy to nawet funkcji, która sprawdza jego parametry i zwraca błąd, jeśli NULL nie powinien być. Mimo że funkcja sprawdza swój parametr pod kątem nieoczekiwanych NULL i zwracanych w sposób prawidłowy jest dobrym rozwiązaniem w zakresie kodowania defensywnego, nie oznacza to, że adnotacja parametru może być typu opcjonalnego (_*Xxx*_opt_).
#include <sal.h>
// Incorrect
void Func1(_Out_opt_ int *p1)
{
*p = 1;
}
// Correct
void Func2(_Out_ int *p1)
{
*p = 1;
}
_Pre_defensive_ i _Post_defensive_
Jeśli funkcja jest wyświetlana na granicy zaufania, zalecamy użycie adnotacji _Pre_defensive_ . Modyfikator "defensywny" modyfikuje niektóre adnotacje, aby wskazać, że w momencie wywołania interfejs powinien być ściśle sprawdzany, ale w treści implementacji należy założyć, że mogą zostać przekazane nieprawidłowe parametry. W takim przypadku preferowana jest granica zaufania, aby wskazać, _In_ _Pre_defensive_ że chociaż obiekt wywołujący otrzymuje błąd, jeśli próbuje przekazać NULLwartość , treść funkcji jest analizowana tak, jakby parametr mógł mieć NULLwartość , a wszelkie próby wyłudzenia wskaźnika bez uprzedniego sprawdzenia, czy element jest NULL oflagowany. Adnotacja _Post_defensive_ jest również dostępna do użycia w wywołaniach zwrotnych, w których przyjmuje się, że zaufana strona jest obiektem wywołującym, a niezaufany kod jest nazywany kodem.
_Out_writes_
W poniższym przykładzie pokazano typowe nieprawidłowe użycie obiektu _Out_writes_.
#include <sal.h>
// Incorrect
void Func1(_Out_writes_(size) CHAR *pb,
DWORD size
);
Adnotacja _Out_writes_ oznacza, że masz bufor.
cb Ma przydzielone bajty z pierwszym bajtem zainicjowanym po zakończeniu. Ta adnotacja nie jest ściśle nieprawidłowa i warto wyrazić przydzielony rozmiar. Nie określa jednak, ile elementów inicjuje funkcja.
W następnym przykładzie przedstawiono trzy poprawne sposoby pełnego określenia dokładnego rozmiaru zainicjowanej części buforu.
#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
Korzystanie z _Out_ PSTR programu jest prawie zawsze błędne. Ta kombinacja jest interpretowana jako parametr wyjściowy wskazujący bufor znaków, a bufor jest zakończony wartością null.
#include <sal.h>
// Incorrect
void Func1(_Out_ PSTR pFileName, size_t n);
// Correct
void Func2(_Out_writes_(n) PSTR wszFileName, size_t n);
Adnotacja, podobna _In_ PCSTR do tego, jest powszechna i przydatna. Wskazuje on ciąg wejściowy, który ma zakończenie o wartości null, ponieważ warunek wstępny _In_ umożliwia rozpoznawanie ciągu zakończonego wartością null.
_In_ WCHAR* p
_In_ WCHAR* p mówi, że istnieje wskaźnik p wejściowy wskazujący jeden znak. Jednak w większości przypadków nie jest to specyfikacja, która jest przeznaczona. Zamiast tego, co jest prawdopodobnie zamierzone, jest specyfikacją tablicy zakończonej wartością null; w tym celu użyj polecenia _In_ PWSTR.
#include <sal.h>
// Incorrect
void Func1(_In_ WCHAR* wszFileName);
// Correct
void Func2(_In_ PWSTR wszFileName);
Brak prawidłowej specyfikacji zakończenia wartości null jest powszechny. Użyj odpowiedniej STR wersji, aby zastąpić typ, jak pokazano w poniższym przykładzie.
#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_
Jeśli parametr jest wskaźnikiem i chcesz wyrazić zakres wartości elementu wskazywanego przez wskaźnik, użyj _Deref_out_range_ zamiast _Out_range_. W poniższym przykładzie zakres *pcbFilled jest wyrażony, a nie 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) nie jest ściśle wymagany w przypadku niektórych narzędzi, ponieważ można go wywnioskować z _Out_writes_to_(cbSize,*pcbFilled)elementu , ale jest on pokazany tutaj pod kątem kompletności.
Niewłaściwy kontekst w _When_
Innym typowym błędem jest użycie oceny po stanie dla warunków wstępnych. W poniższym przykładzie _Requires_lock_held_ jest warunkiem wstępnym.
#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);
Wyrażenie return odwołuje się do wartości po stanie, która nie jest dostępna w stanie wstępnym.
TRUE w systemie _Success_
Jeśli funkcja powiedzie się, gdy wartość zwracana jest niezerowa, użyj return != 0 jako warunku powodzenia zamiast return == TRUE. Nonzero nie musi oznaczać równoważności rzeczywistej wartości, którą kompilator udostępnia dla TRUEelementu . Parametr to _Success_ wyrażenie, a następujące wyrażenia są oceniane jako równoważne: return != 0, return != false, return != FALSEi return bez parametrów ani porównań.
// 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
);
Zmienna referencyjna
W przypadku zmiennej referencyjnej poprzednia wersja SAL użyła dorozumianego wskaźnika jako elementu docelowego adnotacji i wymagała dodania __deref adnotacji do adnotacji dołączonych do zmiennej referencyjnej. Ta wersja używa samego obiektu i nie wymaga _Deref_elementu .
#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
);
Adnotacje dotyczące zwracanych wartości
W poniższym przykładzie przedstawiono typowy problem z adnotacjami wartości zwracanych.
#include <sal.h>
// Incorrect
_Out_opt_ void *MightReturnNullPtr1();
// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();
W tym przykładzie mówi, _Out_opt_ że wskaźnik może być NULL częścią warunku wstępnego. Nie można jednak zastosować warunków wstępnych do wartości zwracanej. W tym przypadku poprawną adnotacją jest _Ret_maybenull_.
Zobacz też
Używanie adnotacji SAL w celu zmniejszenia liczby wad kodu C/C++
Informacje o języku SAL
Dodawanie adnotacji do parametrów funkcji i zwracanych wartości
Dodawanie adnotacji do zachowania funkcji
Dodawanie adnotacji do struktur i klas
Dodawanie adnotacji do zachowania blokowania
Określanie, kiedy i gdzie ma zastosowanie adnotacja
Funkcje wewnętrzne