2016-05-31 2 views
-1

I 그 포인터에 관련된 특정 조건에 따라서 enum 포인터를 허용하는 기능을 가지고 리턴 :함수가 가비지 포인터를 가져 오지 않도록하는 방법은 무엇입니까?

my_enum function(char* prt) 
{ 
    /* function body*/ 
    if (condition1) return enum1; 
    if (condition2) return enum2; 
    if (condition3) return enum3; 
    if (condition4) return enum4; 
    else return enum5; 
} 

I는 포인터를 받아 my_function를 호출하고, 얻어진 값에 반응하는 다른 기능을 갖는다 :

void another_function(char* ptr) 
{ 
my_enum result = function(ptr); 
if (result == MY_VALUE) std::cout<<"OK"<<endl; 
} 

Valgrind를 실행하여 메모리 누수를 확인하고 있습니다. 다음의 에러의 상기 코드 결과 사실

Conditional jump depends on an uninitialized variable.

, 함수 함수로 초기화되지 않은 포인터를 통과 할 수있다.

내 질문 :이 상황을 처리하는 가장 좋은 방법은 무엇입니까 (참조 대신 사용). 그 코드를 사용할 모든 사람이 함수에 전달할 포인터를 초기화 할 수 있는지 확인할 수 없습니다. 포인터가 일부 쓰레기를 가리키는 경우 내 함수를 확인할 수 없습니다 (나는 그것이 NULL 포인터인지 여부도 확인합니다).

이러한 오류를 무시해야합니까? 그들이 쓸모가 없다면, 왜 Valgrind는 그들에 대해 나에게 알리는 것을 괴롭히는가? 내가 할 수있는 일이 있어야 해.

+8

초기화 된 포인터가 맞습니까? valgrind 메시지는'result'가 초기화되지 않은 것처럼 읽 힙니다.'function'이 반환하지 않고 종료 될 때 발생할 수 있습니다. – delnan

+2

열거 형을 반환하는 함수는 여전히 열거 형을 초기화하지 않은 분기를 가질 수 있습니다. 우리는 valgrind 오류를 일으키는 코드를 보지 않고는 알 수 없습니다. – stefaanv

+0

* 함수 *가 반환하지 않고 종료하지 않을 것이라고 확신합니다. 전체 코드를 게시 할 수는 없지만 * if-else * 문이 있습니다. – user2738748

답변

1

얼마나 멀리 갈 의향이 있습니까? 누군가 코드를 깨뜨리기를 원한다면, 당신은 그것을 도울 수 없습니다.

더 효과적인 보호를 적용할수록 더 어려워집니다.

간단한 것은 NULL을 확인하는 것입니다. 그것은 멍청한 포인터를 막지는 못하지만 의식적으로 무효화 된 포인터는 막습니다. 대부분의 사람들은 그것에 만족합니다.

그런 다음 포인터에 래퍼 클래스를 제공 할 수 있습니다.이 클래스를 인스턴스화하려면 유효한 객체가 필요합니다 (또는 의도적으로 발을 쏘는 것에 해당하는 유효하지 않은 포인터를주기 위해 희망이없는 점프). 따라서 초기화되지 않은 포인터의 시나리오는 발생할 수 없습니다. 그러나 객체는 해당 객체가 존재하기 전에 중단 될 수 있습니다. 포인터가 사용됩니다.

그런 다음이 오브젝트 및 해당 포인터에 대한 팩토리/관리자 클래스를 유지 보수 할 수 있습니다. 포인터 대상 개체가 만들어 지거나 파괴 될 때마다 해당 포인터가 만들어 지거나 무효화됩니다. 코드가 멀티 스레딩이고 기능이 이미 검사를 통과하고 유효성이 검증 된 값을 사용하기 전에 파괴가 발생할 수있는 경우가 아니라면 오류가 없습니다.

