./../
  I D :    암호 :    ID저장      회원가입    네게(쥔장) 쪽지보내기    메모장    각종계산기    컬러빠렛    이모티콘(프레임, 새창)    보유장비
목록으로 가기..
C 요약 김종호 | 삭제하기

C-언어 요약 및 사용법

 


C 언어 요약

 1972년에 탄생한 C언어는 실행코드가 작고 빠르면서도 하드웨어와 친숙하며, 뛰어난 이식성이 부각되어 UNIX, DOS 등 많은 운용시스템에서 C언어를 채택하게 되었다. C언어에서는 함수내부에서 선언한 지역변수가 다른 함수에는 영향을 주지 않아 체계적이고도 함수 독립적인 프로그래밍이 가능하다. 처음에 작성한 프로그램에 버그가 있을 경우 버그가 들어 있는 함수만 새로운 것으로 교체하면 기존의 프로그램을 그대로 사용할 수 있으므로 유지, 보수의 측면에서도 유리하다. 어셈블러는 프로세서가 바뀌면 다시 배워야 하지만 C언어는 표준화된 언어이므로 프로세서간의 차이가 거의 없다. 지금부터 C언어를 간단하게 살펴본다.

◆ 먼저 알아 둘 사항

  • 주석문

프로그램을 이해하기 쉽도록 주석문을 다는데 사용한다. 주석문은 /* 로 시작하여 */로 끝난다. 컴파일러는 /* 와 */ 사이의 모든 문자를 무시하므로 /*,*/ 사이에 한글 주석을 달 수 있다. 볼랜드 C++같은 C++ 컴파일러에서는 C언어의 주석문 이외에도 '//'로 시작하는 행은 모두 주석으로 처리된다.

    /* C, C++의 주석 (여러 라인에 적용)*/

    // C++에만 적용되는 주석 (한줄에만 효과)

  • 문장의 끝

문장의 끝은 반드시 ; (세미콜론)으로 끝나야 한다. 문장끝에 ;을 깜빡하여 컴파일 에러가 발생하는 경우가 많으므로 주의한다.

    int x=3; /*문자의 끝에는 ;을 사용해야 한다*/ 

  • 복합문 (compound statement)

복합문은 2개 이상의 문장을 중괄호 {,}로 묶은 것을 말하며, 블록(block)이라 말한다. 복합문도 1개의 문장과 동일하게 취급한다.

    x=3; /* 단일문 */

    { /* 두 개의 단일문으로 이루어진 복합문 */

    x=5; /* 단일문 */

    y=7; /* 단일문 */

    }

  • 대소문자 구분

C언어에서는 대문자와 소문자를 구별한다. 즉 abc와 ABC는 서로 관계없는 변수이다.

int abc; // abc와 ABC는 서로 다른 변수이다.
int ABC;
  • 참과 거짓

좋은 사람과 나쁜 사람에 대한 기준이 개인에 따라 다르듯이 언어마다 참과 거짓을 구분하는데 조금씩 차이가 있다. C언어에서는 숫자 0(영)을 거짓, 숫자 0이 아닌 값을 참으로 판단한다.

    if (1)

        printf(이것은 참입니다. 왜냐하면 1은 0이 아니기 때문입니다.“);

◆ C 언어 요약

  • 키워드

터보-C(혹은 볼랜드-C)의 경우 80x86 하드웨어의 특성을 반영하여 far, near, interrupt 등 몇 개의 키워드를 추가하였으나, C언어의 표준 키워드는 모두 32개에 불과하다. 이 중에서도 주로 사용되는 키워드는 26개 정도에 불과하므로, BASIC언어의 약 150개 키워드에 비해서는 물론이고 80x86어셈블러의 기본적인 키워드의 수인 약 70개에 비해서도 상당히 적은 양이다.

C언어의 키워드는 다음과 같이 모두 소문자로 구성되어 있다. 괄호 안의 키워드는 자주 사용되지 않는 것이다.

    (auto) break case char (const) continue

    default do double else enum extern

    float for (goto) if int long

    register return (short) (signed) sizeof static

    struct switch typedef union unsigned void

    (volatile) while

  • 터보-C 추가 키워드

터보-C에서 추가한 키워드는 인터럽트 관련 조작을 위한 interrupt 키워드와 80x86하드웨어를 세밀하게 제어하기 위해 포인터에 관련된 near, far 그리고 인라인 어셈블러(C코드 중간에 직접 어셈블러를 기록하는 방법)를 위한 asm과 의사 레지스터변수, 그리고 변수전달 방식에 C언어형식을 적용할 것인가 pascal형식을 따를 것인가를 결정하는 cdecl, pascal 키워드로 구성되었다. 괄호안의 키워드는 자주 사용되지 않는 것이다.

    asm (cdecl) far interrupt near (huge)

    (pascal)

    의사 레지스터변수

    _AX _BX _CX _DX _SI _DI _BP _SP

    _AL _AH _BL _BH _CL _CH _DL _DH

  • 상수

상수는 프로그램이 실행되는 동안 그 값이 바뀌지 않으며 문자형, 정수형, 실수형의 세가지 종류가 있다. 상수는 다음 표와 같이 표현한다.

명칭

내용

예

문자형

상수

한 문자를 표시한다. 0~255 (0x00~0xFF)까지의 숫자표시도 가능하다

'a','A',0x55

정수형

상수

8진수,10진수,16진수의 정수를 표시한다.

0123 (8진수=0으로 시작)

123 (10진수)

0x123 (16진수= 0x로 시작)

실수형

상수

부동소수점을 표시한다.

3.1415

1.5e3 (=1500)

  • 자료형

C언어의 자료형은 데이타의 크기와 종류에 따라 5가지의 기본형으로 구성되어 있다. 참고로 자료형이 차지하는 바이트의 크기는 시스템에 따라 다를 수 있다. 아래 표는 IBM-PC/터보-C 기준이다. 표에서 나타내는 자료형은 모두 +/- 부호가 있는 것이지만 내장형제어기에서는 부호 없는 (unsigned) 형이 많이 쓰인다. 만약 변수를 부호 없는 정수로 선언하려면 unsigned int my_var; 처럼 표기하면 된다. 변수형에 unsigned가 붙어도 변수가 차지하는 메모리 공간은 변하지 않는다. 즉 int나 unsigned int 모두 2바이트의 크기이다.

