2015년 7월 29일 수요일

20150730 파일의 분할과 헤더파일의 디자인

☆구조체의 선언및 정의는 헤더파일에 삽입하는 것이 좋다. ★중복은 안됨!★
구조체를 헤더파일에 사용하면 구조체는 실행파일의 내용에 직접적인 연관이 있는
정보이기에 두 번 이상 중복될 수 없다!

★조건부 컴파일을 활용한 헤더파일 중복삽입 문제의 해결★
_를 2개를 붙이는 이유는 어셈블리와 겹치지 않게 하기 위해서
#ifndef __파일이름_확장자명__  //파일이름과 확장자는 대문자로 작성
#define __파일이름_확장자명__ //파일이름과 확장자는 대문자로 작성
//내용 #include "*.*" \n Div IntDiv(int num1, int num2)
#endif // __파일이름_확장자명__ = #endif이 누구를 가리키는지를 알기위해서 주석을 사용

2015년 7월 28일 화요일

20150729 파일의 분할과 헤더파일의 디자인(메인함수==entry point)

★★★분할 컴파일★★★
분할된 파일들을 cl /c 파일명으로 각각 컴파일하면 *.obj파일들이 생성되는데
이 *.obj 파일을 cl 파일명(파일명.exe 파일생성됨) 파일명~파일명으로 링크 단계를
완성시키면 정상적으로 실행되는 실행파일이 생성된다!
☆cl /c main.c -> cl /c func.c -> cl /c num.c -> cl main.obj func.obj num.obj -> main.exe

☆외부에 선언 및 정의되었다고 컴파일러에게 알려줘야 한다!
extern int num; // int형 변수 num이 외부에 선언되어 있다!
extern Increment(void); // void Increment(void) 함수가 외부에 정의되어 있다!
☆함수가 외부에 정의되어 있음을 알릴 때는 extern 선언을 생략가능

★다른 파일에서 접근을 못하게 하고 싶다면 static!(전역변수의 static선언은 아래의 의미)
"이 변수는 외부 파일에서의 접근을 허용하지 않는다!"(보안 속성)
"이 변수의 접근 범위는 파일 내부로 제한한다!"
보안상의 이유와 프로젝트 분할 작업시 동일한 변수명이 있을때를 위해서 사용한다!
★함수에도 사용가능!!★

★★★헤더파일을 include 하는 두 가지 방법★★★
#include <헤더파일 이름> // 첫 번째 방식
#include "헤더파일 이름" // 두 번째 방식

☆이 둘의 유일한 차이점은 포함시킬 헤더파일의 기본 경로인데, 첫 번째 방식을 사용하면
표준 헤더파일(C의 표준에서 정의하고 있는, 기본적으로 제공되는 헤더파일)이 저장되어
있는 디렉터리에서 파일을 찾게 된다! 때문에 이 방식은 stdio.h, stdlib.h, 그리고 string.h
와 같은 표준 헤더파일을 포함시킬 경우에 사용된다!
☆반면 두 번째 방식을 사용하면, 이 문장을 포함하는 소스파일이 저장된 디렉터리에서
헤더파일을 찾는다! 때문에 프로그래머가 정의하는 헤더파일을 포함시킬 때 사용하는 방식!
그리고 이 방식을 사용하면 다음과 같이 헤더파일의 이름뿐만 아니라, 드라이브 명과
디렉터리 경로를 포함하는 '절대경로(완전경로)'를 명시 해서 헤더파일을 지정할 수 있다!
#include "C:\CPower\MyProject\header.h" // Windows 상에서의 절대경로 지정
#include "/CPower/MyProject./header.h" // Linux 상에서의 절대경로 지정
☆dos상에서 \=절대경로(cd \programfiles\atmel에서 첫번째 \는 최상위 위치)
☆.\..\ //\앞에 점=상대경로(첫번째 점하나는 현재위치 두번째 점 두개는 상위 위치)

☆상대경로의 지정 방법
드라이브 명과 디렉터리 경로를 포함하는 방식을 가리켜 '절대경로'라 하는 이유는
"절대로 경로가 변경되지 않는다. 컴퓨터를 옮겨도 지정한 경로는 변경되지 않는다!"
상대경로는 실행하는 컴퓨터의 환경에 따라서 경로가 바뀌기 때문!
#include "Release\header0.h" /* 소스파일이 있는 디렉터리의 하위 디렉터리인 Release
디렉터리에 존재하는 header0.h를 포함하라! */
#include "..\CProg\header1.h" /* 한 단계 상위 디렉터리의 하위 디렉터리인 CProg에
존재하는 header1.h를 포함하라! */
#include "..\..\MyHeader\header2.h" /* 두 단계 상위 디렉터리의 하위 디렉터리인
MyHeader에 존재하는 header2.h를 포함하라! */

2015년 7월 27일 월요일

20150728 선행처리기와 매크로

선행처리는 컴파일 이전의 처리를 의미

컴파일의 5단계 참조 사이트 http://www.hanbit.co.kr/network/view.html?bi_id=1013
컴파일 세부 명령어 참조 사이트
https://msdn.microsoft.com/ko-kr/library/9s7c9wdw(v=vs.120).aspx

전처리 단계 = cl /P 파일 // i파일 생성

★대표적인 선행처리 명령문
#define : Object-like macro // 오브젝트(object) 유사 매크로(#define PI 3.14 == PI=3.14)

★매크로 함수의 장점
☆일반 함수에 비해 실행속도가 빠르다!
☆자료형에 따라서 별도로 함수를 정의하지 않아도 된다!

아래의 일반적인 함수를 호출시 동반되는 사항들과 다르게 매크로 함수의 몸체부분이
매크로 함수의 호출 문장을 대신하기 때문에 실행속도상의 이점이 있다.
// 호출된 함수를 위하 스택 메모리의 할당
// 실행위치의 이동과 매개변수로의 인자 전달
// return 문에 의한 값의 반환

★매크로 함수의 단점
☆정의하기가 정말로 까다롭다!
☆디버깅하기가 쉽지 않다!

★매크로 함수의 장단점에 의해서 이러한 함수들을 매크로로 정의한다
☆작은 크기의 함수
☆호출의 빈도수가 높은 함수

매크로는 매개변수가 존재하는 형태로도 정의할수 있다!함수와 유사한 매크로=매크로함수
#define : Function-like macro // #define SQUARE(X) X*X == SQUARE(123); == 123*123;

