본문 바로가기
2D 프로그래밍 수업(일부비공개)

수업 32일차 - static

by MY블로그 2022. 11. 23.

static

정적 변수 
전역변수처럼 데이터영역에 저장이된다
그렇기떄문에 전역변수처럼 사용이 가능하다.
(초기화는 생성시 한번만 이루어지며 데이터영역에 저장 된다)
장점 : 사용이 편리하며 메모리누수가 없다.
단점 : 데이터영역에 지속적으로 공간을 차지하고있기때문에 프로그램작동 내내 용량을 차지하고있다.

참고할 게임 예시.
게임내에서 맵1과 맵2가 있다 가정하면 각각의 맵에 왔다갔다 할수있는데
각각의 맵에는 수많은 객체들이 존재한다. (ex 플레이어 몬스터 npc 아이템 ect .... )
이경우 2개의 맵을 항상 가지고있으면 매우 무겁기 때문에
맵1에 유저가있을경우 맵2는삭제상태이며 맵2로이동시 맵1을 삭제시켜 가볍게 유지한다
이때문제점이있는데 맵1에서 유저가 다양한아이템을 구매해두면
맵2로이동시 맵1이 삭제되어 맵2에 유저가새로 생성되므로 맵1에서 구매한 아이템이 초기화될수있다.
그렇기때문에 플레이어는 항상 상주 시키는데 이때 static 으로 한다면
맵이삭제되어도 플레이어의 정보는 살아있을수있다.
이때 플레이어한두명이면 괜찮지만 수많은 플레이어가있을경우 데이터영역에 저장이 어려울수있다.
플레이어들의 레벨이 높아질수록 아이템도많아지기때문에 더욱 어렵다.
그래서 이때의 해결 방법으로 플레이어에 포인터를 사용한다.
포인터자료형 이라면 데이터는 32bit 에서는 4byte 64bit는 8byte만 사용된다.


정적 함수
정적 멤버 함수

정적 <-> 동적
정적 : 움직이지 않는, 고정된, 멈춰있다
동적 : 고정되있지 않은, 유동적인

상수 <-> 변수

전역 <-> 지역
전역과 지역의 정의는  스코프{}, 범위에 따라 기준이 정해진다.
전역은 스코프의 밖, 지역은 스코프의 내부

전역 함수
전역 자료형(타입)
(ex. 섯다의 struct SDPlayer > 사용자정의타입으로 만든 자료형 범위가 없다 )

데이터 영역
프로그램이 시작될 때 생성되고, 프로그램이 종료하면 소멸

전역 변수
메인이 아닌 전역에서 int a;를 생성하면
해당 cpp내에서 아무데서나 a를 호출하여 사용할수있다.
a는 자기자신이 언제 몇번 불릴지 알수 없다.
때문에 프로그램이 시작될때 생성되며 프로그램이 끝날때까지 소멸하지않는다.(정적주기)
이특성은 static 이 똑같이 가지고 있다.
(데이터 영역에 저장되는 것들이 이러한 특징이 있다.)

정적 변수

변수의 주기 (생성과 소멸이 반복되는 구간)
정적 주기
일반지역변수 : 정의지점에서 생성되고 불러오기가 끝나면 소멸된다
(ex. int input = 메인에서만사용하고 벗어나면 소멸된다. 함수내에 있기때문에 스택프레임이 사라지면 사용x)
할당은 선언되었을 직후에 된다. (스택에)

동적 주기
프로그래머에 의해 할당되고 해제되는것 (ex. 포인터의 new생성과 delete해제)

 

https://boycoding.tistory.com/170

 

C++ 04.06 - 스코프, 주기 및 링크 요약 (Scope, duration, and linkage summary)

04.06 - 스코프, 주기 및 링크 요약 (Scope, duration, and linkage summary) 스코프(scope), 주기(duration) 및 연결(linkage)에 대한 개념은 매우 혼란스러우므로 이 포스트에서 요약할 것이다

boycoding.tistory.com

 

전역변수에 대하여

https://boycoding.tistory.com/168?category=1007833 

 

C++ 04.04 - 전역 변수가 나쁜 이유 (Why global varibles are evil)

04.04 - 전역 변수가 나쁜 이유 (Why global varibles are evil) 만약 베테랑 프로그래머에게 좋은 프로그래밍에 대해 충고를 하나 부탁하면, 몇 가지 중에서 가장 자주 듣는 말은 "전역 변수를 피하라!"일

boycoding.tistory.com

 


 

< 코드 사용 예제 >

 

일반 정수형 a 를 1로 정의한뒤 함수내에서 5를 더해주는 코드이다.

스태틱이 아닐경우 메인에서 함수를 호출할 경우 결과는 a+5 즉 6이 호출될때마다 초기화되어

같은 값이 계속 나오는 것을 확인할 수 있다.

 

 

 

이 코드에서 int a= 1 을 static 을 사용하여 static int a = 1 로 변경 해보도록 하자

 

실행결과 값이 바뀌는것을 볼수가있다.

처음 메인이 실행될때 함수 Add5()가 호출이되면 a = 1로 처음 정의가되며 static 이기때문에 데이터 영역에 저장이된다.

이후 5를더한 6의 값을 출력후

다시 메인에서 두번째 함수를호출한다. 이때 데이터영역에 저장된 a는 소멸(초기화) 되지않았기 때문에 6인상태이다.

이후 a는 1이라고 초기화가 되어있더라도 데이터에 그대로 남아있었기떄문에