그러면 mutex에서 함수와 관리자를 모두 래핑하는 스레드 안전성을 추가 할 수 있습니다. 교착 상태 및 동기화와 관련된 모든 종류의 두통이 추가됩니다. 그러나 사용자는 안전 기능을 무시하는 사용자로부터 파생 된 클래스 (아마도 #define private public 일 수 있습니다)를 만드는 데 정말로 열심히 노력해야합니다 ...

각 단계에서 오버 헤드는 효과가 실제로 노력을 기울일 가치가없는 수준까지 올라갑니다. . 그래서 NULL에 대한 포인터를 확인하고 다른 사람에 대해 걱정하지 마라.

0

기본적으로 두 가지 해결책이 있습니다.

  1. 유효한 포인터가 있어야하며 API 설명서에 명확하게 명시되어 있어야합니다. 그러면 잘못된 사용으로 인해 UB가 발생하지만, 귀하의 잘못이 아닙니다. 그러나 원시 포인터를 처리하는 것은 C 스타일이며 C++ 프로그래머가 싫어합니다.

  2. 테이크 (행 참조) 항상 현명 초기화된다 캡슐화 포인터 타입, 예컨대 (const char* 대신의) std::string, std::unique_ptr 또는 std::shared_ptr. 예를 들어,

    my_enum function(std::string const&str) 
    { 
        /* function body*/ 
        if (str.empty()) // deal with improper input 
        std::cerr<<"warning: empty string in function()"<<std::endl; 
        if (condition1) return enum1; 
        if (condition2) return enum2; 
        if (condition3) return enum3; 
        if (condition4) return enum4; 
        else return enum5; 
    } 
    

    또는

    my_enum function(std::unique_ptr<SomeType> const&ptr) 
    { 
        /* function body*/ 
        if (!ptr) { // deal with improper input 
        std::cerr<<"warning: invalid pointer in function()"<<std::endl; 
        return enum_error; 
        } 
        if (condition1) return enum1; 
        if (condition2) return enum2; 
        if (condition3) return enum3; 
        if (condition4) return enum4; 
        else return enum5; 
    } 
    

    이 원시 포인터를 방지하고 상황이 이런 종류의 처리를위한 C++ 방법입니다. 후자의 코드에서 한 가지 문제는 인수가 unique_ptr 인 경우에만 작동한다는 것입니다. SFINAE 또는 다른 방법을 사용하여 오브젝트와 같은 자동 포인터 (예 : 오브젝트 obj, 오브젝트 obj::get() constconst obj::element_type* 인 오브젝트로 정의 됨)를 가져 오도록 (오버로드 된) 일반화 할 수 있습니다.

+0

'std :: unique_ptr'는 매개 변수의 수명과 관리가이 함수의 비즈니스가 아니기 때문에 여기서 아무것도하지 않습니다. – Quentin

+0

@Quentin'unique_ptr'에 대한 const 참조를 취하는 것은 (객체의 수명과 관리를 포함하여) 객체에 대한 간섭을 허용하지 않고 const 액세스 만 허용합니다. – Walter

+0

사실, 호출자가 아무 이유없이'std :: unique_ptr'을 사용하도록 강요합니다. 그가'std :: shared_ptr','boost :: scoped_ptr', 또는 단순히 자동 수명을 가진 객체를 사용한다면 어떨까요? – Quentin

1

누군가가 나쁜 (예 : 초기화되지 않은, 매달려있는) 포인터를 전혀 전달하지 못하게 할 수 없으므로 의견이 "최상의"방법에 따라 다릅니다.

일반적인 해결책은 원시 포인터를 모두 피하고 포인터를 전혀 허용하지 않는 방식으로 함수를 작성하는 것입니다.

한 가지 방법은 참조를 수락하는 것입니다. 원시 포인터를 전혀 사용하지 않도록 코드를 작성하면 나쁜 매개 변수로 함수를 호출하기가 더 어려워집니다. 제한 사항은 호출자가 잘못된 참조를 생성 할 수 있지만 (예 : 잘못된 포인터를 역 참조하는 방식으로) 나쁜 참조를 함수에 전달하는 것보다 많은 노력 (또는 실수로 길게 만든 일련의 실수)이 전달되는 것입니다. 나쁜 포인터.

또 다른 방법은 포인터를 보관할 값 (또는 경우에 따라 참조)으로 일부 클래스 개체를 허용하는 것입니다. 그런 다음 모두 구성원 함수를 구현하여 잘못된 포인터를 보관하는 상황을 방지합니다. 그 클래스에 포인터를 받아들이는 멤버 함수를주지 마라. 생성자와 다른 멤버 함수가 일관성을 유지하는지 확인하십시오 (공식적으로 생성자는 엄격한 불변 값 세트를 만들고 다른 멤버 함수는 불변 값 세트를 유지합니다). 여기에는 잘못된 데이터를 사용하여 객체를 만들려고 시도하는 경우 예외를 throw하는 것과 같은 기법이 포함됩니다 (객체를 생성하는 프로세스에서 예외가 발생하고 해당 객체가 존재하지 않으며 함수에 어떤 방식으로도 전달 될 수없는 경우). 결과적으로 함수는 성공적으로 호출 된 경우 수신하는 데이터가 유효하다고 간주 할 수 있습니다.

위의 내용은 실수로 잘못된 데이터를 함수에 전달하는 것을 어렵게 만듭니다. 어떤 기술도 모든 안전 장치를 우회하고 잘못된 데이터를 사용자의 기능에 전달할 수있는 방법을 찾기에 충분하다고 판단 된 사람을 (천재 또는 어리 석음을 통해) 절대적으로 막을 수는 없습니다.