★매크로 몸체에 괄호를 아래처럼 최대한 사용
#define SQUARE(X) ((X)*(X))

☆매크로를 두 줄 이상에 걸쳐서 정의할 때에는 아래와 같이 \ 문자를 활용!
#define SQUARE(X) \
           ((X)*(X))

☆매크로 정의 시, 먼저 정의된 매크로도 사용이 가능!
#define PI 3.14
#define PRODUCT(X, Y) ((X)*(Y))
#define CIRCLE_AREA(R) (PRODUCT((R), (R))*PI)

조건부 컴파일(Conditional Compilation)을 위한 매크로
#if... #endif : 참이라면 코드 삽입
#ifdef... #endif : 정의되었다면 #ifdef는 매크로가 정의되었는지를 기준으로 동작
#ifndef... #endif : 정의되지 않았다면
#else 위의 3개에 추가 가능
#elif의 삽입 : #if에만 해당

★문자열 안에서는 매크로의 매개변수 치환이 발생하지 않는다!
#define STR(ABC) #ABC // 매개변수 ABC에 전달되는 인자를 문자열 ABC로 치환해라!

☆특별한 매크로 연산자 없이 단순히 연결하는 것은 불가능하다!

★필요한 형태대로 단순하게 결합하기 : 매크로 ## 연산자
#define CON(UPP, LOW) UPP ## 00 ## LOW // int A=CON(22, 77); == int A=220077

20150728 메모리관리와 메모리의 동적 할당(Dynamic Memory Allocation)

컴파일러마다 메모리 구성이 다름

★heap영역은 운영체제(OS)가 제공하고 프로그램이 요구하는 메모리의 양만큼 할당
프로그램이 종료시 메모리를 돌려주기 전까지 다른 프로그램이 사용 못하도록 락걸림!
요즘에는 프로그램이 종료되면 운영체제에 의해서 전부해제되나 안되는것도 있다!

★운영체제가 heap영역메모리를 프로그램에 할당할때 처음 한번만 할당된 메모리 주소를
가르쳐주기에 포인터로 받아온 주소를 가리킬때 이 주소를 잃어버리지 않도록 해야한다!

☆Static(정적)-heap영역만 Dynamic(동적)
☆힙 영역의 메모리 공간 할당과 해제 : malloc(memory allocation)과 free 함수
void *malloc(size_t size); // 힙 영역으로의 메모리 공간 할당-초기화가 안되있어 쓰레기값
void free(void *ptr); // 힙 영역에 할당된 메모리 공간 해제
//malloc 함수는 성공 시 할당된 메모리의 주소 값, 실패 시 NULL 반환

void *ptr1 = malloc(4); // 4바이트가 힙 영역에 할당
free(ptr1); // ptr1이 가리키는 4바이트 메모리 공간 해제

☆malloc 함수의 반환형이 void형 포인터인 이유와 힙 영역으로의 접근
int *ptr = (int *)malloc(sizeof(int)); // int형 변수 크기의 메모리 공간 할당
위와같이 void형으로 반환되는 주소 값을 적절히 형 변환해서 할당된 메모리 공간에 접근!

calloc 함수 = 0으로 초기화 되어있음, malloc으로 구현가능
void *calloc(size_t elt_count, size_t elt_size);
// 성공시 할당된 메모리의 주소 값, 실패시 NULL 반환

★힙에 할당된 메모리 공간 확장 시 호출 : realloc
void realloc(void *ptr, size_t size); // 주소값이 변경되는 경우가 있음!
// 성공시 할당된 메모리의 주소 값, 실패시 NULL 반환
int *arr = (int *)malloc(sizeof(int)*3); // 길이가 3인 int형 배열 할당
arr = (int *)realloc(arr, sizeof(int)*5); // 길이가 5인 int형 배열로 확장

2015년 7월 26일 일요일

20150727 파일 입출력

모든파일은 아래와 같은 형식을 가진다!
정보 헤더
그림정보 data

파일의 헤더부분의 파일정보를 읽어 들이면 파일뷰어 같은것을 만들수 있다!

파일포인터 = 파일의 위치를 알고있는 포인터

파일을 열때는 파일의 처음위치부터 읽어들이지만 append(추가)를 사용할때는
파일의 끝 부분을 가리킨다!

int fseek(FILE *stream, long offset, int wherefrom);
// stream으로 전달된 파일 위치 지시자를 wherefrom에서부터 offset 바이트만큼 이동

매개변수 wherefrom 이…파일 위치 지시자는…
SEEK_SET(0) 이라면파일 맨 앞에서부터 이동을 시작
SEEK_CUR(1) 이라면현재 위치에서부터 이동을 시작
SEEK_END(2) 이라면파일 맨 끝에서부터 이동을 시작

fseek(fp, 2, SEEK_SET); // 파일 맨 앞에서 2번째
fseek(fp, 2, SEEK_CUR); // 파일 위치자가 가리키는 곳에서부터 끝방향으로 2번째
fseek(fp, -2, SEEK_END); // 파일 맨 끝에서 2번째

저수준 파일 입출력
★참조 사이트
http://cont122.edunet4u.net/~chonwb03/57%C2%F7%BD%C3.htm

  저수준의 파일 입출력에서는 FILE이란 구조 대신 간단하게 각 파일마다 번호를 사용하는데, 이를 파일 식별자(filedescriptor), 또는 핸들(handle)이라고 한다. 이 핸들은 0 이상의 값을 가지고 있는데 실제로 0과 1, 2는 고정된 의미(핸들 0은 표준 입력을 위한 번호이며 1은 표준 출력, 그리고 2는 표준 에러로 사용)를 갖고 있어서 파일을 처음 열게 되면 그 파일의 핸들은 3이 된다.

  저수준의 파일 입출력에서도 파일을 사용하기 위해서는 파일을 먼저 열어야 하며, 이때 다음과 같이 open 함수를 사용한다.


 int fd;
 fd = open("파일 이름", 액세스 방식[, 모드]);

  위에서 [ ]는 역시 생략할 수 있는 부분을 의미하며, 파일 이름은 fopen과 같이 열 파일의 이름이고 액세스 방식은 이 파일을 어떻게 열 것인가인데, fopen과는 달리 다음과 같은 형태로 사용한다.


모 드
내     용
O_RDONLY
0x0001
 읽기 전용으로 파일을 연다.