자료형

내용

크기

(바이트)

범위

char

문자형

1

-128 ~ 127

int

정수형

2

-32768 ~ 32767

long

long 정수형

4

-2147483648 ~

2147483647

float

실수형

4

-3.4e38 ~ 3.4e38

double

배정도 실수

8

-3.4e4932~ 3.4e4932

C언어의 다섯 가지 기본 데이타형은 형 변경자(type modifier)를 이용하여 의미를 확장할 수 있다. 이 중 signed와 unsigned 형 변경자는 정수형 계열 데이타 타입 (소수점이 없는 숫자로 표현되는 데이타 타입-char, int, long)에만 적용할 수 있다.

형 변경자

내용

예

signed

부호 있는 정수형

signed int si;

unsigned

부호 없는 정수형

unsigned char byte;

near

근거리 포인터

int near *np;

far

원거리 포인터

int far *fp;

  • 변수명(변수이름)

프로그램 내에서 사용되는 모든 변수는 반드시 선언되어야 한다. 변수이름은 문자나 언더바(_)로 시작되며, 이름 중에 문자, 숫자, 언더바를 자유롭게 사용할 수 있다. 변수는 자료형을 쓰고 그 뒤에 변수명을 기록하는 방법으로 정의한다. 마이너스기호(-)는 변수이름 속에 포함되어서는 안된다.

int my_data; /* my_data를 정수형 변수로 선언 */

float pi; /* pi를 실수형 변수로 선언 */

변수의 초기화

변수는 선언할 때 초기화시킬 수 있다. 예를 들면 다음과 같다.

char ch='c'; /* ch를 문자형 변수로 선언하면서 c'로 초기화 */

int i=100; /* int를 정수형 변수로 선언하면서, 100으로 초기화 */

float x=1.23456; /* x를 실수형 변수로 선언하면서, 1.23456으로 초기화 */ 

  • 연산자

산술연산자

변수의 가감승제 연산에 쓰인다.

연산자

내용

예

*

곱하기

y=a*x;

/

나누기

y=a/x;

%

나머지

(정수형연산에만 사용)

y=5%3 (나머지는 2가 된다)

+

더하기

y=a+b;

-

빼기

y=a-b;

관계연산자

변수간 혹은 변수와 숫자사이의 크기비교에 쓰인다.

연산자

내용

예

==

같다

if(a==5) printf(5와 같다“);

!=

같지 않다.

if(a!=5) printf(5가 아니다“);

<

작다

if(a<5) printf(5보다 작다“);

>

크다

if(a>5) printf(5보다 크다“);

<=

작거나 같다

if(a<=5) printf(5보다 작거나 같다“);

>=

크거나 같다

if(a>=5) printf(5보다 크거나 같다“);

논리연산자

조건판단을 할 때 사용한다.

연산자

내용

예

&&

논리곱(AND)

if(a>5 && a<7) printf(a는 6이다“);

||

논리합(OR)

if(a>5 || a==5) printf(a는 5보다 크거나

같다“);

!

부정(NOT)

if(!a) printf(a는 0이다. 왜냐하면 C에서

거짓은 0, 참은 0이 아니기 때문이다“);

기타연산자

연산자

내용

예

++

1증가

a++; /* a=a+1과 동일*/

--

1감소

a--; /* a=a-1과 동일*/

?:

판단

y=(a<0) ? a : -a; /* a가 0보다 크면 y=a, 0보다 작으면 y=-a */

sizeof

데이타형의 크기

y=sizeof(int); /*정수형(int)는 2바이트 크기이므로, y는 2가된다*/

비트단위 논리연산자

일반적인 변수는 모두 바이트단위(혹은 워드단위)로 처리되지만, 내장형제어기에서는 비트단위로 변수를 조작하는 경우가 많다. 비트단위 논리연산자는 해당변수에 대해 비트단위로 연산을 수행한다.

연산자

내용

예

&

비트단위 논리곱(AND)

y = a&0x0F;

/*a의 하위 4비트만 얻는다.*/

|

비트단위 논리합(OR)

y = a|0x0F

/*a의 하위 4비트를 모두 1로 만든다*/

~

비트단위 부정(1의 보수)

y = ~0x00; /*y를 0xFF로 만든다*/

^

비트단위 배타적 OR

y = a^0xFF;

/*a중 해당비트가 1이면 0으로 만든다*/;

<<

우측으로 이동

y = 0x1<<2; /*0x1을 우측으로 2비트 이동, y=0x4가 된다*/

>>

좌측으로 이동

y = 0x2>>1; /*0x2를 좌측으로 1비트 이동,y=0x1이 된다*/

  • 제어문

if문

가장 기본적인 선택문으로 판단결과가 참인가 거짓인가에 따라 실행해야 할 문장을 선택한다. if결과 처리해야 할 문장이 단일 문장이라면 중괄호 {, } 가 필요 없지만 2개이상의 문자를 연속으로 처리해야 한다면 중괄호를 사용하여 블록화 해야 한다

    if(x>100) printf(x는 100보다 크다);

    else printf(x는 100 보다 작거나 같다“);

    if(x>=10)

      {

      printf(x는 10과 같다“);

      printf(x는 10보다 크다“);

      }

switch/case 문

비교결과에 따라 실행해야 할 코드의 종류가 여러가지일 경우 사용한다. 비교결과와 일치하는 case에 해당되는 문장을 break 키워드를 만날 때까지 실행한다. switch로 선택하고자 하는 변수는 반드시 정수형(char, int, long 등)이어야 한다.

    char ch;

    switch(ch) //ch=문자형 정수

      {

      case 'a': printf(ch는 a입니다“);

        break; /* ch=='a'의 경우 처리 종료*/

      case 'b': /* ch가 'b'일 경우 이곳으로 진입하지만 break문이 없으므로 아래의

        문장을 계속해서 수행한다 */

      case 'c': printf(ch는 b 혹은 c입니다“);

        break; /* switch문 종료 (탈출) */

      default : printf(ch는 그밖의 문자입니다“);

      break;

      }

for 문

