[ 전역변수, 지역변수 ]

  

전역 변수 ( Global Vaiable )라는 것은 그이름이 의미하는 것처럼, 프로그램 어디에서나 접근이 가능한 변수를 말하는 것이다.

 

지역 변수 ( Local Variable )는 중괄호({ }) 내에 선언된 변수

즉! 함수 내에 선언된 변수와 {}내에 선언된 변수 

 

[코드]

 

위의 코드에서 보면 전역 변수와 같은 이름의 지역변수가 존재하는데 이럴땐 어떻게 될까?

 

[결과]


 

의외로 해깔릴수도 있겠지만, 결과를 보면, 확실히

"지역 내에서는 지역 변수가 전역 변수보다 우선시 된다." 는걸 알수 있다.

간단한거지만, 초보자에게는 실수하기 쉬운 내용이다.

프로젝트가 커지고, 전역변수가 난무(?)하는 프로그램을 만나다보면.. 알수있겠지만.. 사실상 저런코드는 아주 좋지 않다. 지역변수에 표기법도 그렇고, 나중에 알겠지만, 헝가리식표기법을 숙지하는게 좋다

 

머 다행이도 전역변수와 이름이 같다해도... 지역변수가 위니까 문법오류가 잘안나오는것 뿐 !!!

 

 

[ static, extern 키워드 ]

 

static 의 사전적의미는 '정적인'이란 뜻이다. static 키워드로 선언한 int형 변수는 자동으로 0으로 초기화 되며, 메모리 영역중 Data 영역에 저장되어 프로그램 시작부터 종료시까지 유지된다.

 

1) static 전역 변수

 

해당 소스파일내에서만 유효하게 쓰겠다는 의미이다.

따라서 다른 소스파일에서 해당 전역변수를 참고할 수 없다. 그러나 static이 아닌 extern 키워드로 전역변수를 선언할경우 다른 소스에서도 인식할 수 있다.

 

<main.c 소스>


<sub.c 소스>


 
static으로 전역변수를 선언하면, 다른 소스파일에서는 그 변수를 직접 바꿀 수도, 알 수도 없다.
하지만 static 전역변수를 선언한 쪽에서 이 변수를 바꿀 수 있는 함수를 제공한다면, 다른 소스파일에서 함수를 호출하는 방식으로 전역변수의 값을 바꿀 수 있다.
 
 
2) extern 전역 변수
<main.c 소스> 

 

<sub.c 소스> 


extern으로 변수를 선언한다면, 다른 소스파일에서도 이름을 같게 하여 설정하면 그 변수를 여러 소스에서 공유해서 쓴다는 것. 이후 main에 관련된 바이너리 파일을 만들 때, 컴파일하여 만들어진 각각의 오브젝트 main.o와 sub.o를 같이 링킹해준다. 그러면 main 소스쪽에서도 sub.c 함수를 통해 만들어진 전역변수 값의 변화를 알 수 있다.
─────────────────────────────────────────────
 

3) static 지역 변수

 

static 지역변수는 전역 변수의 특징을 일부 지니면서도, 선언된 지역 내에서만 접근이 가능하다.

 

 

[코드]

 

 

 

위 코드에서 static int sCount 변수는 main 함수에서 접근할 수 없다.

하지만 static 키워드로 함수내에 선언된 변수는 프로그램 시작시 한번 초기화 되므로 아래의 결과처럼 Count 가 증가 할 수 있는것이다.

 

[결과]


프로그램이 실행되는 동안에 계속해서 유지되어야 하는 변수가 있다면 대부분의 경우 전역 변수를 머리 속에 떠올린다. 그런데 그 변수에 접근하는 영역이 특정 지역에 종속된다면 우리는 static 변수의 선언을 생각 해야한다.

 

 [ goto 키워드 ]

 

프로그램의 실행 위치를  정해진 위치로 이동 시킬때 사용되는 키워드이다. 

 