O_WRONLY
0x0002
 쓰기 전용으로 파일을 연다.
O_RDWR
0x0004
 읽고 쓰기 위해 파일을 연다.
O_CREAT
0x0100
 파일이 없을 경우 새로운 파일을 만든다.
O_TRUNC
0x0200
 현재 있는 파일의 내용을 0으로(제거) 한다.
O_EXCL
0x0400
 O_CREAT과 함께 사용하며, 파일이 없을 경우에만 연다.
O_APPEND
0x0800
 파일을 쓰기용으로 열고 파일 포인터를 파일의 끝에 위치시킨다.
O_TEXT
0x4000
 파일을 텍스트 형식으로 연다.
O_BINARY
0x8000
 파일을 이진 형식으로 연다.

  위의 O_로 시작하는 것들은 모두 상수로 이의 정의는 fcntl.h(이것은 file control의 약자)에 들어 있기 때문에 open 문을 사용하려면 반드시 fcntl.h를 포함하여야 한다.
  위의 것들은 액세스 방식의 한 조건들로 여러 개를 동시에 같이 사용할 수 있으며 이 때에는 각 조건들을 '|'(비트 연산자 OR)를 이용해서 묶으면 된다.
  data.da 란 파일을 읽기 전용으로 열고자 할 때에는 다음과 같이 하면 된다.


 fd = open("data.dat", O_RDONLY);

2015년 7월 21일 화요일

20150722 파일과 스트림, 그리고 기본적인 파일의 입출력

fopen 함수호출을 통한 파일과의 스트림 형성과 FILE 구조체

FILE *fopen(const char *filename, const char *mode);
// 성공 시 해당 파일의 FILE 구조체 변수의 주소 값, 실패 시 NULL 포인터 반환

int fclose(FILE *stream); // 성공시 0, 실패시 EOF를 반환
☆★☆반드시 fopen후에 fclose를 사용하여 스트림을 종료시켜야 리소스 소실이 없어진다!

feof 함수 기반의 파일복사 프로그램
int feof(FILE *stream); // 파일의 끝에 도달한 경우 0이 아닌 값 반환(-1로 정의된 상수)
FILE *src=fopen("src.txt", "rt"); // 복사할 파일내용 읽기
FILE *des=fopen("dst.txt", "wt"); // 복사할 위치에 쓰기
int ch; //
while((ch=fgetc(src))!=EOF){fputc(ch, des);} // 파일의 끝과 src가 같지 않을경우 반복
while(1){ch=fgetc(srt);if(EOF==ch){break;}fputc(ch, des)} // 위와 같은 결과

바이너리 데이터의 입출력 : fread(입력), fwrite(출력) ((void*)는 생략가능)
fread(주소(저장할 위치), 용량, 개수(용량x개수 만큼 read), 파일); // 리턴값은 개수 참조
size_t fread(void *buffer, xize_t size, size_t count, FILE *stream);
// 성공 시 전달인자 count, 실패 또는 파일의 끝 도달 시 count보다 작은 값 반환
int buf[12];
fread(void*)buf, sizeof(int), 12, fp); // fp는 FILE 구조체 포인터
// "sizeof(int) 크기의 데이터 12개를 fp로부터 읽어 들여서 배열 buf에 저장하라!"

fwrite
size_t fwrite(const void *buffer, xize_t size, size_t count, FILE *stream);
// 성공 시 전달인자 count, 실패 시 count보다 작은 값 반환
int buf[7]={1, 2, 3, 4, 5, 6, 7};
fwrite((void*)buf, sizeof(int), 7, fp);
// "sizeof(int) 크기의 데이터 7개를 buf로부터 읽어서 fp에 저장해라!"

★개행이 \n이 아니다!
"C 프로그램에서 \n을 파일에 저장하면 \r\n으로 변환되어 저장됨"
"파일에 저장된 \r\n을 C 프로그램상에서 읽으면 \n으로 변환되어 읽혀짐"
따라서 개행 문자의 변환을 신경 쓸 필요가 없다. 그저 텍스트 모드로 파일을 개방하면됨

☆스트림을 구분하는 기준1
데이터 READ 스트림 읽기만 가능
데이터 WRITE 스트림 쓰기만 가능
데이터 APPEND 스트림 쓰되 덧붙여 쓰기만 가능
데이터 READ/WRITE 스트림 일기, 쓰기 모두 가능

c언어는 위를 바탕으로 아래의 6가지로 스트림을 세분화

모드(mode) 스트림의 성격 파일이 없으면?
r 읽기 가능 에러
w 쓰기 가능 생성
a 파일의 끝에 덧붙여 쓰기 가능 생성
r+ 읽기/쓰기 가능 에러
w+ 읽기/쓰기 가능 생성
a+ 읽기/덧붙여 쓰기 가능 생성
위 표를 참조하여 필요로 하는 스트임의 특성과 일치하는 '파일의 개방 모드(mode)'를 선택
그리고 모드의 이름이 fopen 함수의 두 번째 인자가 된다.
★rt, rb // r뒤의 t는 텍스트 모드 b는 바이너리 모드

도스에서 (명령어) >> (파일이름) // 파일내용에 덧붙여쓰기(>하나면 덮어쓴다!)

☆스트림을 구분하는 기준2 : 텍스트 모드와 바이너리 모드
텍스트 파일(문자데이터-.txt등)과 바이너리 파일(바이너리 데이터-영상,음원파일 등)

2015년 7월 20일 월요일

20150721 구조체

★Union(공용체) - 멤버 변수들의 주소값이 동일(공용체의 멤버들이 메모리 공간을 공유)
typedef union 변수이름 {int A;, short B;}변수이름; // union 변수이름 { };

sum.A=0x12345678, sum.B=0x1234; // 출력결과==12341234(리틀 엔디안 방식)

★enum(열거형 Enumerated Type - type이 int로 고정되어있다! 값의 저장 가능)

enum syllable {Do=1, Re=2}; // type가 int로 고정이기에 생략! ;(세미콜론)도 생략
// Do를 정수 1을 의미하는 상수로 정의한다. 그리고 이 값은 syllable형 변수에 저장 가능
syllable to;
Do는 1을 의미하는 상수이니 for(to=0; to<2; to++)<-처럼 사용 가능 //to+=1

열거형 상수의 이름만 선언했을 경우 열거형 상수의 값은 0에서부터 1씩 증가하는 형태

