C++ 변수

NYH·2023년 11월 4일

C++

목록 보기
4/17
post-thumbnail

목차

  1. 변수란?
  2. 지역 변수, 전역 변수
  3. 정적 변수, 외부 변수
  4. 문제 풀이


1. 변수란?

변수의 정의

변수란 데이터를 저장하기 위해 프로그램에 의해 이름을 할당받은 메모리 공간임.
변수란 데이터를 저장할 수 있는 메모리 공간을 일컫습니다.


변수의 이름 생성 규칙

  1. 대소문자, -, 숫자로 생성할 수 있습니다.
  2. 숫자로 시작할 수 없습니다.
  3. keyword로 등록된 이름은 사용할 수 없습니다. ex) int
  4. 변수의 길이에는 제한이 없습니다
  5. 변수 사이에는 빈 공간을 넣을 수 없습니다.


변수와 메모리 주소

변수는 메모리의 주소를 기억하는 역할을 수행합니다.
메모리 주소는 물리적 메모리 공간을 서로 구분하기 위해 사용되는 식별자입니다.
변수 참조시에는 메모리 주소를 참조하는 것이 아닌 해당 주소에 저장된 데이터를 참조합니다.


변수의 선언

int main()
{
	// 변수의 선언
	int x;
}

메모리 공간에 타입과 이름을 지정하여 정의하는 것을 변수의 선언이라고 합니다.

  • 선언과 동시에 초기화
int main()
{
	// 선언과 초기화
    int x = 1;
}

변수를 선언하면서 메모리 공간에 데이터를 기입해주면 선언과 동시에 초기화를 한다고 합니다.
변수를 선언하면서 초기화를 하지 않으면 메모리 공간에는 어떤 값인지 알 수 없는 쓰레기 값이 들어가 있기 때문에 변수는 선언과 동시에 초기화를 하는 것이 좋습니다.


2. 지역 변수, 전역 변수

지역변수

int func(int x)
{
	int x = 1;
}

int main()
{
	int x = 1;
}

중괄호 내에서 선언되는 변수들을 지역변수라고 합니다.
지역 변수는 중괄호 내에서만 지정된 이름으로 밖에서는 사용할 수 없습니다.

전역변수

int x = 1;

int main()
{
	// main()에서 선언되지 않았지만 전역 변수이므로 사용 가능
	x += 2;
}

모든 중괄호 바깥에서 선언된 변수들을 전역변수라고 합니다.
코드의 모든 구간에서 사용 가능한 변수입니다.


3. 정적 변수, 외부 변수

헤더 파일과 소스 파일

  • 헤더 파일 (.hpp, .h ...)
// header 
// expression.h
int add(int a, int b)
int sub(int a, int b)

코드에서 주로 함수나, 변수, 클래스들을 선언하고 정의하지 않는 코드 파일들을 헤더 파일이라고 합니다.

컴파일을 통해 실행 파일이 생성될 때 컴파일의 마지막 과정인 링크 과정에서 하나로 합쳐집니다.

  • 소스 파일 (.cpp, .cc...)
// source
#include "expression.h"

int add(int a, int b)
{
	return a + b;
}

int sub(int a, int b)
{
	return a - b;
}

코드에서 주로 함수나, 변수, 클래스들을 정의하는 코드 파일들을 소스 파일이라고 합니다.

헤더파일에서 함수나, 변수, 클래스들을 선언하고 해당 선언부에 맞는 정의를 구현하는 코드 파일이 소스파일입니다.

선언과 정의의 차이


전방 선언


int main()
{
	int result = add(1, 2);
}

int add(int a, int b)
{
	return a + b;
}

다음의 코드는 유효하지 않습니다.
컴파일러는 int add(int a, int b) 라는 함수를 알지 못하기 때문에 이런 경우 컴파일러가 add라는 함수를 인식할 다른 방법이 필요합니다.

int add(int a, int b);

int main()
{
	int result = add(1, 2);
}

int add(int a, int b)
{
	return a + b;
}