[코드]

 

[결과]


 

─────────────────────────────────────────────

 

위에 프로그램 예시에서 중요한것은 goto문은 단순히 프로그램 실행위치를 바꿔주는것 뿐이라는 점 

사실상 goto문은 많이 사용하지 않는다. 프로그램 구조가 때로는 많이 복잡해질 수도 있기 때문이다.

 

나도 머 자주 사용하는건 아니지만, 쓸때는 확실히 가독성을 중점으로 코딩하는것이 좋다.

 

이렇게 말해도 goto문은 정이 안간다.

1000줄, 10000줄되는 코드에 goto문의 3~5개의 lable을 왔다갔다 하면서, 디버깅을 해본사람이라면 알수 있을꺼다.

 

 

위에 코딩처럼 어떻게 로직을 구성하느냐에 따라 달라지긴 하겠지만, 중갈호를 붙여 구분을 짖고, lable은 구분을 지어 해당 영역만 실행되는 switch문의 스타일로 사용해도 나쁘진 않다.

 

[결과]

 

goto문의 문법적요소는 프로그래머가 goto문을 사용해 어떻게 코딩을 하냐에 따라 너무 크게 영향을 준다. 그러므로 신중히 사용할 필요가 있겠다. 득이될지, 독이 될지는 프로그래머 몫

 


 

[ 연산자 ]

 

 

증감 연산자 ( ++ , -- )

 


 

위의 코드에서 왜 for문은 전위,후위를 썼는데도 출력 값은 같을까요?

간단합니다. 

 

int i;

for ( i =0 (1번) ; i < 5 (2번) ; i++ (4번) )

{

printf(); (3번)

}

for문 루프의 흐름은 이렇게 됩니다.

 

먼저 반복되어지는 코드로 들어가죠

왜 전위,후위를 사용해도 같은값이 나오는지 이제 딱 감이오죠?

 

보통 for문의 증감식에 증감 연산자를 사용하게 되는데 컴파일러에서 최적화를 하기 때문에 차이가 없을 수도 있지만 최적화를 하지 않을 경우에는 전위 증감 연산자를 사용하면 후위 증감 연산자 보다 성능상 이득이 있을 수 있습니다.

 

증감 연산자를 사용할 때 전위나 후위 모두 사용해도 될 경우에는 전위 증감 연산자를 사용하면 성능상 이득을 볼 수 있습니다

 

 

배열에서 전위,후위 증감연산자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
 
int main(){
 
    int arr[5] = {1, 2, 3, 4, 5 };
    int pos = 0;
 
    cout<<arr[pos++]<<endl;        // 출력값 : 1
    cout<<arr[pos]<<endl;        // 출력값 : 2
    cout<<arr[++pos]<<endl;        // 출력값 : 3
    cout<<arr[pos]<<endl;        // 출력값 : 3
 
 
    system("pause");
    return 0;
}

 

 

─────────────────────────────────────────────

복합 대입연산자 = , += , -= , *= , /= , %= )

 

int a += 4; 

  

풀어쓰면

 

int a = a + 4; 의미 합니다.

 

결과값은 4

 

 

─────────────────────────────────────────────

sizeof 연산자 

 

변수나 자료형의 사이즈를 계산하는 연산자 입니다.

 
int num = 0;
 
printf( "%d \n" , sizeof(int) );
printf( "%d \n" , sizeof(num) );
 
int 형 변수는 4byte이므로
두개의 printf 결과 값은 4


 

 [ 변수 타입과 표현 범위 ]




 

long long (8byte) : -9223372036854775808 ~ 9223372036854775807

unsigned long long (8byte) : 0 ~ 18446744073709551615

long long은 "%lld" 로, unsigned long long은 "%llu" 로 표현됨

윈도우 운영체제의 경우 __int64, unsigned __int64 라는 이름으로 제공되며

