[C 언어 학습 노트]: 보안

변수나 메서드를 수정하여 const불변임을 컴파일러에 알리는 것은 컴파일러가 코드를 최적화하는 데 도움이 되며 개발자가 함수에 부작용이 있는지 이해하는 데 도움이 됩니다. 또한 를 사용하면 const &컴파일러가 불필요한 데이터를 복사하는 것을 방지할 수 있습니다. ``const``[2]에 대한 John Carmack의 의견은 읽어볼 가치가 있습니다.

// Bad Idea
class MyClass
{
public:
  void do_something(int i);
  void do_something(std::string str);
};


// Good Idea
class MyClass
{
public:
  void do_something(const int i);
  void do_something(const std::string &str);
};

반환 유형에 대해 신중하게 생각하십시오.

  • Getters (멤버 변수 읽기 API)

    • 일반적인 상황에서는 반환 값을 통해 멤버 변수를 읽을 때, 반환 값을 사용 &하거나 const &반환하면 성능이 크게 향상될 수 있습니다.

    • 값으로 반환하는 것이 스레드 안전성에 더 도움이 되며, 반환된 값이 복사 및 사용을 위한 것이라면 성능 손실이 없습니다.

    • API 반환 값이 공변 반환 유형을 사용하는 경우 &또는*

  • 임시 및 로컬 값

    • 항상 값으로 반환됨

단순 유형을 전달하고 반환하기 위해 const 참조를 사용하지 마세요.

// Very Bad Idea
class MyClass
{
public:
  explicit MyClass(const int& t_int_value)
    : m_int_value(t_int_value)
  {
  }

  const int& get_int_value() const
  {
    return m_int_value;
  }

private:
  int m_int_value;
}

대신 단순 유형이 값으로 전달되고 반환됩니다. 전달된 값을 변경하지 않으려면 참조가 const아닌 으로 선언하세요 const.

// Good Idea
class MyClass
{
public:
  explicit MyClass(const int t_int_value)
    : m_int_value(t_int_value)
  {
  }

  int get_int_value() const
  {
    return m_int_value;
  }

private:
  int m_int_value;
}

왜 그럴까요? 참조로 전달하고 반환하면 포인터 작업이 발생하므로 값으로 전달하는 것은 프로세서 레지스터에서 처리되며 더 빠릅니다.

원시 메모리에 대한 액세스 방지

C++에서는 메모리 오류 및 누수의 위험 없이 원시 메모리 액세스, 할당 및 할당 해제를 올바르게 처리하기가 어렵습니다[3]. C++11은 이러한 문제를 방지하는 도구를 제공합니다.

// Bad Idea
MyClass *myobj = new MyClass;

// ...
delete myobj;


// Good Idea
auto myobj = std::make_unique<MyClass>(constructor_param1, constructor_param2); // C++14
auto myobj = std::unique_ptr<MyClass>(new MyClass(constructor_param1, constructor_param2)); // C++11
auto mybuffer = std::make_unique<char[]>(length); // C++14
auto mybuffer = std::unique_ptr<char[]>(new char[length]); // C++11

// or for reference counted objects
auto myobj = std::make_shared<MyClass>(); 

// ...
// myobj is automatically freed for you whenever it is no longer used.

C 스타일 배열 대신 std::arrayor 사용std::vector

두 방법 모두 객체의 연속적인 메모리 레이아웃을 보장하며 C 스타일 배열을 완전히 대체할 수 있고 대체해야 하며 원시 포인터를 사용하지 않는 많은 이유 중 하나입니다.

또한 배열을 저장하기 위해 ``std::shared_ptr```을 사용하지 마세요[4].

예외 사용

반환 값(예 boost::optional: )은 무시할 수 있으며 확인하지 않으면 충돌이나 메모리 오류가 발생할 수 있지만 예외는 무시할 수 없습니다. 반면에 예외는 포착되어 처리될 수 있습니다. 예외가 애플리케이션의 최고 수준으로 올라가서 포착되어 로그에 기록되고 애플리케이션이 자동으로 다시 시작될 수 있습니다.

C++ 디자이너 중 한 명인 Stroustrup은 다음 주제에 대해 이야기했습니다. 왜 예외를 사용해야 할까요?[5]

C 스타일 캐스트 대신 C++ 스타일 캐스트 사용

C 스타일 캐스트를 C++ 스타일 캐스트( static_cast<>, dynamic_cast<>, ...)로 대체하면 더 많은 컴파일러 검사가 가능하고 상당히 안전합니다.

// Bad Idea
double x = getX();
int i = (int) x;

// Not a Bad Idea
int i = static_cast<int>(x);

또한 C++ 유형 변환 스타일은 더 명확하고 검색하기 쉽습니다.

그러나 double유형을 int유형으로 변환해야 하는 경우 프로그램 논리를 리팩터링하는 것을 고려하십시오(예: 오버플로 및 언더플로에 대한 추가 검사). 3번 측정하고 0.9999999999981번 자르는 상황은 피하세요.

가변 함수를 정의하지 마세요

가변 함수는 가변 개수의 인수를 허용할 수 있으며 아마도 가장 유명한 예는 입니다 printf(). 이러한 기능을 정의하는 것이 가능하더라도 보안 위험이 있을 수 있습니다. 가변 함수의 사용은 유형에 안전하지 않으며 잘못된 입력 매개변수로 인해 프로그램이 정의되지 않은 동작으로 종료될 수 있습니다. 이 정의되지 않은 동작은 보안 문제를 일으킬 수 있습니다. C++1을 지원하는 컴파일러를 사용하는 경우 가변 템플릿을 사용할 수 있습니다.

추천

출처blog.csdn.net/Jiangziyadizi/article/details/129372662