처음 초기값을 설정한 다음 조건이 참인 동안 변화치를 수행하면서 문장을 실행한다

    /* i=0으로 초기값을 설정한 후,// sum=sum+i를 실행한다. 그후 i++ (즉,i=i+1)를 처리하고,
         비교문 (i<10)의 결과가 참일 때까지 sum=sum+i와 i++ 처리를 반복한다*/

    sum=0;
      for(i=0;i<10;i++) sum=sum+i;
      printf(0에서 9까지의 합은 %d,sum);

while 문

조건식이 참일 동안 while에 따르는 문장을 실행한다

    sum=i=0; /* sum=0; I=0;과 같다. */

    while(i<10) /* i가 10보다 작은 경우 수행한다 */

      {

      sum=sum+i;

      i++; /* i=i+1과 같다 */

      }

    printf(0에서 9까지의 합은 %d, sum);

do{ 문장 }while( 조건문 );

우선 do내부의 문장을 실행한 후 while 다음의 조건문이 참인 동안 do 문장을 실행한다. 최소 1번은 실행하는 while문이라고 볼 수 있다.

    sum=i=0; /* sum=0; i=0;을 이렇게 쓸 수 있다 */

    do

      {

      sum=sum+i;

      i++; /* i=i+1과 같다*/

      }while(i<9) /* i가 10보다 작은 경우 수행한다 */

    printf(0에서 9까지의 합은 %d, sum); 

  • 함수(function)

C언어에서의 함수는 수학에서 이야기하는 함수와는 다른 의미를 가지고 있다. C언어의 함수는 특정한 작업을 수행하도록 설계된 독립적인 프로그램을 말하는 것이니 다른 언어의 서브루틴과 같은 동작을 한다고 보면 된다. 프로그램에서 특정작업을 반복해서 수행해야 할 경우, 그 작업에 해당되는 적절한 함수를 만들어서 필요할 때마다 호출하면 프로그램을 간결하면서도 이해하기 쉽게 구성할 수 있다.

C언어에서는 모든 프로그램이 갖는 3가지 구성인 입력, 처리, 출력 부분 중에서 입력과 출력부분은 과감히 삭제하고 오로지 처리부분만으로 구성되어 있다. 입력과 출력이 필요하지 않는 프로그램은 거의 없으므로, C언어 본체만으로 원하는 일을 작성하기는 힘들게 된다. 따라서 입력과 출력부분에 해당되는 함수부터 제작해야 한다.

사실 처리부분을 제외한 모든 부분을 사용자가 준비한다는 것은 지극히 어려운 일이지만 컴파일러 제조회사에서 표준적인 런타임 라이브러리를 준비하여 입출력 부분은 물론 다양한 처리함수까지 준비하고 있다. 사용자는 이런 라이브러리를 적절히 활용하여 자신의 용도에 맞는 함수를 제작하면 된다.

런타임 라이브러리

런타임 라이브러리는 그것이 선언된 헤더파일만 소스코드에 포함시키면 간단히 사용할 수 있다. 예를 들어 자주 사용하는 출력함수인 printf()는 그것이 선언된 stdio.h 파일만 포함시키면 그 기능을 맘껏 활용할 수 있다. 런타임 라이브러리의 자세한 내용은 터보-C 관련서적을 참고하기 바란다.

    #include <stdio.h> /* printf(), scanf() 등의 함수가 선언되어 있다.*/

    int main(void)

      {

      printf( 이함수의 원형은 <stdio.h>에 선언되어 있다.“);

      }

사용자 정의 함수

런타임 라이브러리에 포함된 함수만으로 원하는 프로그램을 작성하기는 어려우므로 자신의 용도에 알맞은 함수는 스스로 작성하거나 다른 사람이 작성한 함수를 참조하여 자신의 용도에 맞게 응용해야 한다. 다음은 1에서 n까지의 정수를 더해주는 summing()함수를 작성한 예이다.

    /* 1에서 n까지의 정수를 더하여 그 결과를 되돌려 주는 함수 */

    int summing(int n)

      { /*함수 블록 시작 “{”로 시작한다*/

      int i,sum=0; /* 내부변수 선언, sum은 선언되면서 0으로 초기화된다 */

      for(i=1;i<=n;i++) sum=sum+i;

      return(sum); /* 결과 값 전달 */

      } /* 함수 끝 “}”로 끝난다 */

     

    int main(void) /* 매개변수가 없는 함수는 void 인자형으로 선언한다 */

      {

      int n=10,sum;

      sum=summing(n); /* 위에서 선언한 summing()함수를 부른다 */

      } 

  • 선행처리기

선행처리기란 프로그램이 컴파일 되기 전에 처리되는 명령어를 말한다. 여러가지가 있지만 주로 사용되는 것은 #include와 #define 두 가지이다

#define

보통 매크로(macro)라 말한다. 매크로란 일련의 문자, 상수, 수식을 하나의 식별자(문자열)로 표시하는 것으로, 정의된 후 식별자만 기술하면 컴파일 과정에서 지정한 수나 수식으로 전개된다. 매크로를 이용하면 의미전달이 어려운 숫자나 수식을 이해하기 쉬운 문자로 표기할 수 있으므로 편리하다. #define 다음에 변수의 규정에 맞는 문자열을 사용하고 대체할 문자나 숫자를 기술한다. 매크로 정의 다음에는 ; 을 사용하지 않는다.

    #define PI 3.14 /* 3.14대신 PI를 사용한다 */

    #define LCD_ON 0xF7

    #define LCD_addr 0x100 /* 0x100를 LCD_addr로 표기한다 */

    #define outLCD(data) outportb(LCD_addr,data)

    unsigned char Pdata;

    float angle;

    Pdata = LCD_ON;

    outLCD(Pdata); /* outportb(0x100,0xF7); */

    angle = 2.*PI;

#include

현재의 자리에 다른 파일을 포함하기 위해 사용한다. #include 다음에 포함하고자 하는 파일이름을 적는다. 파일이름을 감싸는 데는 < >와 “ ”를 사용하는데, 이 둘은 약간 다른 의미를 가진다. < >부호는 표준 시스템 디렉토리에서 파일을 찾고, 부호는 그 파일이 있는 디렉토리에서 파일을 탐색한다. 보통 내장 함수들이 선언된 시스템에서 제공하는 헤더 파일들은 < >로 포함시키고, 사용자가 자체적으로 제작한 헤더파일은 로 포함을 시킨다.

    #include <stdio.h> /* 시스템 디렉토리에서 stdio.h 파일을 읽어 포함시킨다 */

    #include head.h /* 현재의 디렉토리에서 head.h 파일을 읽어 포함시킨다 */

    #include c:\mydir\head.h /* c:\mydir 디렉토리에서 head.h 파일을 읽어 포함시킨다*/