열거형의 유용함은 둘 이상의 연관있는 이름을 상수로 선언함으로써
프로그램의 가독성을 높이는데 있다.

20150720 구조체와 사용자 정의 자료형

#include <stdio.h>
#pragma pack(1) // 바이트 크기 최적화(1바이트에 최적화) 속도문제가 생길수 있다!

typedef struct _smart // 프로그래머가 바이트크기에 따라 잘 배치or
{
int A;     // 4
short B;  // 8인 이유는 메모리 공간이 32비트 체제에선 4바이트 크기를 가지기 때문에
int C;     // 12
char D;  // 16
short E;  // 16
char F;   // 20
char G;  // 20인 이유는 1바이트 다음에 1바이트는 빈공간을 만들지 않고 붙여서 저장
short H; // 20
char I;    // 24
short J;  // 24
char K;  // 28인 이유는 J가 2바이트이기에 1바이트인 I다음에 바로 2바이트가 오면
           //1바이트를 비우고 채우기 때문
}Smart;

#pragma pack(4) // 바이트 크기 최적화(4바이트) 속도문제를 해결하기 위해 구조체
// 생성시만 1바이트 최적화를 하고 생성완료후 원래되로 되돌린다!

int main(void)
{
printf("%d\n", sizeof(Smart));

return 0;
}

모든 구조체의 이름을 대상으로 struct 선언의 생략을 위한 typedeef 선언이 등장한다!
구조체의 정의와 typedef의 선언을 한데 묶을 수도 있고, 또 이렇게 선언하는 것이 보편적
typedef struct point
{
int xpos;
int ypos;
}Point;
위의 선언은 아래의 정의와 선언을 한데 묶은 것!
struct point
{
int xpos;
int ypos;
};
typedef struct point Point;

typedef 선언이 추가 되었다고 해서 struct 선언을 통한 구조체 변수의 선언이 불가능한
것은 아니다. 다음 2가지 방식으로 구조체 변수를 선언할 수 있다.

Point pos1; // typedef 선언을 이용한 변수의 선언 OK!
struct point pos2; // struct 선언을 추가한 형태의 변수선언 OK!

★구조체의 이름 생략 가능!

★구조체도 함수의 인자로 쓸수잇다! p478~479

24. Point curPos=GetCurrentPosition(); // cpu내부에 cen이 임시로 저장되기에 구조체는 4개가 생성된다! curPos는 스택에 만들어진다!(curPos=GetCurrentPosition())==(curPos=cen)

구조체의 크기가 커질수록 속도가 느려진다!(효율적으로 사용하려면 주소를 넘긴다!)

★구조체의 멤버로 선언된 배열도 통째로 복사된다!

★구조체 변수를 대상으로는 매우 제한된 형태의 연산만 허용이된다!
허용되는 가장 대표적인 연산은 대입연산이며, 그 이외로 주소 값 반환을 목적으로 하는
& 연산이나 구조체 변수의 크기를 반환하는 sizeof 정도의 연산만 허용이 된다!

★중첩구조체-구조체 변수도 구조체의 멤버로 선언될수 있다!

2015년 7월 19일 일요일

20150706 LCD 초기화 순서

LCD 초기화 순서 - 8비트 인터페이스

1. 전원을 투입하거나 리셋 스위치를 누른다.
2. 초기화를 수행하기 전에 최소한 30ms 이상을 기다린다.
3. Function Set 명령(0x3X)를 보내고 4.1ms이상을 기다린 후에 다시 Function Set
명령(0x3X)를 보내고 100us이상을 기다린 후 다시 한번 Function Set 명령(0x3X)를 보낸다. 이상의 3회 Write 동작에서는 Busy Check를 수행하면 안된다.(X 는 Don't Care)
4. Function Set 명령(0b0011XX00)를 보낸다.
5. Display ON/OFF Control 명령(0b00001XXX)을 보낸다
6. Entry Mode Set 명령(0b000001XX)
7. DD RAM Address를 보낸다.
8. 표시할 문자 데이터를 보낸다.
9. 필요할 경우 7~8 과정을 반복한다.

기본적으로 배워야할것
GPIO
Interupt(중요!우선권)
Timer/Counter(CTC, PWM등)
ADC
UART(필수)->SPI. TWI

선형의 데이터를 수치화

아날로그는 노이즈(잡음)에 취약-노이즈를 최소화 하는것이 관건

3번 lcd밝기

RS와 RW는 같이움직인다고 보면된다!

4번단자 ir 로우 dr 하이

참조 ip등급
http://zemoskorea.cafe24.com/common/board/listbody.html?a_gb=board&a_cd=4&a_item=0&page=1&po_no=34&PHPSESSID=1418ac09301245813e9a4c84d8c1823e

2015년 7월 16일 목요일

20150717 구조체

구조체(structure)==하나 이상의 변수(포인터 변수와 배열 포함)를 묶어서 새로운 자료형을
정의하는 도구이다. 즉 구조체를 기반으로 새로운 자료형을 정의 할수있다!
struct point // 구조체 point의 정의 int==4byte 가 2개이므로 8byte
{
int xpos;
int ypos;
}; // 함수와는 다르게 중괄호의 끝에 ; (세미콜론)이 붙는다!
.이 &보다 우선순위가 높다! // scanf("%d", &point.xpos) == scanf("%d", &(point.xpos))

구조체 변수의 초기화
struct point pos={10, 20}; // pos.xpos=10, pos.ypos=20;
struct person man={"이승기", "010-1212-0001", 21};
초기화 과정에서는 문자열 저장을 위해서 strcpy함수를 호출하지 않아도 된다!

구조체 배열 // struct point arr[4];
구조체를 가리키는 포인터 == 구조체 포인터
struct point *pptr=&pos; // 구조체 포인터 변수 선언과 초기화
(*pptr).xpos=(*pptr).xpos+1; // 아래와 동일 우선순위가 *가 .보다 낮기에 소괄호 사용
pptr->xpos=pptr->xpos+1; // ->연산자는 pptr이 가리키는 변수의 멤버 xpos의 값을 +1

TYPE형 구조체 변수의 멤버로 TYPE형 포인터 변수를 둘 수 있다.
struct point{ // 환형?구조
int xpos;
int ypos;
struct point *ptr; }; // 별을 지우면 무한반복생성으로 인한 에러

구조체 변수의 주소 값과 첫 번째 멤버의 주소 값
구조체 변수의 주소 값은 구조체 변수의 첫 번째 멤버의 주소 값과 동일하다!

2015년 7월 15일 수요일

20150716 문자와 문자열 관련 함수

typedef를 사용하면 unsigned int의 선언을 size_t로 대체가능!
예==typedef unsigend int size_t; // 자료형의 이름 변경선언 (size_t==변경된이름)

string.h 문자열 관련함수 헤더파일

strlen 문자열의 길이를 반환하는 함수 // size_t strlen(const char *s);

문자열을 복사하는 함수들 : strcpy, strncpy // strcpy보다 strncpy가 더 안전하다!
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

문자열을 덧붙이는 함수들 : strcat, strncat // strcat보다 strncat가 더 안전하다!
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);