"%I64d", "%I64u" 로 표현됨. (최근에는 long long도 사용가능.)

 

정수형은 signed와 unsigned에 따라 표현범위가 달라지고, 실수형은 unsigned형이 없다.  

 

 

  

─────────────────────────────────────────────

 

 [ 부동 소수점의 표현 ]

 

실수의 표현은 정수.소수 의 형식이 기본이다. 예를들어 1.23 처럼. 소수의 특성상 데이터의 표현에 한계가 있기때문에 부동소수점을 사용하면 더 큰 범위의 표현을 할수있다.

e-n 또는 e+n 을 사용하면 소수점을 -n만큼, 또는 +n만큼 이동시키는 표현이 된다. 

 

 1.23e-3

 0.00123

 1.23e-2

 0.0123

 1.23e+3

 1230

 1.23e+2

123 

  

 

 

소수점의 자릿수가 -n만큼 또는 +n만큼 이동하였음을 알수있다.

소수점의 자릿수에따라 표현범위가 적어진다는 단점을 보완한 방법이다. 

 

 

 

─────────────────────────────────────────────

 

 

 [ 서식 문자 ] 



 

scanf 명령으로 실수를 입력받을때는 데이터형에 주의해야 하는데,

float형의 데이터를 입력받을때는 %g를, double 또는 long double형의 데이터를 입력받을때는 %lg 로 구분한다.

실수를 %f 또는 %e로 입력받을때도 %lf 와 %le 로 구분해서 입력받아야 한다.

 

 

 

 

 [ 출력 예시 ]

 

 

printf("%d", 10);      //10진 정수 10 출력

printf("%s", "abcd"); //문자열 abcd 출력

printf ("%X", 0xff);   //16진수 FF (10진수 255) 출력

printf("%s", p);        //배열 또는 포인터변수 p가 가진 문자열 데이터를 출력

 

scanf("%d", &a);      //변수 a에 대해 10진 정수값을 입력

scanf("%s", p);        //포인터 또는 배열형변수 p에 대해 문자열을 입력

 


 

 [ 진수 ]

 

2진수 => 0~1

8진수 => 0~7

16진수=> 0~9, a~f 



─────────────────────────────────────────────

 

부호비트가 1이면 음수 0이면 양수다. (부호비트는 [1]0000000 )

 

음수를 표현할 때에는 2의 보수 체계를 기억해야 한다.

 

00000101 = 10진수 +5 가 된다.

 

이것을

 

11111010 ->1의 보수를 취한다.

11111011 ->1을 더한다. 이 값이 음수 -5값이 된다.

 

이값이 왜 -5인가 하면, 2의 보수 를 취하기 전에 10진수 정수값인 5였으며,

부호의반전으로 -를 붙인거뿐 바뀐건 없다.

 

─────────────────────────────────────────────

 

 [ 비트 연산자 종류 ]

 

&

비트단위 AND

|

비트단위 OR

 

비트단위 XOR

~

비트단위 NOT

<<

왼쪽으로 이동

>>

오른쪽으로 이동

 

 

예제

 

 

 

결과


 

 

& 연산 => 둘다 1이면 1, 아니면 0

 

| 연산 => 둘 중 하나만 1이어도 1, 둘다 0일때 0

 

^ 연산 => 둘다 다르면 1, 같으면 0

 

~ 연산 => 해당변수의 모든비트를 반전, MSB(부호비트)도 반전이 되므로 음수가 됩니다. 1의 보수를 취한후 1을 더하면 음수값을 알수 있습니다.

 

<< 연산 => 비트단위로 n회 왼쪽으로 이동시킵니다. 이동한 만큼 비는 공간은 0으로 채워지며, 왼쪽 끝에 도달하여 더 이상 갈 수 없을 경우 해당 비트는 삭제됩니다. 1비트씩 이동 할 때 마다 값이 2배가 됩니다.

 