이 때 가능한 방법이 전방선언입니다.
함수를 구현하기 전에 사용할 형태만 정의해 둔 뒤 나중에 구현부를 정의하는 것입니다.

이를 통해 함수를 전방 부분에 선언한 뒤 나중에 정의할 수 있습니다.


분할 구현

  1. 헤더 파일

    주로 클래스 및 함수의 선언을 담고 있습니다.
    헤더 파일은 다른 소스 파일에서 선언된 클래스와 함수를 사용할 수 있게 합니다.

  2. 소스 파일

    주로 클래스 및 함수의 정의를 담고 있습니다.
    이 파일에서는 실제 코드가 작성되어 클래스와 함수의 구현부가 들어갑니다.

  3. 분할 및 재사용

    이처럼 헤더 파일과 소스 파일을 나누어서 구현하는 이유는 코드를 논리적 단위로 분리하여 재사용성을 높이기 위함입니다.

분할 구현의 문제점

  1. 헤더 파일과 소스 파일의 불일치 오류

    소스 파일은 선언된 헤더 파일을 정확하게 참고하여 작성하여야 합니다.

    매개 변수, 함수의 이름, 파일의 이름이 틀린 경우 링킹 과정에서 오류가 발생합니다.

  1. 순환 포함 오류, 동일한 헤더 include

    많은 헤더 파일과 소스 파일을 생성하여 파일을 구분하다 보면 동일한 헤더 파일을 include하는 경우와 순환 포함 오류가 발생합니다.

  • 동일한 헤더 파일 include 재정의 오류
// a.h

class A{
public:
	A() { }
    void doSomething();
}

// b.h
#include "a.h"
class B{
public:
	B() {}
    void doSomething();
}


// test.cc
#include "a.h"
#include "b.h"

int main()
{

}

동일한 A클래스를 재정의하는 에러가 발생합니다.

헤더 파일을 여러개 생성해서 관리하다 보면 다음과 같이 재정의 오류가 발생할 수 있습니다.

  • 순환 포함 오류
// a.h
#include "b.h"
class A{
public:
	A() { }
    void doSomething();
}

// b.h
#include "a.h"
class B{
public:
	B() {}
    void doSomething();
}


// test.cc
#include "a.h"
#include "b.h"

int main()
{

}

  • 해결 방법 : 헤더 가드(헤더 파일 중복 포함 방지)
#ifndef A_H
#define A_H

#include "B.h"

class A {
public:
    A() { }
    void doSomething();
};

#endif // A_H


#ifndef B_H
#define B_H

#include "A.h" // A.h를 다시 포함하면 순환 포함 문제 발생

class B {
public:
    B() { }
    void doSomethingElse();
};

#endif // B_H

다음과 같이 헤더가드를 사용하여 헤더 파일의 중복을 막음으로써 해결할 수 있습니다.

헤더 파일의 경우 #ifndef, #define, #endif의 구조로 이루어져 있는데 이를 해석해보면,

#ifndef : if not define => 정의되어 있지 않다면
#define : define => 정의하겠다
#endif : end if => 끝나는 부분

요약하자면 아직 define 으로 정의되어지지 않은 헤더 파일이라면 정의를 수행한 뒤 헤더 파일의 endif 부분까지 정의하겠다는 의미입니다.
헤더파일의 재정의 문제를 해결하기 위한 방법입니다.

  1. 복잡성 관리

    많은 양의 헤더 파일과 소스 파일을 사용하다 보면 자연스럽게 파일을 관리하는 복잡성이 올라가게 됩니다. 빌드 시스템 과 구성 관리 도구를 이용하여 이러한 문제를 관리해야 합니다.

  1. 네임 스페이스 충돌

    다양한 소스 파일에서 동일한 이름의 클래스 또는 함수를 사용하면 네임 스페이스 충돌 문제가 발생할 수 있습니다. 이를 피하기 위해서는 적절한 네임스페이스 또는 식별자 범위 관리가 필요합니다.