문자열을 비교하는 함수들 : strcmp, strncmp
//두 문자열의 내용이 같으면 0, 같지 않으면 0이 아닌 값 반환
//아스키코드 값을 비교
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);

2015년 7월 14일 화요일

20150715 스트림과 데이터의 이동

스트림은 입출력시 데이터가 크기가 정해지지 않은것

★스트림의 생성과 소멸
콘솔 입출력을 위한 '입력 스트림'과 '출력 스트림'은 프로그램이 실행되면 자동으로
생성되고, 프로그램이 종료되면 자동으로 소멸되는 스트림이다.

putchar<-문자를 1개씩 출력할때 사용(보통 \n을 많이사용)
fputc<-stdin, stderr등이 합쳐져 있는것을 분리해서 사용가능
putchar('\n') 따옴표는 1글자를 의미
getchar<-문자를 1개씩 입력할때 사용
fgetc<-

fprintf<-fputc처럼 파일출력위치 지정가능

ch1=getchar(); //문자 입력 - 키보드를 읽어와 ch1에 아스키코드값 저장
ch2=fgetc(stdin); // 엔터 키 입력

gets는 보안에 취약해서 가급적 쓰지 않는게 좋다!
fgets(str, sizeof(str), stdin); // stdin으로부터 문자열을 입력 받아서 배열 str에 저장하되,
sizeof(str)의 길이만큼만 저장해라!

버퍼는 메모리에 저장되는 공간(윈도우에선? 큐)
예=scanf로 입력을 받을시 엔터를 치기전에 저장되어 있는것 엔터를 치지 않으면 화면에
표시된 문자나 숫자는 어딘가에 저장되어있다!

버퍼링을 하는 이유는 데이터의 전송 효율성 즉 일을 여러번 나누어서 하는것보다
한번에 하는것이 빠르고 효율적이기 때문에

File - stdout 모니터 1번
- stdin 0번
- stderr 에러 2번
- 일반

stderr은 예전엔 프린터로(dot방식 프린터시절) 에러를 모두 출력햇으나
Log파일로 기록되도록 바뀌었다가 요즘엔 선택?

(리 디렉션)실행파일이름뒤에 >을 붙이면 사용예
도스
dir > a.txt // 1번 실행시 파일이 없으면 생성되어 아래의 내용이 저장된다!
 D 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: 2A6A-5E9A

 D:\C\20150715\p420 디렉터리

2015-07-15  오전 09:53    <DIR>          .
2015-07-15  오전 09:53    <DIR>          ..
2015-07-15  오전 10:26                 0 a.txt
2015-07-15  오전 09:32               214 main.c
2015-07-15  오전 09:32            72,192 main.exe
2015-07-15  오전 09:32               710 main.obj
               4개 파일              73,116 바이트
               2개 디렉터리  229,105,537,024 바이트 남음

cl > a.txt // 1번 stdout - 실행시 아래의 내용저장
usage: cl [ option... ] filename... [ /link linkoption... ]
cl 2> a.txt // 2번 stderr - 실행시 아래의 내용저장
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

fgetc(stdin) // f get c (stdin)==f는 sdtin이고 stdin은 File라고 볼수있다!

2015년 7월 13일 월요일

20150714 void형 포인터 / main함수를 통한 인자의 전달

함수의 원형을 모를때 강제로 에러를 일으킨다!
int a = printf; // 타입이 맞지 않기 때문에 에러가 발생

컴파일러는 아래와 같이 설명한다!
main.c(7) : warning C4047: 'initializing' : 'int' differs in levels of indirection from
 'int (__cdecl *)(const char *, ...)'



함수를 반환하는 함수를 호출하는 함수포인터
운영체제에 많이 씀(복잡한것?)

★void형 포인터(type형이 존재하지 않는 포인터)
형이 존재 하지 않기에 어떤 형이라도 가리킬수있다!

  • 단점은 아무런 포인터 연산도 하지 못한다. 값의 변경이나 참조도 불가능!
  • 읽거나 쓰기 금지!
void형 포인터는 메모리의 저장공간을 지정하지 않기때문에
*(int *)vp=100; // 왼쪽처럼 type casting을 사용하면 값의 변경이 가능(쓰기)
iNum2=*(int *)vp; // 왼쪽처럼 type casting을 사용하면 값의 참조 가능(읽기)

★프로그램 실행시 main함수로 전달할 인자를 열거할 수(결정할 수) 있으며,
main함수 역시 이러한 인자를 전달 받을수 있도록 제한된 형태의 매개변수 선언이 가능
예제(또 다른예로서 도스창에서 notepad를 열때)
#include <stdio.h>
int main(int argc, char *argv[]) /* argc는 실행파일의 이름을 가리킨다!
argv[] 실행파일의 실행직전에 크기 결정 */
{
int i=0;
printf("전달된 문자열의 수 : %d\n", argc); // 실행파일의 이름도 문자열로 인식

for(i=0; i<argc; i++) // 지정된 문자열의 크기만큼 출력
{
printf("%d번째 문자열 : %s\n", i+1, argv[i]);
}
return 0;
}

20150713 임베디드 ADC

Analogue 선형 수업중의 강의?
Digital 비선형 Discreate 수업중의 노트필기?

아날로그를 디지털로 바꾸는 3단계
표본화
양자화
부호화
ADC에서 클럭을 사용하는 이유
표본화를 위해서
어중간한 데이터 때문에 잡음이 생길수 있음
양자화 잡음

분주비는 표본화할때 사용

단순 껐다 켰다 GPIO(일반 목적의 인풋 아웃풋)
아래 나머지 4개를 AFIO(특수목적)
Interrupt
Timer/Counter
USART
ADC