typedef

typedef문은 프로그래머 자신만의 자료형을 만들어 준다. #define과 기능이 비슷하지만, 외형적으로 typedef는 자료형의 변환에만 사용할 수 있다. typedef는 자주 사용되는 자료형을 알기 쉽게 표현하게 하고, 다른 기계에 프로그램의 이식을 쉽게 하도록 도와준다. typedef 다음 원래의 자료형과 바꾸고자하는 자료형을 기술하여 사용한다. #define과 달리 #으로 시작하지 않고, 끝부분에 ;를 사용한다는 것에 주의한다.

    typedef unsigned char BYTE; /* unsigned char형을 BYTE로 바꾼다 */

    BYTE MSB; /* unsigned char MSB; 와 동일한 의미를 갖는다 */

  • struct와 union

C언어에서 struct(구조체)와 union(공용체) 키워드를 이용하여 다섯가지 기본 데이타 타입(char, int, float, double, void)이 조합된 새로운 데이타 타입을 만들 수 있다. struct는 하나의 변수명이 기억장소가 다른 여러 개의 변수집합을 대신해주며, union은 2개 이상의 변수가 동일한 기억장소(메모리)를 공유하게 한다.

struct

struct는 다음과 같이 정의한다

    struct 구조체명{

    항목1;

    항목2;

    :

    }구조체변수명;

앞서 정의된 구조체형의 새로운 구조체변수는 다음과 같이 정의한다.

struct 구조체명 구조체변수명;

구조체변수명 바로뒤에 '.'을 첨가한 후 항목명을 연이어 기록하면 외부에서 접근할 수 있는 변수명이 된다. 이때 사용하는 '.'을 항목연산자(membership operator)라고 한다. 구조체형 포인터변수는 '->'라는 기호로 항목을 표시한다.

    struct SPOINT{

      int x; /* 2바이트 크기의 항목 */

      char y; /* 1바이트 크기의 항목 */

    }sp1; /* x,y라는 2개의 데이타타입으로 구성된 SPOINT라는 struct (구조체) 와 그 구조체를 사용한 변수 sp1을 선언한다. 여기서 x,y는 모두 다른 메모리번지를 가지게 된다. SPOINT 구조체의 크기는 2바이트 (x)+1바이트(y)=3바이트가 된다. */

    struct SPOINT sp2; /* 앞서 정의한 SPOINT라는 구조체형 변수 sp2를 선언한다*/

    sp1.x = 10; /* sp1 구조체변수의 x항목에 10을 대입한다. sp1.y에는 영향을 주지 않는다. */

    sp2.y = 20; /* sp2 구조체변수의 y항목에 20을 대입한다. sp2.x에는 영향을 미치지 않는다.*/

     

union

union(공용체)는 struct(구조체)와 비슷하게 정의한다.

    union 공용체명{

      항목1;

      항목2;

      :

    }공용체명;

     

앞서 정의된 공용체형의 새로운 공용체변수는 다음과 같이 정의한다.

    struct 공용체명 공용체변수명; 

공용체 항목을 지시하기 위해서는 공용체변수명 바로뒤에 ‘.’을 첨가한 후 항목명을 연이어 기록하면 된다.

    union UPOINT{

      int x; /* 2바이트 크기의 항목 */

      char y; /* 1바이트 크기의 항목 */

      }up1; /* x,y라는 2개의 데이타타입으로 구성된 UPOINT라는 union (공용체)와 그 공용체를 사용한 변수 up1을 선언한다. 여기서 x,y는 같은 메모리 번지를 가지게 된다. 따라서 up1.x를 변화시키면 up1.y 부

      분도 up1.x가 차지하는 메모리 공간만큼 변하는 효과를 가진다. UPOINT 공용체의 크기는 가장 크기가 큰 항목의 크기가 되므로 이 경우 2바이트(x의 크기)가 된다.*/

    union UPOINT up2; /* 앞서 정의한 UPOINT라는 공용체형 변수 up2를 선언한다*/

    up1.x = 0x1234; /* up1 공용체변수의 x항목에 0x1234를 대입한다. up1.y는

    up1.x와 같은 메모리 번지를 공유하고, 1바이트의 크기

    이므로 up1.x의 하위 1바이트인 0x34가 된다.*/

    up2.y = 0x56; /* up2 공용체변수의 y항목에 0x34를 대입한다. up2.x는 up2.y와 동일한 메모리 번지를 공유하고, 2바이트의 크기이므로 up2.x의 하위 1바이트는 0x56이 된다. up2.x의 상위 바이트는 원래의 값과 동일하다.*/

  • 배열과 포인터

배열

배열은 똑같은 자료형을 갖는 원소(변수)의 집합을 말한다. 원소의 번호는 1에서 시작하지 않고, 0부터 시작한다. C 컴파일러는 배열의 크기를 검사하지 않으므로, 배열의 크기 이상의 데이타에 접근하면 예기치 않은 에러가 발생하므로 주의해야 한다.

    int aa[10]; /* aa[0],aa[1],aa[2]... aa[9] */

    float ff[5]; /* ff[0], ff[1],ff[2],ff[3],ff[4] */

    aa[9]= 111; /*정상적인 동작*/

    aa[20]= 123; /* aa[10]이상은 쓰기 금지, 배열 aa의 크기는 10이므로 허가된 원소번호는 aa[0]부터 aa[9]까지다. 배열의 크기를 넘어선 aa[20]에는 다른 데이타 혹은 실행코드가 쓰여있을 수도 있으므로 동작시 예기치 않는 에러가 발생한다*/

포인터

