본문 바로가기

코드 기록

C++에서 문자열 배열 출력하기 (C 스타일 vs 현대적인 C++ 스타일)

Javascript를 메인 언어로 쓰다가, 로우 레벨 언어에 대한 욕심이 생겼다. 그래서 C++을 공부하기 시작했고, 배운 것들을 하나씩 기록해 보기로 했다.


Hello World 찍기부터 다른 느낌

처음 따라 한 예제는 역시 hello world.

std::cout << "hello world" << std::endl;

Javascript라면 단순히 console.log("hello world")로 끝날 일을, C++에서는 이렇게 길다.

이걸 줄이는 방법 중 하나가 namespace를 활용하는 것이다.

using namespace std; cout << "hello world";

하지만 책이나 강좌에서 보면, using namespace std;는 지양하라고 한다. 여러 라이브러리와 파일을 동시에 다루다 보면 함수 이름이 충돌할 수 있기 때문이다. 따라서 std::cout처럼 어디 소속인지 명시하는 습관이 중요하다.


첫 시도: char 배열과 함수

Javascript라면 배열을 바로 console.log에 넣을 수 있지만, C++은 다르다. 그래서 간단히 printer 함수를 만들어 보았다.

int printer(const char* arr[]) { 
	for (int i = 0; i < arr.size(); i++) { 
    	std::cout << arr[i] << std::endl; 
    } 
    return 0; 
}

하지만 이건 당연히 컴파일 에러.
왜냐하면 C 스타일 배열에는 크기 정보가 없기 때문이다.

 

C에서 배열 크기를 모르는 이유

C/C++에서 배열은 사실상 메모리 덩어리일 뿐이다.
배열 옆에 크기 정보를 두거나, 구조체로 감싸서 관리하는 방식은 성능과 단순성을 해칠 수 있기 때문에 설계 단계에서 제외되었다.

👉 그래서 size() 같은 메서드가 없다.
👉 해결 방법: 배열의 크기를 함께 전달한다.

int printer(const char* arr[], int size) {
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << std::endl;
    }
    return 0;
}

int main() {
    const char* arr[] = {"hello", "world"};
    printer(arr, 2);
}

이 방식은 가장 원초적이고 C에 가까운 방법이다.

 

C++스러운 방법: std::array vs std::vector

std::array

std::array를 쓰면 크기를 관리할 수 있다. 하지만 단점은 템플릿 인자로 배열 크기를 반드시 지정해야 한다는 것.

std::array<std::string, 2> arr = {"hello", "world"};

👉 크기가 고정되어야 할 때는 좋지만, 크기가 가변적인 상황에는 적합하지 않다.

 

std::vector + std::string

현대 C++에서는 보통 **std::vector<std::string>**을 쓴다.

int printer(std::vector<std::string> arr) {
    for (int i = 0; i < arr.size(); i++) {
        std::cout << arr[i] << std::endl;
    }
    return 0;
}

int main() {
    std::vector<std::string> vec = {"hello", "world"};
    printer(vec);
}

왜 string인가?

char* 대신 std::string을 쓰는 이유는 다음과 같다:

  • char* (C 스타일 문자열)
    • 문자열은 \0(널 문자)로 끝나는 문자 배열.
    • 가볍고 단순하지만, 크기를 직접 관리해야 하고 strcmp 같은 함수로 비교해야 한다.
    • 버퍼 오버플로우 위험 있음.
  • std::string
    • 내부적으로 동적 메모리 관리 → 길이 제한 없음.
    • +, ==, < 같은 연산자 지원 → 직관적.
    • .size(), .substr(), .find() 등 편의 함수 풍부.
    • 안전성이 높고, 실무에서 압도적으로 많이 쓰인다.

왜 vector인가?

  • 크기 가변: 런타임에 자동으로 늘어나거나 줄어든다.
  • 편의성: .size(), .at() 같은 메서드 제공.
  • STL과 호환: for_each, sort 같은 알고리즘과 쉽게 연동.
  • 현대 C++ 표준 코드: 실무에서 가장 흔히 쓰인다.

 

결론

  • C 스타일 배열은 성능 최적화가 필요한 곳에서 여전히 중요하다.
  • 하지만 현대적인 C++에서는 **std::vector <std::string>**을 쓰는 것이 훨씬 안전하고 직관적이다.

👉 따라서 문자열 배열을 출력할 때는 이렇게 쓰는 게 가장 깔끔하다:

void printer(const std::vector<std::string>& arr) {
    for (const auto& s : arr) {
        std::cout << s << std::endl;
    }
}

int main() {
    std::vector<std::string> vec = {"hello", "C++", "world"};
    printer(vec);
}

여기서 const std::vector<std::string>&를 쓰면 불필요한 복사를 방지할 수 있다.
또한 range-based for 문을 사용하면 코드가 더 현대적이고 간결해진다.

 

 

번외 이야기

내가 로우레벨 언어에 관심을 가지게 된 이유는 요즘 개발 트렌드 때문이다. 새로운 기술과 프레임워크가 끊임없이 등장하고, 그것을 따라가는 것만으로도 벅찬 환경 속에서 나는 고민했다.
“계속해서 빠르게 변하는 흐름을 따라가는 개발자가 될 것인가, 아니면 본질을 파고들어 더 근본적인 지식을 쌓을 것인가?”

생각해 보니 로우레벨을 익히는 것이야말로 변화에 뒤처지지 않는 길일 수 있다는 결론에 이르렀다. 새로 나온 기술이라고 해도 결국 본질에서 벗어나지 않으며, 대부분 물리학이나 수학적 원리를 바탕으로 한 알고리즘 개선일 뿐이다.

남이 만든 라이브러리나 프레임워크를 가져다 쓰는 수준이 아니라, 그것을 직접 구현할 수 있는 레벨의 엔지니어가 된다면 더 가치 있는 개발자가 될 수 있지 않을까. 그래서 나는 로우레벨 언어 공부를 시작했다.

728x90
반응형

'코드 기록' 카테고리의 다른 글

[flask-cors] 재설치  (0) 2024.08.24
개발자 도구 방지 코드 우회하기  (8) 2024.08.06
[React] useClickOutside hook  (1) 2024.06.07
[Pyqt5] QMainWindow 배경 이미지 설정하기  (0) 2024.04.14
Why & What (is) Spidergen ?  (0) 2024.03.30