PD0(INT0)<-예(앞쪽이 GPIO이름, 뒤쪽이 AFIO이름)

아날로그가 더 선명한 화질
아날로그 신호를 디지털 신호로 바꾼후 다시 아날로그 신호로 바꿀때
디지털 기술이 좋아도 아날로그 신호로 복구는 불가능
(디지털 신호로 바뀔때 일정량의 데이터를 소실)

차동 입력 모드 예 ADC0(2V)-ADC1(1V)=1V

2015년 7월 12일 일요일

20150713 함수 포인터와 void 포인터

int A=5; // 주소 출력 불가(컴파일 에러?)
char T[]="하이"; // 문자 자체가 주소

Symbol Table
cinst char 이름X 주소

반환형 함수의 이름 매개변수
void add (int a, int b)
{
return a+b; // 값의 반환
}

test(); // test;<-test자체가 함수의 주소

// void test(); // test의 타입 추출(함수의 추출방식)
// void ();
// void ()();
// void (*)();

int (*test())(const char *, ...); // test함수의 원형
int (*(*)())(const char *, ...);
int (*(*tp)())(const char *, ...); // tp 포인터 선언<-가장 어려운 함수포인터

int (*test())(const char *, ...) // printf를 반환하는 함수 (*)<-자리에 변수이름
{
return printf;
}

예제
#include <stdio.h>

int main(void)
{
// void test(); // test의 타입
// void ();
// void ()();
// void (*)();
// void (*tp)(); // 메인함수에서 실제 호출 방식

// int main();
// int ();
// int ()();
// int (*)();
// int (*tp)();

// int printf(const char *, ...); // == int scanf(const char *, ...)
// int (const char *, ...);
// int ()(const char *, ...);
// int (*)(const char *, ...);
// int (*tp)(const char *, ...);

// void (*tp)();
// tp=test; // tp==4byte(tp는 포인터가 되므로 4byte)
// tp(); // ==test();
// *(tp)(); // ==test();

int iNum;
int (*tp)(const char *, ...);
tp=scanf;
tp("%d", &iNum);
tp=printf;
tp("iNum = %d\n", iNum);
/* printf와 scanf의 함수선언 형태가 비슷하므로 하나의 함수추출 방식으로 둘다 사용 가능
char iNum[10];
int (*tp)(const char *, ...);
tp=scanf;
tp("%s", &iNum);
tp=printf;
tp("%s\n", iNum);
*/
return 0;
}

2015년 7월 9일 목요일

20150710 3차원 배열 / 포인터의 포인터에 대한 이해

2차원 배열을 이해하고 있다면, 3차원 배열도 이해하고 있는것.
int arr1[2][3][4]; // 높이 2, 세로 3, 가로 4인 int형 3차원 배열

★3차원 배열은 여러 개의 2차원 배열이 모여있는 형태로 이해하는 것이 더 합리적!

int * ip
포인터가 가리키는 type 포인터 선언 변수이름

일반적인 포인터는 1차원 배열을 가리킬수있다(2차원 이상은 경고?)

예제
#include <stdio.h>

int main(void)
{
int iA=100;
int *ip=&iA;
int **ipp=&ip;
int ***ippp=&ipp;

printf("%d\n", iA);
printf("%d\n", *ip); // iA의 값
printf("%d\n", **ipp); // iA의 값
printf("%d\n", ***ippp); // iA의 값
printf("\n");
printf("%08X\n", &iA);
printf("%08X\n", ip); // iA의 주소 값
printf("%08X\n", ipp); // ip의 주소 값
printf("%08X\n", ippp); // ipp의 주소 값, ippp의 주소 값=&ippp 
return 0;
}

예제
#include <stdio.h>

int main(void)
{
double num=3.14;
double *ptr=&num; // num의 주소 값을 저장 
double **dptr=&ptr; // ptr의 주소 값을 저장
double *ptr2;

printf("%9p %9p\n", ptr, *dptr); // ptr=&num, *dptr=*ptr=&num
printf("%9g %9g\n", num, **dptr); // num=3.14, **dptr=3.14

ptr2=*dptr; // ptr2=ptr 과 같은 문장 이때 ptr2=&num
*ptr2=10.99;
printf("%9g %9g\n", num, **dptr); // num=10.99, **dptr=10.99
return 0;
}

예제
#include <stdio.h>

void SwapIntPtr( int **dp1, int **dp2)
{
int *temp=*dp1; // *dp1=&num1
*dp1=*dp2; // *dp2=&num2
*dp2=temp; // temp=*dp1
}

int main(void)
{
int num1=10, num2=20;
int *ptr1, *ptr2;

ptr1=&num1, ptr2=&num2;
printf("*ptr1, *ptr2 : %d %d\n", *ptr1, *ptr2);

SwapIntPtr(&ptr1, &ptr2); // ptr1과 ptr2의 주소 값 전달!
printf("*ptr1, *ptr2 : %d %d\n", *ptr1, *ptr2);
return 0;
}

2015년 7월 8일 수요일

20150709 다차원 배열의 이해와 활용

2차원 배열을 선언과 동시에 초기화하는 경우에는 배열의 세로길이만 생략이 가능하다!
// int arr[][2]={1, 2, 3, 4, 5, 6};

2차원배열일때 뒤의 열을 생략하면 행의 첫번째 주소가 나온다!
// printf("%08X\n", A[0]);

2차원 배열의 메모라상 할당의 형태
0x1001번지, 0x1002번지, 0x1003번지

따라서 아래와 같은 방식으로 초기화를 해도 결과 값은 같다!
// int arr[3][2]={1, 2, 3, 4, 5, 6}; // 1차원식 초기화 방법

*(*(A+0)+1)==*(A[0]+1)==A[0][1] // 값

2차원 배열이면 값을 표현하기 위해서  **또는  [][]개가 필요하다!
3차원 배열이면 값을 표현하기 위해서 ***또는 [][][]개가 필요하다!

별과 대활호가 하나도 없으면 행이동

2015년 7월 7일 화요일

20150708 포인터와 함수에 대한 이해

★참조 http://jsy6036.tistory.com/entry/%ED%8F%B0-%EB%85%B8%EC%9D%B4%EB%A7%8C-%EA%B5%AC%EC%A1%B0%EC%99%80-%ED%95%98%EB%B2%84%EB%93%9C-%EA%B5%AC%EC%A1%B0
1. 폰 노이만 구조
2. 하버드 구조