일반적인 변수를 다룰 때, 사용자는 변수의 내용에만 관심을 가지면 되고, 그 변수가 차지하는 메모리의 번지를 의식할 필요는 없다. C언어에서 변수가 정의되면 그 변수에 읽고 쓰여지는 데이타를 보존하기 위해 메모리의 특정번지에 변수가 위치하게 된다. 에러 없이 작성된 프로그램에 대해 컴파일러는 변수나 실행코드와 충돌하지 않도록 적절한 메모리 번지에 변수를 할당하므로 사용자는 변수가 위치한 메모리의 위치에 관심을 가지지 않아도 된다.

마이크로프로세서를 이용한 내장형제어기(Embedded Controller)는 비교적 적은 양의 메모리 위에 실행 코드와 데이타를 담게 되므로 풍부한 메모리를 갖는 IBM-PC 환경에 비해 변수와 메모리의 관계에 대해 섬세한 접근이 필요하다.

일반변수는 조작할 대상이 변수의 값인 반면, 포인터는 다루는 대상이 메모리 번지라는 점이 특이하다. 포인터 관련 연산자는 *과 &가 있다. *는 포인터변수를 선언하거나 포인터변수의 내용에 접근할 때 사용하고 &는 일반변수의 메모리번지를 알아낼 때 사용한다.

    char ch; /* ch는 문자형 변수 (크기는 1바이트=8비트) */

    int i; /* i는 정수형 변수 (크기는 2바이트=16비트) */

    int *pch; /* *pch는 문자형 변수에 대한 포인터 */

    int *pi; /* *pi는 정수형 변수에 대한 포인터 */

    .

    ch= 0x12; /* 문자형 변수 ch에 1바이트의 데이타 0x12를 기록한다 */

    pch = &ch; /* 문자형 변수 ch의 메모리 번지를 pch에 대입한다.*/

    pch=0x1000; /* pch가 메모리 번지 0x1000 (16진수)를 지시하게 한다 */

    ch = *pch; /* pch가 지시하는 메모리(0x1000번지)에서 1바이트(8비트)데이타를 꺼내어 ch에 대입한다. *pch연산 결과가 몇 바이트의 데이타를 꺼낼 것인가 하는 결정은 pch를 정의할 때 사용한 데이타 타입에 의해 결정된다. 앞의 pch정의에서 char *pch; 와 같이 *pch의 데이타 타입을 char(1바이트)로 정의했으므로 *pch에서 얻어지는 데이타는 1바이트가 된다. */

    i= 0x1234; /* 정수형 변수에 2바이트의 데이타(0x1234)를 기록한다 */

    pi= &i; /* pi에 정수형 변수 i의 메모리 번지를 넣는다 */

    pi=0x2000; /* pi가 메모리 번지 0x1000 (16진수)를 지시하게 한다 */

    i = *pi; /* pi가 지시하는 메모리(0x1000번지)에서 2바이트(16비트)데이타를 꺼내어 i에 대입한다. *pi 연산 결과가 몇 바이트의 데이타를 꺼낼 것인가 하는 결정은 pi를 정의할 때 사용한 데이타 타입에 의해 결정된다. 앞의 pi정의에서 int *pi; 와 같이 *pi 데이타 타입을 int(2바이트)로 정의했으므로 *pi 연산으로 얻어지는 데이타는 2바이트가 된다. */

포인터는 메모리의 어드레스 버스라 생각할 수 있다. 포인터 연산자 (*)는 포인터가 지시하는 어드레스 번지에 해당되는 데이타 값을 얻는 기능이라 생각할 수 있다. 동일한 어드레스를 지정해도 8비트 폭의 메모리에서는 8비트 데이타가, 16비트 폭의 어드레스에서는 16비트 데이타를 얻을 수 있듯이, 문자형 포인터 (char *pch)에 대한 포인터 연산 (*pch)의 결과는 8비트(1바이트)값이 출력되고, 정수형 포인터( int *pi)에 대한 포인터 연산 (*pi)의 결과는 16비트(2바이트) 값이 출력된다.

터보-C의 near 와 far 포인터

V25, 386EX는 8086과 소프트웨어적으로 호환되는 칩이므로 메모리를 80x86과 마찬가지로 세그먼트(16비트):옵셋(16비트)의 형태로 다루게 된다. 세그먼트와 옵셋의 값을 자유롭게 바꿔 가면서 접근할 수 있는 메모리 공간은 총 1메가 바이트이고, 세그먼트가 정해져 있을 때 옵셋의 값만 변화시켜가면서 다룰 수 있는 메모리 공간은 64K 바이트가 된다.

터보-C를 스몰모델(small model)로 컴파일 했을 때, 데이타의 번지는 DS(데이타 세그먼트)를 기준으로 옵셋만 변화하게 되어 64K 바이트의 메모리 공간에 할당된다. DS의 값은 프로그램을 메모리에 이식할 때 결정한다. 스몰모델에서도 DS의 값에 의존하지 않고 1메가 바이트 메모리 전체 영역을 다뤄야 할 때가 있다. 이와 같이 64K 혹은 1메가 바이트의 메모리를 다룰 수 있도록 하기 위해 터보-C에서는 포인터 형을 정의하는 키워드로 near와 far를 준비하고 있다.

near 포인터는 64KB용, far 포인터는 1메가 바이트 용으로 다음과 같이 사용한다.

    int far *fip; /* fip는 1메가 바이트 메모리 영역용(far) 포인터 */

    int near *nip; /* nip는 64KB 메모리 영역용(near) 포인터 */ 

터보-C 스몰모델에서는 코드와 데이타의 기본형을 모두 near형으로 가정하기 때문에, 스몰모델에서 64KB의 메모리 영역을 다룰 때 near라는 키워드는 사용하지 않아도 무방하다. far형 포인터에 메모리번지를 대입할 때는 long형 정수를 사용한다. long형 정수(4바이트)중 앞의 2바이트는 세그먼트, 뒤쪽의 2바이트는 옵셋을 나타낸다.

    int i; /* 정수형 (2바이트) 변수 i*/

    int far *fip; /* fip는 1메가 바이트 메모리 영역용(far) 포인터 */

    int near *nip; /* nip는 64KB 메모리 영역용(near) 포인터, 스몰모델에서 int *nip와 동일한 역할을 한다*/

    nip=0x1000; /* nip는 DS(데이타 세그먼트)를 기준으로 한 0x1000의 옵셋번지(DS:0x1000)를 나타낸다. => 64KB 메모리 영역*/

    fip=0x20001234L; /*fip는 2바이트 세그먼트(0x2000)와 2바이트 옵셋(0x1234)의 메모리 번지(0x2000:0x1234)를 나타낸다=>1메가 바이트 메모리 영역*/

    *nip=0x5555; /* nip의 메모리 번지( DS:0x1000 번지)에 0x5555의 데이타를 기록한다*/

    i = *nip; /* nip의 메모리 번지(DS:0x1000 번지)의 내용(=0x5555)를 i에 대입한다*/

    *fip=i; /* i의 내용(=0x5555)를 fip의 메모리 번지(0x2000:0x1234)에 기록한다*/