a=6인상태로 5를 더하여 11이 출력된다.

마지막함수 호출도 두번째와 같은원리로인하여 16이 호출된다.

 

< 또다른 예제 >

#include<iostream>
using namespace std;

struct Family
{
	string firstName;
	string secondName;
	int age = 10;

	void Print()
	{
		cout << firstName << " " << secondName << " " << age << endl;
	}
};

int main()
{

	Family father;
	Family mother;
	Family son;

	father.firstName = "신";
	father.secondName = "관희";
	father.age = 40;

	mother.firstName = "조";
	mother.secondName = "일남";
	mother.age = 35;

	son.firstName = "박";
	son.secondName = "찬";
	son.age = 5;

	father.Print();
	mother.Print();
	son.Print();

	cout << &father.firstName << endl;
	cout << &mother.firstName << endl;
	cout << &son.firstName << endl;

	return 0;
}

실행결과

Family 라는 구조체를 생성하여 선언 후 출력시켰다. 각각의 구조체는 서로다른 주소를 가지고 있음을 볼수있다.

이에 static 을 사용하게 된다면

 

#include<iostream>
using namespace std;

struct Family
{
	static string firstName;
	string secondName;
	int age = 10;

	void Print()
	{
		cout << firstName << " " << secondName << " " << age << endl;
	}
};

string Family::firstName = " "; // 스태틱은 전역범위에서 1회 초기화해야한다.

int main()
{

	Family father;
	Family mother;
	Family son;

	father.firstName = "신";
	father.secondName = "관희";
	father.age = 40;

	mother.firstName = "조";
	mother.secondName = "일남";
	mother.age = 35;

	son.firstName = "박";
	son.secondName = "찬";
	son.age = 5;

	father.Print();
	mother.Print();
	son.Print();

	//객체의 특징 X
	cout << &father.firstName << endl;
	cout << &mother.firstName << endl;
	cout << &son.firstName << endl;

	return 0;
}

string firstName 을 static string firstName 으로 변경후 출력시켰다.

출력결과 firstName 은 처음에 초기화후 처음 선언된 성인 "신" 이저장되었다가 "조"로 바뀌고

마지막 선언인 "박"으로 최종적인 저장이 되어 전부 박으로 출력되었다.

서로 같은 공간(데이터영역에)을 사용하는것을 볼수있는것이 3개의 주소값은 모두 같은 값을 가지고 있다.

 

< 마지막 예제 >

마지막으로는 그럼 이후 출력에는 위에는 그대로 성을"박"으로 출력하고

다시 다른 성으로 정해서 출력하는 코드를 보도록 한다.

#include<iostream>
using namespace std;

struct Family
{
	static string firstName;
	string secondName;
	int age = 10;

	void Print()
	{
		cout << firstName << " " << secondName << " " << age << endl;
	}
};

string Family::firstName = " "; // 스태틱은 전역범위에서 1회 초기화해야한다.

int main()
{

	Family father;
	Family mother;
	Family son;

	father.firstName = "신";
	father.secondName = "관희";
	father.age = 40;

	mother.firstName = "조";
	mother.secondName = "일남";
	mother.age = 35;

	son.firstName = "박";
	son.secondName = "찬";
	son.age = 5;

	father.Print();
	mother.Print();
	son.Print();

	//객체의 특징 X
	cout << &father.firstName << endl;
	cout << &mother.firstName << endl;
	cout << &son.firstName << endl;

	Family::firstName = "정"; // 스태틱접근방식. 접근하여 수정이 가능하다.
	// ( 사용자정의타입이름의 :: 객체이름 ) 범위 접근

	father.Print();
	mother.Print();
	son.Print();

	return 0;

마지막으로 2번째 예제 결과이후에 

Famill : : firstName = "정"; 을 선언하여 다시 출력한 결과를 보았다.

사용되고있는 데이터공간의 저장위치는 동일한채로

위에출력은 그대로 아래에서 출력 결과는 바뀌는 것을 볼수가있다.

접근 방식은 꼭 기억해두자. ( 구조체이름 : : 객체의이름 = "  " ; )


내부 링크

같은 cpp 파일 내에서만 어디든 접근이 가능하다

(ex. 전역 변수, static 변수, 상수, 정적, 정적 함수)

하나의 cpp 내에서 어디든 간섭이 가능하다.

 

외부 링크

서로 다른 cpp 파일 에서도 접근이 가능하다.

(ex. oop수업자료 게임모음집 메인cpp에 다른 cpp 가져온것 메인메뉴에 각종게임 넣은것 즉.전역함수는 외부링크)

단 전역함수여도 static 이사용이되면 외부 링크가 불가능하게 된다.(즉, 내부링크로 전환이 된다)

함수는 static 여부가 링크와 관련되어 변동이 된다.

 

요약 

변수에 static 이 사용되면 저장 및 할당 위치가 변동이 되지만

함수에 static 이 사용되면 내부 외부 링크에 변동이 된다.

(현재 배우지는 않았으나 연결시켜주는  [C++] static 키워드 (+ extern) 이 있다.)

https://junstar92.tistory.com/318

 

[C++] static 키워드 (+ extern)

References Professional C++ https://en.cppreference.com/w/ Contents static 키워드 용도 extern 키워드 non-local 변수의 초기화(소멸) 순서 C++ 코드에서 static 키워드의 용도는 다양하며, 얼핏보면 그 용도들간에는 전

junstar92.tistory.com

 

댓글