최적화된 c프로그래밍 = 속도가 빠를지 메모리를 적게사용할지를 고려해서 코드작성
프로그래밍을 할때 버스와 같은 크기의 자료형을 사용하는것이 가장좋다!
(버스보다 작은 크기의 자료형을 선언하면 메모리에 저장시 남는부분을 막아야 하기때문)

★매개변수로 배열을 선언할수 없다!

void arr(int arr[]) // 매개변수선언시 배열형식으로 선언가능
배열의 주소값이 인자로 전달될시 int arr[]형태의 선언을주로 많이 사용한다. 하지만 이둘이
같은 선언으로 간주되는 경우는 매개변수의 선언으로 제한된다. 따라서 다음의 코드에서,
int main(void) {
int arr[3]={1, 2, 3};
int *ptr=arr; // int ptr[]=arr; 로 대체 불가능
}
★함수 내에서는 인자로 전달된 배열의 길이를 계산할수 없다!
배열의 주소값을 인자로 전달받는 매개변수는 포인터 변수이기 때문에 이를 대상으로 sizeof 연산을 할 경우 배열의 크기가 반환되지 않고 포인터변수의 크기가 반환된다.
이렇듯 함수 내에서는 인자로 전달된 배열의 길이를 계산할 수가 없기 때문에
배열의 크기나 길이 정보도 함께 인자로 전달해야한다.

명확하게 말하면 Call-by-reference는 c에는 존재하지 않는다.(참조가 아닌 값 교환)
Call-by-reference 사용예
1. 배열의 크기(길이?)계산 불가
2. 데이터의 값을 수정
3. 데이터의 용량의 크기가 너무 클때

목적의식을 가지고 Call-by-value를 쓸지 Call-by-reference를 쓸지 판단해야 한다!

포인터 대상의 const 선언
const가 *을 기준으로 왼쪽은 가리키는 곳 오른쪽은 변수 둘다일경도 있음

2015년 7월 6일 월요일

20150707 포인터와 배열의 이해

포인터의 형은 메모리 공간을 참조하는 기준이 된다.(메모리를 어떻게 해석하느냐?)
포인터 연산시 포인터가 가리키는곳의 크기만큼 증가한다!

상수는 코드영역에서 불러온다!(코드 영역은 대부분의 운영체제가 쓰기를 막아놓음)

char str1[]="My String"; // My String은 상수(변수 형태의 문자열)

위는 스택영역과 코드영역에서 My String이 둘다 사용되므로 20바이트의 크기를 가진다.

char *str2="My String"; // 상수형태의 문자열

*str2는 포인터 주소값이므로 4바이트 따라서 14바이트의 크기를 가진다.

문자열을 사용할때 수정할 필요가 없으면 포인터를 사용하여 메모리 낭비를 줄이자!

콘스트상수
printf(주소); // " " = 콘스트역할

int iNum=100;
char *str3="iNum=%d\n";
printf(str3, iNum);

위를 실행시키면 iNum=100

아래의 예제에서 p1과 p5가 가리키는 곳은 같지만 컴파일러의 종류가 코드영역
수정을 허용한다면 주소값이 달라진다!

char *p1="Hi";
char *p2="Hi~";
char *p3="Hi Hello";
char *p4="Hi Hell0~";
char *p5="Hi";

a=10+20;
위의계산은  상수이기에 컴파일시 계산되서 a에 대입된다!
a=a+b;
위의 계산은 변수이기에 실행시 계산되서 a에 대입된다!

20150706 포인터와 배열 함께 이해하기 / 실수 저장방식(정규화)

int iNum1=0x12345678;
int iNum2=0xABCDEF07;
unsigned char *p;
p = &iNum1;
  ↑
형동등성

포인터를 기준으로 정수형은 경고가 생겨도 자료형이 비슷하면 적용된다!
경고를 해결하기위해 &iNum1 앞에 형변환연산자를 넣어준다.((unsigned char *))

포인터는 메모리를 보는 방식을 결정한다!

사람이 대소를 비교할때 숫자를 왼쪽부터 판단하고 계산을할때는 오른쪽부터 함
계산이 중요하기에 Value값 저장 방식은 1바이트 단위로 뒤에서부터 저장된다.
슈퍼컴퓨터는 논리연산이 중요하기에 Big Endian(엔디안)을 사용한다!

BigEndian
start end
3 4 5 6
12 34 56 78
3 4 5 6
78 56 34 12
end start
little Endian

실수 저장 방식
IEEE 754의 부동 소수점 표현은 크게 세 부분으로 구성되는데, 최상위 비트는 부호를 표시하는 데 사용되며, 지수 부분(exponent)과 가수 부분(fraction/mantissa)이 있다.
  • 먼저, 부호와 지수를 가수로 나누어야 한다.
  • 소수점을 왼쪽으로 이동시켜, 왼쪽에는 1만 남게 만든다. 예를 들면                 101.01 = 1.0101x2² 과 같다. 이것을 정규화된 부동소수점 수라고 한다.
  • 가수부는 소수점의 오른쪽 부분으로, 부족한 비트 수 부분만큼 0으로 채워 23비트로 만든다. 결과는 1010 1000 0000 0000 0000 0000이 된다.
  • 지수는 2이므로, Bias를 더해야 한다. 32비트 IEEE 754 형식에서는 Bias는 127이므로 2+127 = 129이 된다. 이진법으로 변환하면 1000 0001이 된다.
  • General floating point ko.svg

5.25를 2진수로
101.01 -> 1.0101x2²(정규화된 부동소수점 수)

 0.25
x   2
------
 0.5<--0.5의 앞자리 0
x  2
------
 1.0<-1.0의 앞자리 1

절대값 부호방식(Magnitude)

예제
−118.625 (십진법)을 IEEE 754 (32비트 단정도)로 표현해 보자.
  • 먼저, 부호와 지수를 가수로 나누어야 한다.
  • 음수이므로, 부호부는 1이 된다.
  • 그 다음, 절댓값을 이진법으로 나타내면 1110110.101이 된다. (이진기수법을 참조)
  • 소수점을 왼쪽으로 이동시켜, 왼쪽에는 1만 남게 만든다. 예를 들면 1110110.101=1.110110101×2⁶ 과 같다. 이것을 정규화된 부동소수점 수라고 한다.
  • 가수부는 소수점의 오른쪽 부분으로, 부족한 비트 수 부분만큼 0으로 채워 23비트로 만든다. 결과는 11011010100000000000000이 된다.
  • 지수는 6이므로, Bias를 더해야 한다. 32비트 IEEE 754 형식에서는 Bias는 127이므로 6+127 = 133이 된다. 이진법으로 변환하면 10000101이 된다.

