본문 바로가기
CS

[Linux/C++] main 함수에서 전달받는 argv 임의로 초기화 하는 방법

by Warehaus 2021. 11. 1.

오늘은 C++ 로 작성 된 프로그램을 gtest 로 테스트하다가 문제가 발생했어요

내용: 구성 된 gtest 를 수행 시 segmentation fault 가 발생
조치: argv 를 사용하는 테스트가 초기화되지 않은 argv 를 사용. 초기 값 설정이 필요

결론적으로 Test 용도로 argv 를 임의로 설정해야 했습니다.

실제 프로그램에서는 프로그램 이름으로  argv[0] 이 들어오기 때문에, 테스트에서만 문제가 발생하는 경우였고, argc 가 0 보다 큰지 정도만 체크해도 됐지만, fake argument를 같이 넣어서 테스트를 다시 작성했습니다.

 

원래 있던 코드는 아래처럼 작성되어 있었어요.

#include <iostream>

using namespace std;

int main( int argc, char *argv[] ) {

    cout << argc << endl;
    for ( int i = 0; i < argc; i++ ) {
        cout << argv[i] <<endl;
    }
    return 0;
}

argv 는 char *argv[] 또는 char **argv 로 main 함수에 전달되고 argc를 통해서 argv 갯수를 우리는 알 수 있어요

 

실행

$ ./a.out 
1
./a.out

앞서 말했 듯이, gtest 에서는 argc 나 argv 가 전달되지 않았어요.

그래서 위의 코드랑 조금은 다른 상황이라고 볼 수 있죠.

 

argc = 0, argv 는 nullptr 인 상황이었고, Segmentation fault가 났던 이유는 이런 argv 포인터를 참조했기 때문이었죠.

 

오류코드

using namespace std;

class MyCommand {

    char **myargv;

public:
    MyCommand ( char *argv[] ) {
        myargv = (char **)argv;
    }

    void doSomething() {
        // Access to myargv
    }
};

int main( int argc, char *argv[] ) {

    cout << argc << endl;
    for ( int i = 0; i < argc; i++ ) {
        cout << argv[i] <<endl;
    }

    MyCommand *mc = new MyCommand ( argv );

    mc->doSomething();

    MyCommand *err_mc = new MyCommand ( nullptr );

    err_mc->doSomething();

    return 0;
}

 

위의 예시 코드를 실제로 실행했을 때 사실 오류는 안나옵니다.

그런데  doSomething에서 argv에 접근을 하려고 하면 메모리 접근에 문제가 생기는거죠.

 

그래서 nullptr가 들어올 수 있는 가능성을 배제하기 위해 아래와 같이 코드를 수정 해 주었습니다.

 

수정코드

 19 int main( int argc, char *argv[] ) {
 20 
 21     cout << argc << endl;
 22     for ( int i = 0; i < argc; i++ ) {
 23         cout << argv[i] <<endl;
 24     }
 25 
 26     if ( argv > 0 ) {
 27         MyCommand *mc = new MyCommand ( argv );
 28         mc->doSomething();
 29         
 30         MyCommand *err_mc = new MyCommand ( nullptr );
 31         err_mc->doSomething();
 32     } else {
 33         char *fake_argv[] = { (char*)"test.sh", NULL };
 34         
 35         MyCommand *mc = new MyCommand ( fake_argv );
 36         mc->doSomething();
 37         
 38         MyCommand *err_mc = new MyCommand ( fake_argv );
 39         err_mc->doSomething();
 40     }   
 41     
 42     return 0;
 43 }

 

argv 마지막은 NULL로 초기화 해 줍니다.

fake_argv에 인자가 하나밖에 없으니 필요에 따라  argc 도 1 로 변경 해 줍니다.

 

사실 일반적으로 argc 또는 argv 를 변경해야 할 이유가 없긴 합니다.

저같은 경우 command line ( argv[0] ) 을 프로세스 추적을 목적으로 변경이 필요했기 때문에  특정 클래스에서 접근이 필요 했으며,

해당 클래스를 테스트하는 도중에 gtest 에서 argument가 넘어오지 않아 임의로 세팅한 경우였습니다.