본문 바로가기

Programming/C Programming

c 와 c++ 의 static 내용정리

학생때 적어놓았던 강좌인데 필요로 하시는 분들이 계시내요.

참고하세요.




static와 const를 생각나는데로 죽~ 정리해 봤습니다.

 

static부분 순서는 다음과 같습니다.

 

1. static 지역변수 - 1

2. static 지역변수 - 2 : 배열/포인터를 리턴하는 함수

3. static 전역변수

4. static 함수

5. static 멤버변수

6. static 멤버함수

 

빼먹은것 없이 정리할려고 했는데, 잘 되었는지 모르겠군요.

혹시 보시고 틀리거나 잘못된 사항 있으면 지적해 주시구요..

많은 도움 되었으면 합니다.^^

 

================================<static 지역변수 - 1>================================


#include <iostream>
using namespace std;

void foo()
{
 int a=0;
 static int b = 0; // static는 변수가 지역을 벗어나도 소멸되지 않도록 한다.
 a++;
 b++;

 cout << a << "   " << b << endl;
}

void main()
{
 foo(); // 1번
 foo(); // 2번
 foo(); // 3번

 // cout << b << endl; // 에러가 난다.
}

-----------------------------------------------------------------------------------

보통 함수에서 변수를 선언하면 스택메모리에 저장이 된다.
a, b를 선언하였다면 a가 먼져 스택에 쌓이고 b가 스택에 들어가게 된다.
그리고 함수가 종료될때 이 a, b는 다시 꺼내어 지고 그 값은 소멸하게 된다.

하지만, static로 지역변수를 선언하면 그 변수는 스택이 아닌 static저장영역에
놓이게 되며 이는 함수가 끝나도 지워지지 않게된다.

 

~~~~~~~~~~~~~<메모리 그림>~~~~~~~~~~~~

==data영역==
  변수/포인터등이 저장되어 있다.

  --heap
    동적할당을 받는 영역

  --stack
    지역변수
    초기화 하지 않으면 쓸모없는 값이 들어가 있다.

  --static
    전역변수, static 지역/전역변수가 저장된다.
    초기화 하지 않아도 0으로 초기화 되고, 프로그램이 끝날때 지워진다.

==code영역==
  코드가 저장되어 있다.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

즉, 위의 예제에서 static int b;로 선언한 b는 계속 그 값을 유지하며,
foo함수가 불릴때마다 값이 커지게 된다.
출력결과는 다음과 같다.
1번 - 1   1
2번 - 1   2
3번 - 1   3

여기서 특이한점은 foo함수를 호출할때마다 static int b=0;를 만나게 되는데
(위에서도 총 3번이나 호출하였다)
맨처음 호출될때만 생성과 초기화를 해주고 2번과 3번부터는  static int b=0;을 마주칠때
생성과 초기화를 하지 않는다는 점이다.

static저장영역에 있다고 해도
일반지역변수들 처럼 foo함수안에서만 b에 접근할 수 있다.

 

 


===============<static 지역변수 - 2 : 배열/포인터를 리턴하는 함수>===================


#include <iostream>
using namespace std;

int (* Add( int(*a)[2] , int(*b)[2] ) )[2]
{
 static int sum[2][2];   // 반듯이 static로 선언해야 한다.
 
 for(int i=0 ; i<2 ; i++)
  for(int j=0 ; j<2 ; j++)
   sum[i][j] = a[i][j] + b[i][j];

 return sum;
}

void main()
{
 int a[2][2] = {1, 2, 3, 4};
 int b[2][2] = {5, 6, 7, 8};

 int(*p)[2] = Add(a, b);

 for(int i=0 ; i<2 ; i++)
  for(int j=0 ; j<2 ; j++)
   cout << p[i][j] << endl;
}

-----------------------------------------------------------------------------------