배열과 포인터의 관계

배열(array)의 이름은 첫번째 원소의 주소를 나타낸다.

    int aa[10];

로 정의했을 때, &aa[0] (즉 aa[0]의 메모리 번지)와 aa는 같은 값이 된다.

    int i;

    int *pi;

    int aa[10];

    pi=aa; /* pi에 aa배열 첫번째 원소의 메모리 번지를 대입

    이것은 pi=&aa[0]과 동일하다 */

    *pi=0x1234; /* 이것은 aa[0]=0x1234와 동일하다 */

    *(pi+1)=0x3456; /* 이것은 aa[1]=0x3456과 동일하다 */ 

  • 전역변수와 지역변수

지역변수는 그것을 선언한 함수 내부에서만 인식되는 변수이다. 이와 대조적으로 전역변수는 함수외부에서 선언된 변수로 그것을 선언하지 않은 함수 내부에서도 인식되는 변수를 말한다. 만약 어떤 함수에서 전역변수와 같은 이름으로 지역변수를 선언할 경우 그 함수 내에서는 지역변수가 우선한다. 즉 전역변수와 지역변수의 이름이 같은 경우 전역변수는 무시되고 지역변수만이 영향을 발휘한다. 전역변수는 고정적인 메모리로 할당받지만, 지역변수는 스택에 포함되어 함수가 불릴 때에 할당되고, 함수를 끝낼 때 사라진다

    int global;/*전역변수. 모든 함수에서 인식된다*/

    int test1(void)

      {

      int local; /*함수 내부에서 선언하였으므로 지역변수, 지역변수는 이 함수

      내부에서만 인식된다.*/

      local = 1;

      global=2; /*전역변수이므로, 선언하지 않은 함수 내부에서도 인식된다.*/

      }

    int test2(void)

      {

      int local; /* 지역변수. 이 지역변수는 함수 test1()의 지역변수인 local과는

      아무런 관계가 없다*/

      int global; /*함수 내부에서 선언하였으므로 지역변수, 전역변수인 global과

      이름이 같으므로 전역변수 global은 무시되고, 지역변수인 global

      만이 인식된다.*/

      local = 1;

      global=2; /*전역변수인 global과는 아무런 관계가 없고 지역변수인 global로

      동작한다.*/

      }

static 변수

함수 내부에서 선언된 static 변수는 그 함수 안에서만 인식된다는 점에서는 것은 일반적인 지역변수와 동일하다. 그러나 함수 내의 일반 지역변수와는 달리 static 변수는 일정한 메모리를 할당받기 때문에 함수를 종료해도 사라지지 않고 계속 그 값을 유지한다.

    void test(void)

      {

      int local=10; /* 지역변수. 함수에 진입할 때마다 10으로 초기화되고, 함수를 종료하면 사라진다 */

      static int slocal=10; /* static형 변수, 함수를 처음 부를 때만 10으로 초기화되고 함수를 종료해도 그 값을 계속 유지한다*/

      local++; /* 연산결과는 언제나 11이 된다*/

      slocal++; /* 연산결과는 함수를 부를 때마다 증가한다 */

      }

    void main(void)

      {

      test(); /* 함수를 처음 부를 때 test()함수의 slocal의 값이 11로 초기화되고 slocal++ 연산결과 slocal의 값은 11가 된다*/

      test(); /* 다시한번 test()함수를 부를 때 slocal의 값은 11로 유지하고 있으므로 slocal++ 연산 결과 slocal의 값은 12가 된다 */

      }

  • 비트단위 제어

주변기기를 제어하다 보면 바이트 단위보다는 비트단위가 편리할 때가 많다. 예를 들어 스텝모터(STEPPING MOTOR) 제어에서 비트신호를 어떻게 조합하느냐에 따라 회전방향이나 구동방법이 달라지게 된다. C언어에는 비트단위의 논리연산과 비트단위 제어를 위한 준비가 되어있다.

비트단위 논리 연산자

연산자

내용

예

&

비트단위 논리곱(AND)

y = a&0x0F; /*a의 하위 4비트만 얻는다.*/

|

비트단위 논리합(OR)

y = a|0x0F /*a의 하위 4비트를 모두 1로 만든다*/

~

비트단위 부정(1의 보수)

y = ~0x00; /*y를 0xFF로 만든다*/

^

비트단위 배타적 OR

y = a^0xFF; /*a중 해당비트가

1이면 0으로 만든다*/;

<<

우측으로 이동

y = 0x1<<2; /*0x1을 우측으로 2비트 이동, y=0x4가 된다*/

>>

좌측으로 이동

y = 0x2>>1; /*0x2를 좌측으로 1비트 이동,y=0x1이 된다*/

비트필드(BIT FIELD)

비트필드는 정수형 변수를 비트 단위로 쪼개어 각 비트(또는 복수의 비트)에 해당하는 필드를 제어하도록 만든 변수형으로 다음과 같이 정의한다.

    sturct 구조체명{

    unsigned 필드1:비트폭;

    unsigned 필드2:비트폭;

    :

    }변수명;

각 필드에 대한 접근은 구조체(struct)와 동일하게 ‘.’ 연산자를 이용한다.

    struct BITS{

    unsigned B0:1; /*LSB*/

    unsigned B1:1;

    unsigned B2:1;

    unsigned B3:1;

    unsigned B4:1;

    unsigned B5:1;

    unsigned B6:1;

    unsigned B7:1; /*MSB*/

    }b; /*총 8비트(1바이트)의 비트필드를 b라는 변수에 할당*/

    b.B0 = 1; /* b변수의 첫번째 비트(LSB)를 1로 만든다*/

    b.B6 = 0; /* b변수의 7번째 비트를 0으로 만든다.*/ 

  • 터보-C의 하드웨어 관련 함수 요약

