ES.42: Keep use of pointers simple and straightforward
ES.42: 使用指針時要簡單且直接
Reason(原因)
Complicated pointer manipulation is a major source of errors.
複雜的指針操作是錯誤的主要來源之一。
Note(注意)
Use gsl::span instead. Pointers should only refer to single objects. Pointer arithmetic is fragile and easy to get wrong, the source of many, many bad bugs and security violations.span is a bounds-checked, safe type for accessing arrays of data. Access into an array with known bounds using a constant as a subscript can be validated by the compiler.
使用gls::span。指針只應該用於參照單獨的對象。指針運算脆弱且易錯,會導致特別特別多的錯誤和安全違反。span類型提供了具有邊界檢查的、安全的訪問數組數據的手段。使用常數下標訪問一個已知邊界的數組的操作可以在編譯時檢查。
Example, bad(反面示例)
void f(int* p, int count)
{
if (count < 2) return;
int* q = p + 1; // BAD
ptrdiff_t d;
int n;
d = (p - &n); // OK
d = (q - p); // OK
int n = *p++; // BAD
if (count < 6) return;
p[4] = 1; // BAD
p[count - 1] = 2; // BAD
use(&p[0], 3); // BAD
}
Example, good(範例)
void f(span<int> a) // BETTER: use span in the function declaration
{
if (a.size() < 2) return;
int n = a[0]; // OK
span<int> q = a.subspan(1); // OK
if (a.size() < 6) return;
a[4] = 1; // OK
a[a.size() - 1] = 2; // OK
use(a.data(), 3); // OK
}
Note(注意)
Subscripting with a variable is difficult for both tools and humans to validate as safe.span is a run-time bounds-checked, safe type for accessing arrays of data.at() is another alternative that ensures single accesses are bounds-checked. If iterators are needed to access an array, use the iterators from a span constructed over the array.
使用變量下標的情況下確保安全性無論對工具還是人都很困難。span是訪問數組數據的安全類型,可以提供執行時的範圍檢查。at()是確保單獨訪問時進行邊界檢查的另一種方式。如果迭代器需要訪問數組,使用來自構建在數組之上的span的迭代器。
Example, bad(反面示例)
void f(array<int, 10> a, int pos)
{
a[pos / 2] = 1; // BAD
a[pos - 1] = 2; // BAD
a[-1] = 3; // BAD (but easily caught by tools) -- no replacement, just don't do this
a[10] = 4; // BAD (but easily caught by tools) -- no replacement, just don't do this
}
Example, good(範例)
Use a span:
使用span:
void f1(span<int, 10> a, int pos) // A1: Change parameter type to use span
{
a[pos / 2] = 1; // OK
a[pos - 1] = 2; // OK
}
void f2(array<int, 10> arr, int pos) // A2: Add local span and use that
{
span<int> a = {arr.data(), pos};
a[pos / 2] = 1; // OK
a[pos - 1] = 2; // OK
}
Use at():
使用at():
void f3(array<int, 10> a, int pos) // ALTERNATIVE B: Use at() for access
{
at(a, pos / 2) = 1; // OK
at(a, pos - 1) = 2; // OK
}
Example, bad(反面示例)
void f()
{
int arr[COUNT];
for (int i = 0; i < COUNT; ++i)
arr[i] = i; // BAD, cannot use non-constant indexer
}
Example, good(範例)
Use a span:
使用span:
void f1()
{
int arr[COUNT];
span<int> av = arr;
for (int i = 0; i < COUNT; ++i)
av[i] = i;
}
Use a span and range-for:
使用span和範圍for:
void f1a()
{
int arr[COUNT];
span<int, COUNT> av = arr;
int i = 0;
for (auto& e : av)
e = i++;
}
Use at() for access:
使用at()訪問:
void f2()
{
int arr[COUNT];
for (int i = 0; i < COUNT; ++i)
at(arr, i) = i;
}
Use a range-for:
使用範圍for:
void f3()
{
int arr[COUNT];
int i = 0;
for (auto& e : arr)
e = i++;
}
Note(注意)
Tooling can offer rewrites of array accesses that involve dynamic index expressions to use at() instead:
工具可以建議重寫包含動態索引運算的數組訪問代碼,轉而使用at()。
static int a[10];
void f(int i, int j)
{
a[i + j] = 12; // BAD, could be rewritten as ...
at(a, i + j) = 12; // OK -- bounds-checked
}
Example(示例)
Turning an array into a pointer (as the language does essentially always) removes opportunities for checking, so avoid it。
將數組轉換爲指針(像語言一直在做的那樣)放棄了檢查的機會,應該避免。
void g(int* p);
void f()
{
int a[5];
g(a); // BAD: are we trying to pass an array?
g(&a[0]); // OK: passing one object
}
If you want to pass an array, say so:
如果想傳遞一個數組,這樣做:
void g(int* p, size_t length); // old (dangerous) code
void g1(span<int> av); // BETTER: get g() changed.
void f2()
{
int a[5];
span<int> av = a;
g(av.data(), av.size()); // OK, if you have no choice
g1(a); // OK -- no decay here, instead use implicit span ctor
}
Enforcement(實施建議)
-
Flag any arithmetic operation on an expression of pointer type that results in a value of pointer type.
-
標記對指針表達式進行數學運算然後得到指針類型的結果的情況。
-
Flag any indexing expression on an expression or variable of array type (either static array or std::array) where the indexer is not a compile-time constant expression with a value between 0 and the upper bound of the array.
-
如果一個索引不是編譯時可確定其值區間爲0到數組上限的常量表達式,對數組類型變量或表達式的索引表達式的風險進行提示。
-
Flag any expression that would rely on implicit conversion of an array type to a pointer type.
-
提示表達式依靠從數組到指針的隱式類型轉換,提示。
This rule is part of the bounds-safety profile.
本規則是邊界安全規則羣組的一部分。
原文鏈接
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es42-keep-use-of-pointers-simple-and-straightforward
覺得本文有幫助?歡迎點贊並分享給更多的人。
閱讀更多更新文章,請關注微信公衆號【面向對象思考】