위 예제는 2중배열 두게를 파라미터로 받아 2중배열을 리턴하는 함수에 관해
다루고 있다.
조금 보기 어려울것 같아서 함수의 선언부분을 옮겨봤다.

int (*   Add(     int(*a)[2] ,    int(*b)[2] )    )[2]
리턴값   함수명   첫째파라미터    둘째파라미터    리턴값

이 Add함수는 sum이라는 이중배열을 return한다.
이때 이중배열의 주소를 p에 대입시키는데
sum이 static로 선언되지 않았다면 Add가 끝나는 순간 sum의 메모리는 풀리게 되고
p는 풀린 메모리의 주소를 가지고 있게된다.
그냥 출력해도 값이 나올 수도 있지만,
풀린 sum의 메모리에 다른값이 덮어 씌워진다면
우리는 엉뚱한 값을 얻게 될 것이다.

 

 

===================================<static 전역변수>=================================


#include <iostream>
using namespace std;

int x = 0;  // 전역변수 선언

void main()
{
 int x = 10;
 {
  int x = 20;

  cout << x << endl;   // 1번
  cout << ::x << endl; // 2번
 }

}

-----------------------------------------------------------------------------------

먼저 전역변수에 관해 살펴보자
1번을 출력하면 가장 자신의 블록안의 있는 x값인 20을 출력한다.
하지만 2번 (::x)표현은 전역변수를 뜻하는 것이다.
따라서 0이 출력된다.
만약 전역변수가 선언되어 있지 않다면 2번은 ERROR가 날것이다.

-----------------------------------------------------------------------------------

 

------------A.cpp--------------
int x=0;
int y=1;
static int z=2;


------------B.cpp--------------

#include <iostream>
using namespace std;

extern int x;
//extern int y=3; // ERROR
//extern int z;   // ERROR

//int y=2 // 주의..ERROR


void main()
{
cout << x << endl;
}

// 두파일을 같은 프로잭트 폴더에 넣고, 컴파일은 b.cpp만 해준다.
// 에러는 빌드가 아닌 컴파일시 일어난다.

-----------------------------------------------------------------------------------

A.cpp의 x, y, z는 각각 전역변수이다. 전역변수는 External Linkage(외부연결)을 가진다.
external Linkage는 다른파일에서도 extern키워드를 사용해서 읽어들일 수 있다는 뜻이다.
기본적으로 전역변수와 함수는 External Linkage를 가지게 된다.

하지만 우리가 전역변수 앞에 static을 써주면 그 전역변수는 Internal Linkage를 가지게 된다.
즉, 예제에서 z는 Internal Linkage를 가지므로 B.cpp에서 읽을 수가 없다.

y는 B.cpp에서 읽을 수 있다. 하지만, extern키워드를 사용할때는 초기화를 할 수 없다.

주의할점은 헤더파일과 소스파일에서는 이경우와 조금 틀리다.
이것은 서로 다른파일로 구분되어 컴파일 되지만,
헤더파일은 소스파일과 합쳐져서 하나의 파일을 이루기때문에 Linkage의 개념이 아닌,
하나의 변수로 보아야 한다.
이때 헤더파일이나 소스파일에 쓰이는 전역변수들이 모두 External Linkage를 가진다면
다른파일들에서 정의하는 전역변수들의 이름과 충돌이 날 우려가 있다.
주의..라고 쓴 y를 보면 쉽게 알 수 있을것이다.
A.cpp에서 이미 선언을 했으므로 B.cpp에서 선언을 하면 애러가 나게된다.
따라서 전역변수들은 특별한 경우가 아니라면,
모두 static 전역변수로 선언이 되야된다. 꼭...!!!

 

 


=====================================<static 함수>===================================

static함수는 static변수와 크게 다르지 않다.
const 함수를 제외한 모든 함수는 기본적으로 External Linkage를 가지고 있다.
하지만 static 키워드를 사용하면 그 함수는 Internal Linkage를 가지게 된다.

 


====================================<static 멤버변수>================================


