C/C++ 동작의 세 가지 분류C와 C++ 표준은 프로그램의 동작을 미정의 동작(Undefined Behavior), 구현 정의 동작(Implementation-defined Behavior), 미지정 동작(Unspecified Behavior)이라는 세 가지 범주로 나누어 규정합니다. 이 세 가지는 비슷해 보이지만 컴파일러와 개발자에게 부여되는 책임과 보장 수준이 완전히 다릅니다.1. 미정의 동작 (Undefined Behavior)미정의 동작은 말 그대로 표준에서 어떠한 동작도 정의하지 않는 경우입니다. 언어 문법적으로는 유효한
핵심 원리: 분기 예측(Branch Prediction)C++에서 정렬된 배열을 처리하는 것이 정렬되지 않은 배열보다 빠른 이유는 바로 분기 예측(branch prediction) 때문입니다. 컴퓨터 아키텍처에서 분기 예측이란 프로그램 명령어 흐름에 나타나는 조건 분기(점프)가 실행될지 말지를 CPU가 미리 추측하는 기술을 의미합니다.현대 CPU는 파이프라인 방식으로 동작하기 때문에 다음에 실행할 명령어를 미리 예측해 불러와야 최고 성능을 낼 수 있습니다. 예측이 맞으면 파이프라인이 끊기지 않고 계속 작동하지만, 예측이 틀리면 이미
배열(Array)이란 무엇인가?배열(array)은 동일한 자료형의 요소들이 연속된 메모리 위치에 나란히 저장되어 있으며, 고유한 식별자(배열 이름)에 인덱스를 더해 각 요소에 개별적으로 접근할 수 있는 자료구조입니다.C++에서 배열을 사용하려면 먼저 배열을 선언해야 합니다. 예를 들어 다음과 같습니다.int arr[10];위 코드는 크기가 10인 int 타입의 배열을 선언하는 문장입니다. 이 배열은 연속된 메모리 공간에 정수 10개를 저장할 수 있습니다.배열 요소에 접근하는 방법배열의 특정 요소에 접근하려면 배열 접근 연산자([])
C++ 참조(Reference)란?변수를 참조(reference)로 선언하면 해당 변수는 기존 변수의 또 다른 이름, 즉 별칭(alias)이 됩니다. 참조는 새로운 메모리 공간을 만드는 것이 아니라 이미 존재하는 변수를 그대로 가리키며, 참조를 통해 값을 읽거나 수정하면 원본 변수에 그대로 반영됩니다.문법Type &newname = existing_name;선언 및 초기화 예제int num = 10;int &ref = num; // ref는 num의 별칭참조는 선언과 동시에 반드시 초기화해야 하며, 한 번 특정 변
복사 생략(Copy Elision)은 대부분의 컴파일러가 구현하고 있는 최적화 기법으로, 특정 상황에서 불필요한(그리고 잠재적으로 비용이 큰) 객체 복사를 방지하는 역할을 합니다.예를 들어, 생성된 객체가 실제로 사용되지 않거나 부작용(side effect)이 없는 코드가 있다면, 컴파일러는 해당 객체를 아예 생성하지 않고 건너뛸 수 있습니다.예제 코드struct MyStruct { MyStruct() {} MyStruct(const MyStruct&) { std::cout << Cop
C/C++에서 구조체(struct)의 sizeof 값이 각 멤버의 sizeof 합계와 일치하지 않는 이유는 바이트 패딩(byte padding)과 정렬(alignment) 때문입니다.패딩과 정렬이란 무엇인가?C/C++의 모든 데이터 타입에는 고유한 정렬(alignment) 요구 사항이 존재합니다. 프로세서는 자신의 아키텍처에 맞는 워드(word) 길이 단위로 데이터를 처리하는데, 32비트 머신에서는 처리 단위가 4바이트(32비트)입니다. 예를 들어 다음과 같은 구조체가 있다고 가정해 보겠습니다.예제 코드#include <ios
C와 C++에서 배열(array)과 포인터(pointer)는 매우 유사하게 동작합니다. 하지만 두 개념 사이에는 미묘하면서도 중요한 차이점이 존재합니다. 대표적인 예가 바로 sizeof 연산자의 동작 방식입니다.배열과 포인터의 sizeof 차이배열 이름을 포인터로 변환(전달)하는 순간, 원래 배열의 크기 정보를 잃어버리게 됩니다. 다음 예제를 통해 이를 확인할 수 있습니다.예제 코드#include<iostream> int main() { const int a[] = { 2, 3, 5, 7, 11 }; co
전방 선언이란?전방 선언(Forward Declaration)은 선언 이후에 등장하는 코드에게 특정 이름의 클래스가 존재한다는 사실을 미리 알려주는 기능입니다. 이를 통해 컴파일러는 해당 이름이 사용되는 것을 보고도 오류 없이 컴파일을 진행할 수 있으며, 클래스의 실제 정의는 나중에 링커(Linker)가 찾아 연결하게 됩니다.예제class Person; void myFunc(Person p1) { // ... } class Person { // 여기에 클래스 정의 작성 };위 예제에서 컴파일러는 myFunc 함수를
스마트 포인터(Smart Pointer)란?스마트 포인터는 C++의 raw 포인터(또는 bare 포인터)를 감싸는 래퍼(wrapper) 클래스입니다. 단순히 메모리 주소만 저장하는 일반 포인터와 달리, 스마트 포인터는 자신이 가리키는 리소스(주로 힙 메모리)의 생명주기를 관리합니다.예를 들어, 프로그래머가 delete 호출을 잊어버리거나 예외 발생으로 인해 해당 메모리 위치에 대한 참조가 사라지는 상황을 생각해 볼 수 있습니다. 스마트 포인터는 이런 경우에도 자동으로 메모리를 해제해 주기 때문에, 마치 가비지 컬렉터(garbage
프로그래밍 언어의 공식 표준 문서는 해당 언어의 문법과 동작을 정의하는 가장 권위 있는 자료입니다. 그렇다면 최신 C와 C++ 표준 문서는 어디에서 찾을 수 있을까요? 아래에서 공식적으로 열람하거나 구매할 수 있는 방법을 정리했습니다. 1. C 표준 문서 — ANSI 웹 스토어 현재 사용 중인 C 표준(ISO/IEC 9899) 문서는 미국 국립표준협회(ANSI)의 공식 웹 스토어에서 구매할 수 있습니다. ANSI 웹 스토어 링크: https://webstore.ansi.org/RecordDetail.aspx?sku=INCITS%
C++는 C 스타일 캐스트 대신 목적에 따라 네 가지 명시적 캐스트 연산자를 제공합니다. 각 연산자는 서로 다른 상황에서 사용되도록 설계되었으며, 적절한 캐스트를 선택하면 코드의 의도가 명확해지고 컴파일러가 오류를 더 잘 잡아낼 수 있습니다.const_cast — const 속성의 추가 및 제거const_cast는 변수에서 const 또는 volatile 속성을 제거하거나 추가할 때 사용하는 유일한 캐스트입니다. 예를 들어, const로 선언된 객체를 받아야 하는 레거시 API에 non-const 포인터를 전달해야 하는 경우 등,
싱글톤(Singleton) 디자인 패턴은 클래스의 인스턴스 생성을 단 하나의 객체로 제한하는 소프트웨어 설계 원칙입니다. 시스템 전반에서 정확히 하나의 객체만 존재해야 할 때 매우 유용합니다.대표적인 활용 사례로는 파일에 로그를 기록하는 로거(logger)가 있습니다. 로거를 싱글톤 클래스로 구현하면 프로그램 어디서든 동일한 인스턴스에 접근할 수 있어, 여러 모듈이 일관성 있게 로그를 남길 수 있습니다.C++ 싱글톤 구현 예제다음 코드는 C++에서 싱글톤 클래스를 구현하는 기본적인 방법을 보여줍니다.#include <iostr
C++에서 지역 변수(local variable)는 자신이 선언된 스코프(scope)를 벗어나면 더 이상 접근할 수 없습니다. 이것이 바로 지역 변수라는 이름이 붙은 이유입니다. 하지만 예외적으로, 스코프 밖에서 지역 변수의 메모리에 접근할 수 있을 것처럼 보이는 경우가 있습니다. 아래 예제를 통해 살펴보겠습니다.예제 코드#include<iostream> using namespace std; int* foo() { int x = 3; return &x; } int main() { int*
C++에서 float와 double 같은 부동소수점(floating-point) 변수를 비교하는 방식은 작업의 최종 목표에 따라 달라집니다. 부동소수점 연산은 본질적으로 미세한 오차를 포함할 수 있기 때문에, 단순히 == 연산자로 비교하면 의도치 않은 결과가 나올 수 있습니다.세부적인 이론까지 깊이 파고들지 않으면서, 어느 정도의 계산 오차를 감수할 수 있는 간단하고 바로 실행 가능한 함수가 필요하다면 아래 코드를 사용해 보세요.예제 코드#include<iostream> using namespace std; // 허용
C, C++, Python 등 비트 연산을 지원하는 모든 프로그래밍 언어에서는 비트 연산자(bitwise operator)를 사용해 특정 비트를 설정(set), 해제(clear), 토글(toggle)할 수 있습니다. 이때 원하는 위치의 비트를 지정하기 위해 비트 시프트 연산자(<<)도 함께 사용합니다.비트 설정(Set)하기특정 비트를 1로 설정하려면 비트 OR 연산자(|)를 사용합니다. 1을 원하는 자리만큼 왼쪽으로 시프트한 값과 기존 값을 OR 연산하면 해당 비트만 1이 됩니다.예제 코드#include<iostre
C++에서 CSV 파일을 직접 읽고 파싱하는 작업은 생각보다 까다롭습니다. 따옴표로 묶인 필드, 이스케이프 문자, 쉼표가 포함된 값 등 다양한 예외 상황이 존재하기 때문에, 직접 파일을 한 줄씩 처리하면 이러한 경우를 놓치기 쉽습니다. 따라서 실무에서는 검증된 라이브러리를 활용하는 것이 좋습니다.특히 C++의 Boost 라이브러리는 CSV 파일을 읽고 구문 분석하는 데 매우 유용한 도구들을 제공합니다. 아래에서 Boost를 활용한 방법과 표준 라이브러리만으로 구현하는 방법을 각각 살펴보겠습니다.방법 1: Boost 라이브러리의 to
개요C++에서 문자열을 토큰화(분리)하는 것은 텍스트 처리 작업에서 매우 자주 사용되는 기술입니다. 이 글에서는 대표적인 두 가지 방법을 예제 코드와 함께 소개합니다.방법 1: stringstream 사용하기첫 번째 방법은 stringstream을 사용하여 공백으로 구분된 단어를 하나씩 읽어오는 것입니다. 이 방법은 다소 제한적이지만, 적절한 검사 로직을 추가하면 대부분의 경우 충분히 유용하게 활용할 수 있습니다.예제 코드#include <vector> #include <string> #include <s
const와 포인터 조합의 기본 의미C/C++에서 const 키워드와 포인터(*)가 함께 사용되면 작성 위치에 따라 그 의미가 크게 달라집니다. 각 선언이 어떤 것을 의미하는지 아래와 같이 정리할 수 있습니다.int* // int를 가리키는 일반 포인터int const * // 상수 int를 가리키는 포인터 → 가리키는 값 변경 불가int * const // int를 가리키는 상수 포인터 → 포인터 자체 변경 불가int const * const // 상수 int를 가리키는 상수 포
C++ 가상 소멸자의 필요성Scott Meyers는 그의 저서 Effective C++에서 다음과 같이 조언합니다.클래스에 가상 함수가 하나라도 있다면 반드시 가상 소멸자(virtual destructor)를 가져야 하며, 기본 클래스로 설계되지 않았거나 다형적으로(polymorphically) 사용될 의도가 없는 클래스라면 가상 소멸자를 선언해서는 안 됩니다.즉, 다형적 기본 클래스(polymorphic base class)에서는 소멸자를 반드시 virtual로 선언해야 합니다.가상 소멸자가 없으면 어떤 문제가 발생할까?그 이유를
두 포함 방식의 핵심 차이C/C++에서 헤더 파일을 포함하는 두 가지 방식, 즉 #include <파일명>과 #include 파일명의 가장 큰 차이점은 전처리기(preprocessor)가 포함할 파일을 검색하는 위치에 있습니다.#include <파일명>전처리기는 구현 환경(컴파일러)에 종속된 방식으로 파일을 검색하며, 컴파일러가 미리 지정해 놓은 디렉터리들을 탐색합니다. 이 방식은 주로 다음과 같은 경우에 사용됩니다.표준 라이브러리 헤더 파일 포함 (예: <stdio.h>, <iostream&g