disable() 함수

80x86 CPU의 하드웨어 인터럽트를 금지시킨다. 이 함수는 어셈블러 CLI(clear interrupt)와 동일한 효과를 낸다. disable()함수는 80x86 CPU 코어에게 인터럽트 금지 명령을 내리는 것이고, 주변 하드웨어에 직접적인 역할을 수행하지는 않는다. IBM-PC에서 인터럽트와 관련된 하드웨어 부품은 80x86 CPU와 8259A PIC(프로그래머블 인터럽트 제어기)이다.

386EX 프로세서에는 80x86과 소프트웨어적으로 호환되는 386SX CPU코어 이외에도 하드웨어 인터럽트를 관장하는 인터럽트 제어기인 8259A가 내장되어 있다. TB-386EX 보드에서는 하드웨어 인터럽트 제어기가 IBM-PC/AT와 동일하게 2개로 구성되어 있으며, 하나는 마스터 8259A로 또 하나는 슬레이브 8259A로 설정되어 있다. 모든 하드웨어 인터럽트는 마스터 8259A의 출력핀을 통해 CPU 코어(386SX)에 연결되는데, enable()과 disable() 함수는 마스터 8259A의 출력핀과 CPU 코어의 연결 여부를 결정한다.

8259A 입력핀에 요구한 인터럽트 (IRQ0,IRQ1..)는 8259A의 내부 레지스터에 의해 인터럽트 우선순위, 금지/허가 여부가 처리된 후 인터럽트를 발생할 조건이 되면 8259A가 80x86 CPU에 인터럽트를 걸게 된다. 이 때 8259A로부터 요구된 인터럽트를 금지하는 것이 바로 disable()함수의 역할이다.

enable() 함수

80x86 CPU의 하드웨어 인터럽트를 허가한다. 이 함수는 disable()함수와 반대되는 동작을 하며, 다음과 같이 정의되어 있다.

    #include <dos.h>

    void enable(void);

    void disable(void);

FP_SEG(), FP_OFF()

far 포인터 (1MB 어드레스 공간을 지시하는 포인터)의 세그먼트와 옵셋을 얻는 매크로 함수. far 포인터는 4바이트로 구성되어 있으며, 위쪽의 2바이트는 세그먼트를 나타내고 아래쪽의 2바이트는 옵셋을 나타낸다.

    unsigned int seg,off;

    void far* fp=0x12345678L; /* far 포인터 fp를 정의하고 fp에 0x12345678 즉,

    fp = 0x1234:0x3456 로 만든다.*/

    seg=FP_SEG(fp); /* fp의 세그먼트(=0x1234)를 얻는다 */

    off=FP_OFF(fp); /* fp의 옵셋(=0x3456)을 얻는다*/ 

peek(), peekb(), poke(), pokeb()

주어진 세그먼트/옵셋이 지시하는 메모리에 데이타를 읽고/쓰는 함수이다.

    unsigned char byte;

    unsigned int word;

    unsigned int seg=0x1234,off=0x3456;

    byte = peekb(seg,off); /* 메모리의 seg:off에서 1 바이트를 읽는다 */

    word = peek(seg,off); /* 메모리의 seg:off에서 2 바이트(=1워드)를 읽는다 */

    pokeb(seg,off,byte); /* 메모리의 seg:off에서 1 바이트 데이타(byte)를 쓴다 */

    poke(seg,off,word); /* 메모리의 seg:off에서 2 바이트 데이타(word)를 쓴다 */ 

inport(), inportb(), outport(), outportb()

V25, 386EX 프로세서는 80x86과 마찬가지로 메모리 맵과 IO맵이 구별되어 있다. peek(), poke() 함수가 메모리를 다루는 함수인 반면, inport(), outport()는 IO포트로부터 데이타를 주고받는 역할을 한다.

    unsigned char byte;

    unsigned int word;

    unsigned int port=0x100;

    byte = inportb(port); /*IO포트의 port번지(=0x100)에서 1 바이트를 읽는다*/

    word = inport(port); /*IO포트의 port번지(=0x100)에서 2 바이트를 읽는다*/

    outportb(port,byte); /*IO포트의 port번지(=0x100)에 1 바이트(=byte)를 쓴다*/

    outport(port,word); /*IO포트의 port번지(=0x100)에 2 바이트(=word)를 쓴다*/

setvect(), getvect()

내장형제어기를 꾸미다 보면 자신만의 인터럽트 루틴을 다루어야 할 경우가 생긴다. 주기적으로 키보드를 검사해서 무슨 키가 눌려졌나를 알아낸다던가, 외부에서 인터럽트 요구가 왔을 때, 적절한 응답을 해주는 경우에 사용하는 함수다. 이 함수에서는 함수 포인터를 사용하는데, 함수 포인터에 친숙하지 않는 사람은 예제를 공식처럼 외워서 쓰고, 기회가 있을 때 자세한 원리를 파악해도 지장이 없을 것이다.

새롭게 설치할 인터럽트는 void interrupt 라는 터보-C의 키워드로 시작해야 한다. 이 예제는 타이머 인터럽트(인터럽트 벡터번호 0x1Ch)를 새로 만들어 타이머 인터럽트가 걸릴 때마다 timer_count라는 변수를 1씩 증가시키는 동작을 하는 것이다.

    /* 새로운 타이머 인터럽트 루틴을 만든다 */

    /* TB-386EX 보드의 타이머 인터럽트(int 1Ch)는 하드웨어 인터럽트이며 10ms마다 인터럽트가 발생한다 */

    /* 새롭게 작성한 인터럽트 루틴 new_int1Ch */

    int timer_count=0; /* 전역변수를 선언한다.*/

    void interrupt new_int1Ch(void)

      {

      enable(); /* 또 다른 인터럽트를 허가한다 */

      timer_count++; /* 인터럽트가 걸릴 때마다 timer_count값을 증가시킨다 */

      }

    void my_function(void)

      {

      for(;;) /* 무한 루프를 돈다*/

      if(timer_count>1000) break; /* timer_count가 1000보다 크면 for문을 탈출한다. */

      }

    void main(void)

      {

      void interrupt (*old_int1Ch)(); /* 인터럽트함수 포인터 */

      old_int1Ch = getvect(0x1C); /* 인터럽트 0x1C 서비스루틴의 번지를 old_int1Ch에 저장한다*/

      setvect(0x1C,new_int1Ch); /* 새로운 인터럽트(new_int1Ch)를 설치한다 */

      my_function(); /* 일반 함수를 실행한다 */

      setvect(0x1C,old_int1Ch); /* 모든 일이 종료되면 원래의 인터럽트(old_int1Ch)를 인터럽트번호 0x1Ch로 복구한다 */

      }