>> 연산 => 비트단위로 n회 오른쪽으로 이동 시킵니다. 이동 한 만큼 왼쪽 끝의 비트는 0으로 채워지며, 오른쪽 끝에 도달하여 더이상 갈 수 없을 경우 비트는 삭제됩니다. 그러나 음수일 경우 왼쪽의 비트 처리가 시스템에 따라 달라집니다. 음수를 유지하기 위해 1로 채워질 수도 있고, 0으로 채워질수도 있습니다.

 

─────────────────────────────────────────────

 

 [ 비트 연산의 필요성 ]

 

중요한건 비트연산을 왜쓰냐는건데 ... 

 

난!

하나의 변수에 특성,속성,옵션을 지정할때 쓴다.

 

exmaple_1. 메시지박스

 

#define DF_OKBUTTON 0x0001

#define DF_CANCELBUTTON 0x0002

 

option |= ok_button

 

검사

if( option & ok_button ){

printf("OK_button \n");

}

 

exmaple_2. 착용무기 

 

 

 

결과


 

상태 구분 방법은 BOOL 변수로 TRUE, FALSE 로 구분하는 방법은 있다.

하지만, 상태값이 3개나 10개, 30개라면 3, 10, 30개씩 BOOL값이 필요하다.

 

이는 심한 메모리 낭비를 불러오고 플래그의 상태를 확인하기 위하여 함수의 매개변수갯수가  

플래그의 갯수만큼이  것이다. 

 

8bit 8개의 상태를 체크할  있다 

int(4byte) 하나면 32개의 상태를 체크할  있다.

 

 즉!

 

iHand 변수에 무기가 여러개가 들어가게되는데, 비트연산이 아니라면 변수 하나에 무기를

바꿀수만 있지 다중으로 적용은 안된다.

배열처럼 다중 변수 선언이 되야한다.

검[0] =true;

창[1] =true;

 

하지만, 속성이 늘어날수록 변수는 늘어나고, 메모리에 비효율적인것이다.

 

ex 검, 검창, 검활, 검창활 등 경우의 수는 계속 늘어나게 된다.

 

결론은

 

비트연산은 자신의 소스에 알맞게 적용해서 사용하면 효율적이다

 

────────────────────────────────────────

 

 [ 참고 ]

 

0x00000001

0x00000002

0x00000004

0x00000008

0x00000010

 

위는 16진수 표기로 비트를 표현한건데, 16진수는 하나당 비트 4비트이다.

이건 2진수 1111 => 10진수로 15인데, 16진수는 10진수 15(f)까지 표시가 가능하다.

 

즉 16진수는 표기는 하나당 (1111)4비트가 된다. 위에 선언은 총 4 * 8 =32bit가 선언된것이다.

 

그런데 2n승의 값으로 선언이 되어있는데, 이것은 잘 만 생각해보면 이해할수있다.

 

0001 = > 1  [검]

0010 = > 2  [활]

0011 = > 3  [총]

 

2진수의 10진수로 바꾸면 이렇게 되는데,

 

hand = 0001 | 0010;

 

[검]과 [활]을 적용시키면 [총]이 된다. 머.. 이상할껀 없다. [검] [활] [총]까지 모두 검출 할 수 있다.

 

하지만, 난 분명, 검과 활만 적용시켰는데 총까지 적용되었다.

 

결과적으로 비트가 중첩되는 현상이 생겼다.

 

중첩이 된다면, 위에 예시처럼 의도 하지 않는 속성이 들어 간다.

 

 

 

2n승의 값 으로 비트를 선언해야 하겠다.

 

────────────────────────────────────────

 

 [ 옵션 함수 처리 ]

 

void SetOption(DWORD dwOption, BOOL bSet)
{
     // 옵션 적용
     if (bSet == TRUE) {
             m_dwOptions |= dwOption;

     }
     else {
             m_dwOptions &= ~dwOption; //<옵션해제

     }
}

 

 

 

+ Recent posts