#include <iostream>
using namespace std;

class A
{
public:
 //A():z(0){} // ERROR
 int x;
 int y;
 static int z;
};

int A::z = 0;               // 1번

void main()
{
 A::z = 3;            // 2번

 cout << A::z << endl;
 
 A p1; A p2;

 p1.z = 10;
 p2.z = 5;

 cout << p1.z << endl;  // 3번
}

-----------------------------------------------------------------------------------

1번 : static멤버변수는 반드시 외부에서 초기화를 해주어야 한다.
      초기화하지 않으면 컴파일시 ERROR가 난다.

2번 : static멤버변수는 객체를 만들지 않아도 접근이 가능하다.

3번 : static멤버변수는 객체들간에 동일하게 접근이 된다.
      p1이나 p2나 동일한 z를 가진다.

이러한 특성을 이용하여 static멤버변수를 클래스 객체 카운터로 활용하기도 한다.

 

 

=================================<static 멤버함수>===================================


#include <iostream>
using namespace std;

class A
{
public:
 static void foo(){cout << "foo" << endl;}
 void goo(){cout << " goo" << endl;}
};

void main()
{
 A::foo(); // 1번

 A a;
 a.foo();

 void(*p)() = A::foo; // 2번
 p();

 void(A::*q)() = A::goo; //3번
 (a.*q)();
}

-----------------------------------------------------------------------------------

보통의 멤버함수는 보이는 파라미터 외에 하나의 파라미터가 숨겨져 있다.
이것은 자신의 멤버함수를 나타내는 파라미터 this이다.
하지만, static멤버함수는 this파라미터를 가지고 있지 않다.
따라서 객체를 생성하지 않고 호출할 수 있다.(1번)
이러한 차이는 foo()함수의 포인터인 p와
goo()함수의 포인터인 q의 선언모양에서 확연하게 들어난다. (2, 3번)

-----------------------------------------------------------------------------------

◎참고 : 함수포인터

일반함수를 포인터로 받아보자.
포인터는 일반적인 모양이 아닌 함수의 정보를 가진 형태로 선언되어야 하며
그 모양은 다음과 같아.

char hoo(int a, double b){ ... }

char (*p) (int, double);
함수리턴값 함수포인터 첫번째파라미터 두번째파라미터

이렇게 선언을 하면 p를 이용하여 hoo를 가르킬 수 있다.

p(1, 3.5); // 혹은 (*p)(1, 3.5); 를 써도 동일하다.

-----------------------------------------------------------------------------------

#include <iostream>
using namespace std;

class A
{
public:
 
 void goo()
 {
 x=10;
 y=5;
 }
 
 static void foo()
 {
 //x=10; // 1번
 y=10; // 2번
 //goo(); // 3번
 }

private:
 int x;
 static int y;
};

int A::y = 0;

void main()
{
 A::foo();
}

-----------------------------------------------------------------------------------

static멤버함수가 this포인터를 가지지 않는다는것은 무엇을 뜻할까?
그것은 static 멤버함수가 자신의 멤버에 접근할 수 없음을 뜻한다.
보통의 멤버함수들은 this->x와 같은 용법을 사용해서 자신의 멤버변수에
접근한다. 대신에 static멤버함수는 static멤버변수에 접근할 수 있다.
따라서 1번은 일반 멤버변수이므로 ERROR가 난다.
3번은 멤버함수이므로 this포인터가 없이 접근할 수 없다.
물론 static멤버함수에서 또다른 static멤버함수에 접근하는 일은 가능할 것이다.

'Programming > C Programming' 카테고리의 다른 글

배열 초기화 선언  (1) 2010.10.29
c 와 c++ 의 const 내용정리  (0) 2010.10.08
해킹방지를 위한 코딩법  (0) 2010.08.20
__read_mostly keyword  (0) 2010.06.29
전처리기 사용 : define ##  (0) 2010.02.23