int86()

80x86 CPU에 소프트웨어 인터럽트를 발생시키는 함수이다. 레지스터에 적당한 값을 넣고, 인터럽트를 걸어서, 인터럽트루틴에서 처리한 레지스터의 값을 얻을 수 있다. 이 함수에서는 터보-C에서 제공하는 REGS라는 공용체(union)와 이 공용체의 항목인 두개의 구조체 WORDREGS, BYTEREGS를 사용하여 레지스터 값을 전달한다.

    struct WORDREGS {

      unsigned int ax, bx, cx, dx, si, di, cflag, flags;

      }; /* 8개의 워드(2바이트) 단위 레지스터를 항목으로한 구조체*/

    struct BYTEREGS {

      unsigned char al, ah, bl, bh, cl, ch, dl, dh;

      }; /* 8개의 바이트단위 레지스터를 항목으로 한 구조체*/

    unionREGS{

      structWORDREGS x; /*레지스터를 워드 단위로 조작*/

      structBYTEREGS h; /*레지스터를 바이트 단위로 조작*/

      }; 

REGS 공용체의 항목은 8워드(16바이트) 크기의 WORDREGS와 4워드(8바이트) 크기의 BYTEREGS이고, WORDREGS와 BYTEREGS가 겹치는 아래쪽 4워드는 WORDREGS의 항목과 BYTEREGS의 항목으로 자유롭게 접근할 수 있다. 즉 REGS 형의 변수 r을 다음과 같이 선언하면 80x86의 CPU 레지스터 AX를 다음과 같이 조작할 수 있다. 참고로 80x86의 AX레지스터는 16비트 레지스터이며 상위 8비트를 AH, 하위 8비트를 AL로 표기한다. 80x86의 BX, CX, DX 레지스터도 같은 방법을 적용한다.

    REGS r;

    r.x.ax=0x1234 /* 의사적으로 80x86 CPU의 AX 레지스터에 0x1234를 할당한다.*/

    /* AX 레지스터에 대한 워드단위 처리는 다음과 같이 바이트 단위로 해도

    동일한 결과를 낸다*/

    r.h.ah=0x12; /* 의사적으로 AH에 0x12를 할당 */

    r.h.al=0x34; /* 의사적으로 AL에 0X34를 할당 */

int86()함수를 사용한 예를 본다.

    REGS rin,rout;

    rin.h.ah = 0x0E;

    rin.h.al = 'S';

    int86(0x10,&rin,&rout); /* 인터럽트 0x10h (비디오 인터럽트)를 건다. 이때 AH=0x0E, AL='S'의 값을 넣는다. 처리결과는 rout에 실려 나온다 */



첨언 달기
이름 : 암호 :
번호    제목...(첨언) 작성일 접속
xxx [공지]자유겔러리 등록 오류 수정 완료 2017-12-22 685027
xxx [공지]자료실 다운로드 오류 처리 2017-12-01 30927
xxx [공지]비난, 광고 게시물을 신고, 차단, 삭제됩니다. 2005-06-26 26725
70 [CPP]  C++ "+=" 연산자 중 두 개 이상이 이 피연산자와 일치합니다. 2017-09-28(목) 9508
69 [일반]  윈도우 한영전환 키를 ALT로 인식하여 한영전환 안될때 2017-07-05(수) 3902
68 [일반]  (Windows 7) 흔들어 전체 창 최소화(Aero Gesture) 기능 끄기 2017-07-02(일) 4094
67 [XML]  페이스북 공유시 나오는 정보 수정 방법 2017-02-05(일) 3837
65 [일반]  WUAUSERV 프로세서로 cpu 부하가 오를경우 2016-10-14(금) 4540
64 [일반]  window7 네트워크 드라이브 연결 후 재부팅시 계속 계정 물어보는경우 2016-09-03(토) 4146
63 [일반]  qhd이상 dp나 hdmi 해상도나 아이콘 쏠림 버그 2016-07-05(화) 7898
62 [일반]  다음 팟 를레이어 오디오-비디오 스킨 별도 2015-10-25(일) 4026
61 [일반]  윈도우 8_8.1_10 관리자(Administrator) 계정으로 앱 실행방법 2015-05-14(목) 4665
60 [일반]  Windows 7에서 thumbs.db 파일이 생성되지 않도록 설정하기 2015-05-12(화) 16735
59 [일반]  CAM350으로 2개의 거버를 하나로 합치기 2014-10-17(금) 10858
58 [일반]  화일검색시 용량별 기타 조건별 2014-06-13(금) 4607
57 [일반]  IE 8에서 세션 공유 안하는 방법 2014-04-08(화) 4581
56 [일반]  GMT(UTC) Time Zone 2013-06-23(일) 9449
55 [일반]  전선 AWG별 거리저항 2012-06-28(목) 10858
50 [일반]  16700 시리즈 로직 분석기가 제대로 부팅되지 않아 재설정 2012-06-02(토) 5062
49 [일반]  C C++ 연산 우선순위 ..[1] 2012-02-16(목) 4918
48 [일반]  그리스(희랍) 문자 2011-02-10(목) 7445
47 [일반]  ASCII 5 By 7 Font 2011-01-30(일) 6033
46 [일반]  PCB 용어 정리 2011-01-15(토) 42164
검색 :
Page : 이전 1  2  3  4  5  6  7  8  9  10  11  12  13  다음




Copyright 2003-2026 ⓒ 김종호. All rights reserved. kimjongho.co.kr kimjongho.kr kimjongho.net kimjongho.com