이 결과를 정리해서 표시하면 다음과 같다.

Float point example frac.svg

2015년 7월 3일 금요일

20150703 포인터와 배열의 관계

배열의 이름은 배열의 시작 주소 값을 의미(이름자체가 주소)하며,
그 형태는 값의 저장이 불가능한 상수이다.
배열의 이름은 '상수 형태의 포인터'이다. 그래서 배열의 이름을 가리켜
'포인터 상수'라 부르기도 한다.
★메모리 주소는 -가 없다!(unsigned)

Symbol Table
type      Value      Address
int        a            1000
int[ ]     b[0]        2000
int[ ]     b[1]        2004

b는 배열 전체의 이름(주소[번호])

// printf("arr[0]=%d\n", arr[0]);
// printf("arr[0]=%d\n", *arr);
// printf("arr[0]=%d\n", *p);

위 3개의 출력 결과는 같다!!

int arr[4]={1, 2, 3, 4};
int *p;
printf("*&arr[0]=%08X\n", *&arr[0]);
printf("*&arr[0]=%08X\n", *(arr+0));
printf("*&arr[0]=%08X\n", p[0]);
printf("*&arr[0]=%08X\n", *(p+0));
// printf("*&arr[0]=%08X\n", *&p[0]); //이것도 실행결과는 같음

출력값은 *&arr[0]=00000001

위 4개의 출력 결과는 같다!!(주소값을 참조하고 또 주소값을 참조하기에 결과값은 1)
속도 또한 같으므로 위 4개중 개인이 알아보기 쉬운걸 사용하면된다! 섞어도 됨!

포인터를 대상으로하는 증가 및 감소 연산은
int형 포인터를 대상으로 n증가 n * sizeof(int)의 크기만큼증가(4)
double형 포인터를 대상으로 n증가 n * sizeof(double)의 크기만큼증가(8)
감소 연산도 위와 같다!

2015년 7월 2일 목요일

20150702 포인터의 이해(★포인터 변수)

주소만 전문적으로 저장하는 타입 = 포인터타입
32bit(bus가) cpu체제에서는 4바이트 주소값을 가지기 때문에 포인터를 선언할때
4바이트로 선언하여 사용하는것이 좋다!

포인터를 선언할때 *(type)을 변수이름 앞에 붙여준다!(★자료형)
int, double등 같이 선언해야한다!(예 int *ip; // (int)정수를 가리키는 ★포인터 변수)

★포인터변수를 선언할때 선언만 해놓고 이후에 유효한 주소값을 채워넣을 경우
*ip=0; || *ip=NULL; // NULL은 사실상 0을 의미함
위의 초기화하는 값 0을 가리켜 '널 포인터'라 한다. 이는 0번지를 의미하는것이 아니라
"아무데도 가리키지 않는다!!"

주소값만을 사용하여 변수를 수정가능
*((int *)0x12FF3C)=38; // 포인터 변수를 쓰지않고 주소값만을 사용하여 변수 수정방법

자료형  변수이름   주소값
int       iNum        0012FF3C
int*      ip             0012FF38

c에서 *의미 2항 연산자(곱하기), 단항 연산자(포인터)
iNum앞에 *을 붙이면 2항 연산자로 인식해서 컴파일시 에러가 뜬다!

ip=100; // ip에 100을 넣는다!
*ip=100; // ip에 저장된 포인터주소값을 따라가서 대응하는 곳에 100을 넣는다!

예제

#include <stdio.h>

int main()
{
int iNum=100;
int *ip;

printf("iNum Value(값)       : %d\n", iNum);
printf("iNum Address(주소값) : %08X\n", &iNum);
printf("ip Address(주소값)   : %08X\n", &ip);
// printf("ip Value(값)       : %08X\n", ip);

ip=&iNum;

printf("ip Value(값)         : %08X\n", ip);
// printf("ip Address(주소값)   : %08X\n", &ip);
printf("ip Address(주소값)   : %d\n", *ip);
return 0;
}

ip는 iNum을 가리킨다!

예제
#include <stdio.h>

int main()
{
int A=99;
int *p=&A;

*p=100;
printf("%08X\n", &A);

*((int *)0x12FF3C)=38; // *(int *)은 주소를 인지시킨다!
                                        // type casting operator(형변환 연산자)
printf("%d\n", A);

return 0;
}

2015년 7월 1일 수요일

20150701 1차원 배열 ★배열을 scanf로 입력받을시 &를 붙이지 않는다!

배열의 정의후 정의된 공간보다 많은 양을 저장가능하지만 문제가 발생할 가능성이 크다!

문자열의 끝에 null값이 자동삽입된다. (\n)<-표기상 실제저장=0

★배열을 scanf로 입력받을시 &를 붙이지 않는다!
scanf 함수호출을 통해서 입력 받은 문자열의 끝에도 널 문자가 삽입되어 있다!
scanf 함수로는 공백(스페이스바)를 입력받을수 없다!


20150630 변수의 존재기간과 접근범위2 ★주소를 저장하기위한 변수(포인터)

전역변수와 지역변수의 이름이 동일한 경우 해당영역 내에서는 동일한 이름의
전역변수를 가린다! 해당영역내에서 지역변수가 우선!

지역변수는 실행파일(*.exe)의 용량관련(늘어날순 있어도 줄어들진 않는다!)
전역변수는 *.obj의 용량관련

static를 변수이름앞에 붙이면 전역변수의 속성을 가지나 하나의 함수에만 사용가능
초기화 하면 data영역에 접근하여 저장
초기화 하지 않으면 bss영역에 접근하여 저장

register을 변수이름앞에 붙이면 cpu내에 존재하는 레지스터라는 메모리 공간에
저장될 확률이 높아지지만 우리가 아무리 register선언을 추가해도 컴파일러가 합당하지
않다고 판단하면 레지스터에 할당되지 않는다. 반대로 아무런 선언을 하지 않아도
컴파일러가 레지스터에 할당해야겠다고 판단하면 그 변수는 레지스터에 할당된다.

재귀함수(어셈블리어로 보았을경우 실행코드가 안좋기때문에 거의 쓰지 않는다)