정적 변수

  1. 특징
    • 한번만 초기화 됩니다.
    • 메모리에서 데이터 영역에 저장됩니다.
    • 선언 시점부터 프로그램이 종료될 때까지 유지됩니다.
    • 변수가 선언된 스코프 내에서만 사용 가능합니다. 스코프를 떠나더라도 상태 값이 유지됩니다.
    • 스코프가 아닌 외부에서는 접근이 불가능합니다.
  1. 사용 예시 (함수 카운팅)
int countFunctionCalls()
{
	static int count = 0;
    count++;
    return count;
}

static int count = 0; 구문을 보게 되면 해당 함수를 실행할 때마다 초기화를 수행하는 것처럼 보이지만,
실제로는 이미 초기화되었을 경우 한번만 초기화를 수행합니다.

  1. 주의 사항
  • 프로그램 시작 시 한번만 초기화 되므로 사용에 주의를 해야 합니다.
  • 멀티 스레드 환경에서 정적 변수에 대한 동시 접근을 막고 필요한 경우 스레드 안전성에 대한 구현이 필요합니다.


외부 변수

외부 변수는 다른 파일 또는 소스 코드 단위에서 선언된 변수를 나타냅니다.
외부 변수를 사용하려면 변수가 선언된 외부 파일에서 extern 키워드를 사용하여 해당 변수를 선언하고 프로그램의 여러 부분에서 변수에 접근할 수 있게 합니다.

  • 필요성

    전역으로 사용되는 변수 값을 다른 cpp 파일에서 사용하고 싶을 경우.

// config.h
int x = 1;

// function.h
#include "config.h"

// function2.h
#include "config.h

// main.cpp
#include "function.h"
#include "function2.h"
/*
	int x = 1;
    int x = 1;
*/

int main()
{

}

메인에서 전역 변수 헤더 config.h를 include한 두 헤더를 include하게 되면?

다음과 같이 재정의 오류가 발생합니다.
해당 문제를 해결하기 위해 extern 키워드를 붙인 외부 변수를 사용합니다.

  • extern 예시
// Config.h
#ifndef CONFIG_H
#define CONFIG_H

extern int configurationSetting; // 외부 변수 선언

#endif // CONFIG_H

// Config.cc
#include "Config.h"

int configurationSetting = 42; // 설정 값을 초기화

// main.cc
#include "Config.h"
#include <iostream>

int main() {
    // 설정값에 접근
    std::cout << "Configuration Setting: " << configurationSetting << std::endl;

    // 설정 값을 변경
    configurationSetting = 100;

    return 0;
}

다음과 같이 extern 외부 변수를 사용할 수 있습니다.

  • 잘못된 extern 사용법
// 잘못된 사용.
extern int x = 1; 

extern 외부 변수 사용 시 초기화를 수행하면 안됩니다.
이러한 이유는 c++에는 모든 함수와 변수들의 정의는 전체 프로그램에서 유일 해야 한다는 유일 정의 규칙이 있기 때문입니다.


4. 문제 풀이

문제 1.

변수의 정의에 대해서 설명해주세요


문제 2.

int 타입 변수 x의 값은?

void func(int x)
{
	x += 10;
    x -= 5;
}

int main()
{
	int x = 10;
	func(x);    
}


문제 3.

다음 출력 결과는?

#include <iostream>

int countFunctionCalls()
{
	static int cnt = 0;
    return cnt++;
}

int main()
{
	for(int i = 0; i < 4; ++i)
    {
    	countFunctionCalls();
    }
    
    std::cout << "함수의 실행 횟수 : " << countFunctionCalls() << std::endl;
	
}


문제 4.

다음 코드의 문제점을 확인하고 올바르게 고쳐주세요.


// a.h

int add(int a, int b);

// b.h

#include "a.h"
int sub(int a, int b);

// main.cc

#include "a.h"
#include "b.h"

int main()
{
	
	return 0;
}


profile
그냥 해라

0개의 댓글