728x90

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


외부에 선언 및 정의되었다고 알려주는 extern


extern 선언을 생략해도 되지만,

다른 파일에 선언 및 정의되었음을 알리기위해 extern을 쓴다.



둘 이상의 파일을 컴파일하는 방법 (프로젝트에 여러 파일을 추가해 컴파일)


기존에 존재하는 파일은 Existing Item... 새로 만드려면 New Item...



전역변수의 static 선언


static int num=0; // 외부 소스파일에서 접근 불가능한 전역변수


void SimpleFunc(void)

{

....

}


함수의 static 선언


static void MinCnt(void)

{

cnt--;

}


외부 소스파일에서의 접근(호출)을 허용하지않기 위한 선언

즉 접근범위를 파일 내부로 제한한다.




헤더파일의 디자인과 활용

프로젝트에 다음 헤더파일과 소스파일을 추가해 컴파일하면 정상적인 실행이 된다. (아래 세 파일은 동일한 디렉토리에 존재해야만 함)


header1.h

1
2
{
    puts("Hello world!");


header2.h

1
2
    return 0;
}


main.c

1
2
3
4
5
#include <stdio.h>

int main()
#include "header1.h"    // 이 위치에 .h에 저장된 내용을 include
#include "header2.h"    // 이 위치에 .h에 저장된 내용을 include



헤더파일을 include 하는 첫번째 방법


#include <헤더파일 이름>

표준 헤더파일(C 표준에서 정의하는 stdio.h, stdlib.h같은 기본적으로 제공되는 헤더파일)이 저장되어 있는 디렉토리에서 파일을 찾게 됨



헤더파일을 include 하는 두번째 방법


#include "헤더파일 이름"

소스파일이 저장된 디렉토리에서 헤더파일을 찾게 됨


두번째 방법은 다음과 같이 절대 경로를 명시해서 헤더파일 지정가능

#include "C:\CPwer\MyProject\header.h"    // windows 상에서의 절대경로 지정



하지만 절대경로를 지정해서 헤더파일을 선언하면 다른 컴퓨터에서는 경로(path)가 다를 수가 있기 때문에 잘 쓰지 않는다.


따라서 다음과 같이 상대 경로를 자주 이용한다.


#include "..\..\Myheader\header2.h"




extern 선언은 대개 헤더파일에 삽입


필요한 함수를 소스파일에서 모두 extern 선언할 필요없이

헤더파일만 include하면 되므로 편하고 소스의 길이도 준다.


extern 선언은 실행파일의 크기를 증가시키거나 성능의 손해를 

보는게 아니므로 신경쓰지 않아도 된다.



헤더파일 디자인의 예




basicArith.h    

/* 매크로 PI에 대한 정의가 삽입 (매크로 명령문은 개별 파일단위로만 유효), 매크로 PI가 필요한 소스파일은 이 헤더파일은 include하면 된다.

   다른 소스파일에서 include해두면 혹여나 정의해둔 상수값이 바뀌어도 해당 헤더파일 이외의 수정은 필요없게 된다 */

1
2
3
4
5
6
#define PI 3.1415

extern double Add(double num1, double num2);
double Min(double num1, double num2);
double Mul(double num1, double num2);
double Div(double num1, double num2);


basicArith.c    // 사칙연산의 기능을 제공하는 함수들이 정의. 함수의 선언은 basicArith.h에 포함되어 있다.

double Add(double num1, double num2)
{
	return num1+num2;
}

double Min(double num1, double num2)
{
	return num1-num2;
}

double Mul(double num1, double num2)
{
	return num1*num2;
}

double Div(double num1, double num2)
{
	return num1/num2;
}


areaArith.h

1
2
double TriangleArea(double base, double height);
double CircleArea(double rad);


areaArith.c    // 면적을 구하는 함수들이 정의. 이 함수들은 basicArith.c에 정의된 Mul, Div와 같은 함수들을 호출하기 때문에 basicArith.h를 include

1
2
3
4
5
6
7
8
9
10
11
#include "basicArith.h"

double TriangleArea(double base, double height)
{
	return Div(Mul(base, height), 2);
}

double CircleArea(double rad)
{
	return Mul(Mul(rad, rad), PI);
}



roundArith.h

1
2
double RectangleRound(double base, double height);
double SquareRound(double side);


roundArith.c    // 둘레를 구하는 함수들이 정의. 이 함수들도 basicArith.c에 정의된 사칙연산과 관련된 함수들을 호출. 따라서 basicArith.h를 include

1
2
3
4
5
6
7
8
9
10
11
#include "basicArith.h"

double RectangleRound(double base, double height)
{
	return Mul(Add(base, height), 2);
}

double SquareRound(double side)
{
	return Mul(side, 4);
}


main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include "areaArith.h"
#include "roundArith.h"

int main(void)
{
	printf("삼각형 넓이(밑변 4, 높이 2): %g \n", 
		TriangleArea(4, 2));
	printf("원 넓이(반지름 3): %g \n", 
		CircleArea(3));

	printf("직사각형 둘레(밑변 2.5, 높이 5.2): %g \n", 
		RectangleRound(2.5, 5.2));
	printf("정사각형 둘레(변의 길이 3): %g \n", 
		SquareRound(3));
	return 0;
}


Output : 

삼각형 넓이(밑변 4, 높이 2): 4

원 넓이(반지름 3): 28.2735

직사각형 둘레(밑변 2.5, 높이 5.2): 15.4

정사각형 둘레(변의 길이 3): 12




구조체의 정의도 헤더파일에 넣어두고, 필요할때마다 include하는 것이 일반적


구조체의 정의도 파일단위로 선언이 유효


동일 구조체의 정의를 둘 이상의 소스파일에서 필요로 하면 

소스파일마다 정의를 추가시켜 줘야 한다.



헤더파일의 중복삽입 문제


extern int num;

void Increment(void);

바로 위와 같은 유형의 선언은 두 번 이상 삽입이 되어도

컴파일 오류가 발생하지 않는다. 이는 컴파일러에게 전달하는 메시지일뿐이기 때문이다.


하지만 구조체나 함수의 정의가 포함된 헤더파일을 두 곳 이상에서 참조하게 되면

두 번 이상 정의된 형태가 되어 컴파일 에러가 발생한다.


따라서 이 문제는 조건부 컴파일을 활용해 해결한다.


조건부 컴파일을 활용한 중복삽입 문제의 해결


#ifndef __STDIV2_H__    // __STDIV2_H__    매크로가 정의되어 있지 않다면

#define __STDIV2_H__    // __STDIV2_H__    매크로를 정의하라 (정의되어 있다면 포함되지 않음)



/* 이 파일을 처음 포함하는 소스파일은 __STDIV2_H__가 정의되있지 않은 상태이므로 2~8행까지 포함

   따라서 2행에 의해 매크로 __STDIV2_H__가 define, 4~8행에서 구조체 Div가 정의된다.


   이후 다른 파일에서 다시 포함하는 경우네는 매크로 __STDIV2_H__가 정의된 상태이므로 1~10행의 모든 내용이 포함되지 않는다. 

   따라서 구조체 Div가 소스파일당 하나씩 정의되게 된다.

 */

stdiv2.h    

1
2
3
4
5
6
7
8
9
10
#ifndef __STDIV2_H__
#define __STDIV2_H__

typedef struct div
{
	int quotient;   // 몫
	int remainder;  // 나머지
} Div;

#endif


intdiv4.h

1
2
3
4
5
6
7
#ifndef __INTDIV4_H__
#define __INTDIV4_H__

#include "stdiv2.h"
Div IntDiv(int num1, int num2);

#endif


intdiv4.c

1
2
3
4
5
6
7
8
9
#include "stdiv2.h"

Div IntDiv(int num1, int num2)
{
	Div dval;
	dval.quotient=num1/num2;
	dval.remainder=num1%num2;
	return dval;
}


main.c

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include "stdiv2.h"
#include "intdiv4.h"

int main(void)
{
	Div val=IntDiv(5, 2);
	printf("몫: %d \n", val.quotient);
	printf("나머지: %d \n", val.remainder);
	return 0; 
}


main.c에 2행과 3행에서 stdiv2.h를 두 번 포함하려고 한다.

하지만 stdiv2.h에 삽입된 매크로 지시자 #ifndef~#define~#endif에 의해 중복 삽입 문제는 생기지 않는다.


헤더파일에 존재하는 내용은 #ifndef~#define~#endif를 이용해 중복삽입 문제를 미연에 방지하는 것이 좋다.

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

C 복습-콘솔 입출력  (0) 2017.02.11
c 복습  (0) 2016.11.03
선행처리기와 매크로2  (0) 2016.08.30
선행처리기와 매크로  (0) 2016.08.29
파일위치 지시자 / 메모리 관리와 동적할당  (0) 2016.08.28
728x90

조건부 컴파일(Conditional Compilation)을 위한 매크로


#if... #endif : 참이라면
조건부 코드 삽입을 위한 지시자


참이면 #if~#endif 까지 컴파일 대상에 포함
거짓이면 선행처리기가 #if~#endif 까지의 코드를 지움


#include <stdio.h>
#define  ADD     1    // 0이 아닌 값은 참
#define  MIN     0    // 0은 거짓

int main(void)
{
	int num1 = 20;
        int num2 = 10;

#if ADD    // ADD가 '참'이라면
	printf("%d + %d = %d \n", num1, num2, num1+num2);
#endif

#if MIN    // MIN이 '참'이라면
	printf("%d - %d = %d \n", num1, num2, num1-num2);
#endif

	return 0;
}



Output:

1
20 + 10 = 30 





#ifdef... #endif : 정의되었다면

정의되었다면 컴파일 대상에 포함
정의되지 않았다면 선행처리기가 #ifdef~#endif 까지의 코드를 지움



#include <stdio.h> // #define ADD 1 #define MIN 0 // 정의 여부만 물으므로 값을 지정하지 않아도 상관없음 int main(void) { int num1 = 20; int num2 = 10; #ifdef ADD // 주석에 의해 정의되지 않은걸로 처리 printf("%d + %d = %d \n", num1, num2, num1+num2); #endif #ifdef MIN // 정의되어 있으므로 컴파일 대상에 포함 printf("%d - %d = %d \n", num1, num2, num1-num2); #endif return 0; }



Output:

1
20 - 10 = 10 




#ifndef... #endif : 정의되지 않았다면


정의되지 않았다면 포함

정의되어있다면 선행처리기가 #ifndef~#endif 까지의 코드를 지움




#else의 삽입: #if, #ifdef, #ifdef에 해당 (#endif로 끝을 표시)


#if  

if문이 참이면 포함, 거짓이면 else문이 포함


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#define  HIT_NUM      5

int main(void)
{
#if HIT_NUM==5    // if문이 참이면 컴파일 대상에 포함
	puts("매크로 상수 HIT_NUM은 현재 5입니다.");    
#else     // if문이 참이 아닐 때 else문이 컴파일 대상으로 포함, 이 때 if문의 코드는 전처리기가 제거
	puts("매크로 상수 HIT_NUM은 현재 5가 아닙니다.");
#endif     // if문의 끝을 나타냄

	return 0;
}



Output:

1
매크로 상수 HIT_NUM은 현재 5입니다.




#elif의 삽입 : else if와 유사, #if에만 해당 


if~else if~else 구문과 동일 형태를 구성할 수 있다.


#if~#elif~#else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#define  HIT_NUM      7

int main(void)
{
#if HIT_NUM==5
	puts("매크로 상수 HIT_NUM은 현재 5입니다.");
#elif HIT_NUM==6
	puts("매크로 상수 HIT_NUM은 현재 6입니다.");
#elif HIT_NUM==7
	puts("매크로 상수 HIT_NUM은 현재 7입니다.");
#else
	puts("매크로 상수 HIT_NUM은 5, 6, 7은 확실히 아닙니다.");
#endif 

	return 0;
}


Output : 

매크로 상수 HIT_NUM은 현재 7입니다.




'매개변수'의 결합과 '문자열'화


문자열 내에서는 매크로의 매개변수 치환 불가


#define STRING_JOB(A, B)    "A의 직업은 B입니다."

괄호안의 A와 B는 문자열안(" ")에 치환될 수 없다.


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define  STRING_JOB(A, B)     "A의 직업은 B입니다."

int main(void)
{
	printf("%s \n", STRING_JOB(이동춘, 나무꾼));
	printf("%s \n", STRING_JOB(한상순, 사냥꾼));
	return 0;
}

Output :

A의 직업은 B입니다.

A의 직업은 B입니다.



"이동춘의 직업은 나무꾼입니다."라는 문장을 기대하지만

"A의 직업은 B입니다."로 치환된다.



이를 해결하기 위해 #연산자를 이용


#define    STR(ABC) #ABC

매개변수 ABC에 전달되는 인자를 문자열 "ABC"로 치환


예)

STR(123) "123"

STR(12, 23, 34) "12, 23, 34"



char * str = "ABC" "DEF";

char * str = "ABCDEF"


위의 두 문장 선언은 같다.

둘 이상의 문자열 선언을 나란히 하면, 하나의 문자열 선언으로 인식


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define  STRING_JOB(A, B)     #A"의 직업은 " #B"입니다."

int main(void)
{
	printf("%s \n", STRING_JOB(이동춘, 나무꾼));
	printf("%s \n", STRING_JOB(한상순, 사냥꾼));
	return 0;
}

Output : 

이동춘의 직업은 나무꾼입니다.

한상순의 직업은 사냥꾼입니다.




매크로 연산자 없이 단순 연결은 불가능


10/65/175 -> 1065175


#define STNUM(Y, S, P) YSP

#define STNUM(Y, S, P) Y S P

위의 두 정의는 컴파일 에러



#define STNUM(Y, S, P) ((Y)*100000+(S)*1000+(P))

최선의 해결책인듯 하지만...


1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
//#define  STNUM(Y, S, P)   YSP
//#define  STNUM(Y, S, P)   Y S P
#define  STNUM(Y, S, P)   ((Y)*100000+(S)*1000+(P))

int main(void)
{
	printf("학번: %d \n", STNUM(10, 65, 175));
	printf("학번: %d \n", STNUM(10, 65, 075));
	return 0;
}

Output:

1
2
학번: 1065175 
학번: 1065061 


위와 같이 0이 8진수를 의미하는 걸로 인식되어서 원하던 결과를 얻을 수 없다.



필요한 형태대로 단순 결합 : ## 연산자


#define CON(UPP, LOV) UPP ## 00 ## LOW


int num = CON(22, 77); -> 220077



#define STNUM(Y, S, P) Y ## S ## P

YSP가 단순 결합됨


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define	STNUM(Y, S, P)	Y##S##P

int main(void)
{
	printf("학번: %d \n", STNUM(10, 65, 175));
	printf("학번: %d \n", STNUM(10, 65, 075));
	return 0;
}



Output:

1
2
학번: 1065175 
학번: 1065075 


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

c 복습  (0) 2016.11.03
파일의 분할과 헤더파일의 디자인  (0) 2016.08.30
선행처리기와 매크로  (0) 2016.08.29
파일위치 지시자 / 메모리 관리와 동적할당  (0) 2016.08.28
파일 입출력 공부  (0) 2016.08.25
728x90

선행처리기(Preprocessor)


선행처리 거친 소스파일은 프로그래머가 인지할 수 있는 형태이다.


선행처리기의 일

- 단순 치환


소스

1
2
3
4
5
6
#define	PI	3.14	// 선행처리기에게 명령하는 문장은 #으로 시작하며 한줄마다 처리된다. (끝에 semi colon붙이지 않음)

int main()
{
	num = PI * 3.5;
}


선행처리 후 소스

#define PI 3.14 //지워짐 


1
2
3
4
int main(void)
{
	num  = 3.14 * 3.5;
}



Object-like macro


#define PI 3.14

directive / macro / macro's body


PI를 상수 3.14로 정의 


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

#define  NAME          "홍길동"
#define  AGE            24   
#define  PRINT_ADDR     puts("주소: 경기도 용인시\n");

int main(void)
{
	printf("이름: %s \n", NAME);
	printf("나이: %d \n", AGE);
	PRINT_ADDR;
	return 0;
}


Output:

1
2
3

이름: 홍길동 
나이: 24 
주소: 경기도 용인시


Function-like macro

#define SQUARE (X) X*X


SQUARE(123); 123*123


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#define SQUARE(X)   X*X

int main(void)
{
	int num=20;

	/* 정상적 결과 출력 */
	printf("Square of num: %d \n", SQUARE(num));	//20*20
	printf("Square of  -5: %d \n", SQUARE(-5));	// (-5)*(-5)
	printf("Square of 2.5: %g \n", SQUARE(2.5));	// 2.5*2.5

	/* 비정상적 결과 출력 */
	printf("Square of 3+2: %d \n", SQUARE(3+2));	// (5)*(5)가 아닌 3+2*3+2 = 11
	return 0;
}


Output:

1
2
3
4
Square of num: 400 
Square of  -5: 25 
Square of 2.5: 6.25 
Square of 3+2: 11 


잘못된 매크로 함수 정의의 해결책


#define SQUARE(X) X*X


바로 위 예제에서 SQUARE(3+2)가 기대했던 25가 아닌 값으로 연산되는걸 해결해보자.



해결책1

#define SQUARE(X)    X*X

SQUARE(3+2)을 SQUARE((3+2))으로 바꿈


(3+2)*(3+2)=25, 하지만 매크로라고 하기 무색하게 사용이 불편



해결책2

#define SQUARE(X)    (X)*(X)


SQUARE(3+2) (3+2)*(3+2)=25

이 경우는 해결, 하지만...


num=120/SQUARE(2) 120 / (2)*(2)

120/4=30을 기대하지만 120/2=60, 60*2=120으로 연산된다.



최상의 해결책!

#define SQUARE(X)    ((X)*(X))

120 / ((2)*(2)) = 30




매크로를 두 줄에 걸쳐서 정의하는 방법


#define SQUARE(X)  \

((X)*(X))


한줄의 끝에 매크로의 정의가 이어짐을 의미하는 backslash추가




먼저 정의된 매크로의 사용

위에서 먼저 정의된 매크로를 아래에서 사용할 수도 있다.

아래는 이를 응용한 예제이다.


1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#define PI 3.14
#define PRODUCT(X, Y)   ((X)*(Y))
#define CIRCLE_AREA(R)  (PRODUCT((R), (R))*PI)

int main(void)
{
	double rad=2.1;
	printf("반지름 %g인 원의 넓이: %g \n", rad, CIRCLE_AREA(rad));
	return 0;
}

// (((2.1)*(2.1))*3.14) = 13.8474

Output:

1
반지름 2.1인 원의 넓이: 13.8474 



매크로 함수의 장점 (일반함수와 비교)


- 매크로 함수는 일반함수에 비해 실행속도가 빠르다.


함수 호출을 완성화기 위해선 별도의 메모리 공간이 필요하고

호출된 함수로의 이동 및 반환의 과정을 거쳐야 한다.

(메모리 공간 할당과 해제)


매크로 함수는 선행처리 과정에서 치환이 이뤄지니 그러한 과정이 불필요하며, 따라서 실행속도가 빠른 것.



- 자료형에 의존적이지 않다.


ADD (int n1, int n2)

dAdd(double n1, double n2)

일반함수는 위와 같이 전달되는 인자의 자료형에 의존적이다.


하지만 매크로 함수는 자료형에 따른 별도의 함수 정의가 필요 없다.



매크로 함수의 단점


1. 정의하기가 까다롭다.


일반함수

int DiffABS (int a, int b);

{

if(a>b)

return a-b;

else

return b-a;

}


정의된 매크로 함수

#define DIFF_ABS(x, y) (x)>(y) ? (x)-(y) : (y)-(x);


이는 그나마 정의하기 쉬운 편이고, 함수의 내용이 복잡할 수록 매크로 함수로는 정의하기가 어려워진다.



2. 디버깅하기가 쉽지 않다.

컴파일러는 선행처리된 이후의 내용(결과물)을 기준으로 오류를 뱉는다. 

따라서 프로그래머 입장에서 오류를 찾기 힘들게 되고 더 신경을 써야할 부분이 생기게 된다.


1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define  DIFF_ABS(X, Y)     ( (x)>(y) ? (x)-(y) : (y)-(x) )

int main(void)
{
	printf("두 값의 차: %d \n", DIFF_ABS(5, 7));
	printf("두 값의 차: %g \n", DIFF_ABS(1.8, -1.4));
	return 0;
}

선언되지 않은 식별자 오류 (매크로 함수의 X, Y 대소문자 구분이 틀림)

Output:

1
2
3
4
5
In function 'main':
Line 6: error: 'x' undeclared (first use in this function)
Line 6: error: (Each undeclared identifier is reported only once
Line 6: error: for each function it appears in.)
Line 6: error: 'y' undeclared (first use in this function)



어떠한 함수를 매크로로 정의하는 것이 좋은가?


작은 크기의 함수

한 두줄 정도 크기의 작은 함수

if~else, for과 같이 실행 흐름을 컨트롤 하는 문장은 배제하는 것이 좋다.



호출 빈도수가 높은 함수

함수를 매크로로 정의하는 가장 큰 이유는 성능적 측면이 크다.

다만, 빈도수의 높은 기준이 어느정도인지는 프로그래머의 주관에 따라 다를 수 있다.


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

파일의 분할과 헤더파일의 디자인  (0) 2016.08.30
선행처리기와 매크로2  (0) 2016.08.30
파일위치 지시자 / 메모리 관리와 동적할당  (0) 2016.08.28
파일 입출력 공부  (0) 2016.08.25
구조체 공부  (0) 2016.08.23
728x90

파일 위치 지시자

FILE 구조체의 멤버 중 하나

Read/Write에 대한 위치 정보를 갖고 있다. (어디까지 읽었는지 또는 어디부터 이어서 쓸건지)


파일 입출력 함수가 호출될때 

파일 위치 지시자의 참조 위치가 달라짐



파일 위치 지시자의 이동 fseek


#include <stdio.h>

int fseek(FILE * stream, long offset, int wherefrom);

// 성공시 0, 실패시 0이 아닌 값 반환


어디서부터(wherefrom) 

몇칸 오른쪽(+) 또는 왼쪽(-)으로 이동할건지에 대한 인자(offset)를 전달받음


SEEK_SET  - 파일 맨앞에서부터 이동시작

SEEK_CUR - current : 현재 위치에서부터 이동시작


SEEK_END - 파일 맨끝에서부터 이동시작

  주의 할점은 END는 EOF를 뜻하는 것



현재 파일 위치 지사자의 위치를 보여주는 함수 ftell


#include <stdio.h>

long ftell(FILE * stream);

// 파일 위치 지시자의 위치 정보 반환, 오류시 -1 반환


읽기/쓰기 위치는 0부터 시작


#include <stdio.h>

int main(void)
{
	/* 파일생성 */
	FILE * fp = fopen("text.txt", "wt");
	fputs("123456789", fp);
	fclose(fp);

	/* 파일개방 */
	fp = fopen("text.txt", "rt");

	/* SEEK_END test */
	printf("\n현재위치 : %d\n", ftell(fp) + 1); // 읽기/쓰기 위치는 0부터 시작이지만 시작점을 1로 가정한다.
	fseek(fp, -2, SEEK_END);    // END는 EOF임에 주의!
	putchar(fgetc(fp));    // 8을 읽고난뒤 다음 문자를 가리킴!
	printf("\n현재위치 : %d\n", ftell(fp) + 1); 

	/* SEEK_SET test */
	fseek(fp, 2, SEEK_SET);
	putchar(fgetc(fp));    // 3을 읽고난뒤 다음 문자를 가리킴!
	printf("\n현재위치 : %d\n", ftell(fp) + 1); 

	/* SEEK_CUR test */
	fseek(fp, 2, SEEK_CUR);    // 4에서 오른쪽 2칸 이동
	putchar(fgetc(fp));    // 6이 출력


	fclose(fp);
	return 0;
}

Output : 

현재위치 : 1

8

현재위치 : 9

3

현재위치 : 4

6



ftell을 이용한 위치정보 저장, 활용예제


#include <stdio.h>

int main(void)
{
	long fpos;
	int i;

	/* 파일생성 */
	FILE * fp=fopen("text.txt", "wt");
	fputs("1234-", fp);
	fclose(fp);

	/* 파일개방 */
	fp=fopen("text.txt", "rt");

	for(i=0; i<4; i++)
	{
		putchar(fgetc(fp));    // 문자 하나 읽어 출력후 지시자는 다음 바이트를 가리킴    
		fpos=ftell(fp);    // 현재위치 정보를 fpos 변수에 담음
		fseek(fp, -1, SEEK_END);    // - 로 위치 이동
		putchar(fgetc(fp));
		fseek(fp, fpos, SEEK_SET);    // fpos에 담긴 위치로 이동
	}
	fclose(fp);
	return 0;
}




메모리의 구성


유사한 성향의 데이터를 묶어 관리할 수있도록 다음과 같이 메모리 공간이 나뉘어진다.

code / data / heap / stack


층층이 나눠진 서랍장 수납공간과 같이 

효율적인 관리뿐만 아니라 메모리 접근 속도도 향상되게 된다.


 

메모리의 stack에는 일반적으로 값이 밑에서부터 위로 만들어진다.


넣어진 종이컵을 꺼내려면 나중에 넣은 종이컵부터 꺼내게 되는 것과 같다.


함수의 호출순서가 main->fct1->fct2라면

stack의 반환은 역순으로 이루어진다. (fct->fct1->main)


메인함수 종료시 스택영역 소멸

프로그램 종료시 데이터 영역에 할당된 것도 소멸


지역변수 - 함수를 빠져나갈때 할당된 메모리가 소멸


#include <stdio.h>

char * ReadUserName(void)
{
	char name[30];
	printf("What's your name? ");
	fgets(name, sizeof(name), stdin);
	return name;
}

int main(void)
{
	char * name1;
	char * name2;
	name1=ReadUserName();
	printf("name1: %s \n", name1);
	name2=ReadUserName();
	printf("name2: %s \n", name2);
	return 0;
}

컴파일시 warning메시지 출력

"warning C4172: 지역 변수 또는 임시: name의 주소를 반환하고 있습니다."

또한 쓰레기값이 출력되게 된다.


위 예제에서 ReadUserName 함수가 호출되어 할당된 메모리 공간은, 함수를 빠져나간후 반환과 동시에 메모리에서 소멸된다.

따라서 name 지역변수는 시간이 지나면 다른 데이터에 의해 덮어질, 의미를 갖지 않는 데이터가 된다.



전역변수 - 값을 덮어써버림

#include <stdio.h>

char name[30];

char * ReadUserName(void)
{
	printf("What's your name? ");
	gets(name);
	return name;
}

int main(void)
{
	char * name1;
	char * name2;
	name1=ReadUserName();
	printf("name1: %s \n", name1);
	name2=ReadUserName();
	printf("name2: %s \n", name2);

	printf("name1: %s \n", name1);
	printf("name2: %s \n", name2);
	return 0;
}

Output : 

What's your name? 길동

name1: 길동

What's your name? 철수

name2: 철수

name1: 철수

name2: 철수


name1이든 name2든 전역변수 name을 똑같이 가리키기 때문에 값을 덮어써버리게 된다.

이와 같이 지역변수로도 전역변수로도 풀지 못하는 상황을 해결하기 위한 것이 바로 malloc 함수이다.



malloc - Heap영역에 메모리 공간 할당을 위한 함수

#include <stdlib.h> // 또는 <malloc.h>

void * malloc(size_t size); // heap 영역에 생성하고자하는 바이트수만큼 메모리 공간 할당, 첫번째 메모리 주소 반환


heap 영역 : 프로그래머가 원하는 시점에 메모리 공간에 할당 및 소멸을 하기 위한 영역


malloc 함수는 인자로 숫자만 하나 전달받기 때문에

메모리의 포인터 형을 결정짓지 못한다.


void형은 type이 없기에 어떤 값이든 받을 수 있지만, void * 형 변수에 대한 포인터 연산을 할 수 없다.


따라서 다음과 같이 형변환을 거치는 호출형태를 취한다.

int * ptr1 = (int * )malloc(sizeof(int)*7); // 4byte 7개 확보

double ptr2 = (double *)malloc(sizeof(double)*9);


malloc함수는 메모리 할당 실패시 NULL을 반환

heap영역으로의 접근은 pointer를 통해서만 이루어진다.


free - Heap영역의 메모리 공간 해제

void free(void * ptr); // 할당된 메모리 공간 해제


free함수를 호출하지 않아도 프로그램 종료시엔 할당된 자원이 모두 반환된다.

하지만 실제 구현되는 프로그램은 오랜 시간 running되는 경우가 많으므로, heap 영역의 리소스 관리를 위해 free함수 사용을 습관화하는 것이 좋다.


#include <stdio.h> #include <stdlib.h> //malloc.h을 써도 되지만 일반적으로 stdlib.h을 include char * ReadUserName(void) { char * name = (char *)malloc(sizeof(char) * 30); //malloc 함수를 통해 heap에 할당 printf("니 이름이 뭐니? "); gets(name); return name; } int main(void) { char * name1; char * name2; name1 = ReadUserName(); printf("첫째 이름: %s \n", name1); name2 = ReadUserName(); printf("둘째 이름: %s \n", name2); printf("첫째 이름 재출력: %s \n", name1); // 소멸되지않았음을 확인하기 위함 printf("둘째 이름 재출력: %s \n", name2); free(name1); // malloc함수를 통해 할당한 메모리 공간 해제 free(name2); return 0; }

Output : 

니 이름이 뭐니? 홍길동

첫째 이름: 홍길동

니 이름이 뭐니? 홍수정

둘째 이름: 홍수정

첫째 이름 재출력: 홍길동

둘째 이름 재출력: 홍수정



calloc

#include <stdlib.h>

void * calloc(size_t elt_count, size_t elt_size);


malloc 함수와의 차이점은 elt_count x elt_size 크기만큼의 바이트를 동적할당한다는 것이다.

그리고 malloc 함수는 쓰레기값으로 초기화되는것과 달리 calloc는 모든 비트를 0으로 초기화한다.


realloc

#include <stdlib.h>

void * realloc(void * ptr, size_t size);


ptr이 가리키는 heap의 메모리 공간을 size_t size만큼 늘린다.


만약 malloc함수를 통해 기존에 할당된 메모리 공간에 이어서 확장할 여력이 되면 malloc과 realloc이 가리키는 주소는 같다.


기존의 malloc함수를 통해 할당되었던 메모리 공간을 확장할 여력이 안되면 새로운 메모리 공간을 할당하게 된다. 

이 때 malloc 함수를 통해 할당된 메모리를 복사하게 되며 기존의 메모리 공간은 지워진다.

따라서 realloc이 반환하는 주소값이 malloc 함수와는 달라지게 된다.


malloc과 같이 calloc,realloc도 free함수 호출을 통해 할당된 메모리 공간을 해제한다. (한번만 호출하면 된다)

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

선행처리기와 매크로2  (0) 2016.08.30
선행처리기와 매크로  (0) 2016.08.29
파일 입출력 공부  (0) 2016.08.25
구조체 공부  (0) 2016.08.23
c언어 복습 - 문자와 문자열 관련 함수  (0) 2016.08.22
728x90
입력 스트림과 출력 스트림의 형성을 요청하는 fopen 함수

#include <stdio.h>
FILE * fopen(const char * filename, const char * mode);

fopen함수를 통해 FILE 구조체 변수가 생성, 파일과의 스트림 형성
FILE 구조체 변수에 파일 정보가 담김 (파일 데이터가 담기는 것이 아님)

FILE * fp = fopen("data.txt", "wt");  // wt(write in text mode)모드로 파일 data.txt와 출력스트림을 형성
FILE * fp = fopen("C:\\Project\\data.txt", "wt");    // 경로 지정시 \ 자체를 표현하려면 \\ 두개 써야함

FILE * fp = fopen("data.txt", "rt");    // rt모드는 입력스트림을 형성

fputc('A', fp); // fp가 지칭하는 파일 data.txt에 문자 A 저장



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main()
{
	FILE * fp = fopen("data.txt", "wt");	
	if (fp == NULL)
	{
		puts("File open failed");
		return -1;
	}

	fputc('A', fp);	//	출력의 대상을 파일로 지정.
	fputs("BC", fp);
	fclose(fp);

	return 0;
}






스트림의 소멸을 요청하는 fclose 함수


#include <stdio.h>

int fclose(FILE * stream);



fclose 함수가 가지는 의미 (써야하는 이유)


  • OS는 스트림 형성을 위해 시스템의 자원(주로 메모리)을 할당한다. 이 때 fclose함수를 통해 자원을 반환해준다.
  • 두번째로 fclose 함수를 통해 버퍼에 있던 데이터가 출력이 되게 된다. 출력버퍼에 데이터가 존재하는 상황에서 비정상 종료가 될 때 소멸될 수 있는 문제도 있을 수 있다. 이 때문에 버퍼의 데이터가 파일로 바로 저장되도록 fclose 함수를 호출해주는 것이 좋다.

출력 버퍼를 비우는 fflush 함수

앞에서 stdout을 대상으로 호출했듯이 출력버퍼를 대상으로 호출한다. (wt모드)

입력버퍼를 비우려면 데이터를 읽으면 된다. 따라서 입력버퍼를 비우는 함수는 따로 존재하지 않는다.




파일 입출력 예제 (파일을 생성해서 문자와 문자열 저장후 읽어들이기)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#include <stdio.h> int main(void) { FILE * fp=fopen("simple.txt", "wt"); if(fp==NULL) { puts("파일오픈 실패!"); return -1; } fputc('A', fp); fputc('B', fp); fputs("My name is Hong \n", fp); fputs("Your name is Yoon \n", fp); fclose(fp); return 0; }

13행에서 \n을 넣지않으면 읽기를 했을 때 문자가 생길 수 있다. 문자열이 파일에 저장할 때는 null문자가 저장되지 않고, 때문에 파일에서는 개행을 기준으로 문자열을 구분하기 때문이다.


#include <stdio.h>

int main(void)
{
	char str[30];
	int ch;
	FILE * fp=fopen("simple.txt", "rt");
	if(fp==NULL) {
		puts("파일오픈 실패!");
		return -1;
	}

	ch=fgetc(fp);
	printf("%c \n", ch);
	ch=fgetc(fp);
	printf("%c \n", ch);

	fgets(str, sizeof(str), fp);
	printf("%s", str);
	fgets(str, sizeof(str), fp);
	printf("%s", str);

	fclose(fp);
	return 0;
}


읽어 들일 때는 write순서대로 read해야 저장된 값대로 읽어지는 것을 볼 수 있다. (저장된 값의 순서대로 fputc는 fgetc로 fputs는 fgets로 읽는다.)



feof 함수


#include <stdio.h>

int feof (FILE * stream);

파일의 끝(End Of File)에 도달한 경우 0이 아닌 값 반환 (읽어들일 데이터가 더 이상 없음을 의미)


파일 입력함수는 오류가 발생했을 때에도 EOF를 반환한다. 아래의 문자와 문자열 단위 파일복사 프로그램에서

파일에 끝에 도달한건지, 오류가 발생한건지 체크를 할 수 있다.



#include <stdio.h>

int main(void)
{
	FILE * src = fopen("src.txt", "rt");	// 미리 만들어진 src.txt 파일을 text mode로 읽음
	FILE * des = fopen("dst.txt", "wt");	// src.txt의 text를 dst.txt파일에 text mode로 씀
	int ch;

	if (src == NULL || des == NULL)
	{
		puts("파일 열기 실패!");
		return -1;	// -1은 오류를 의미
	}

	while ((ch=fgetc(src)) != EOF)
	{
		fputc(ch, des);
	}

	if (feof(src) != 0)
		puts("파일 복사완료!");	// 0이 아닌 값을 반환하면 EOF에 도달했음을 의미
	else
		puts("파일 복사실패..");

	fclose(src);
	fclose(des);

	return 0;
}

정상적으로 파일이 복사되면 파일 복사완료! 메시지와 함께 src.txt파일의 text가 dst.txt파일에 복사된 것을 볼 수 있다.


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

선행처리기와 매크로  (0) 2016.08.29
파일위치 지시자 / 메모리 관리와 동적할당  (0) 2016.08.28
구조체 공부  (0) 2016.08.23
c언어 복습 - 문자와 문자열 관련 함수  (0) 2016.08.22
c 복습-문제풀이2  (0) 2016.08.22
728x90
/*
  중첩된 구조체의 정의
 Point 구조체(x, y pos), Circle 구조체(Point cen, double rad)
 center, radius를 보여주는 함수를 정의하고 메인함수에서 출력
 */
#include <stdio.h>
typedef struct
{
 int xpos;
 int ypos;
}Point;
typedef struct
{
 Point cen;
 double rad;
}Circle;
Circle GetCircleInfo(void)

 Circle cir;
 printf("원의 중심 입력: ");
 scanf("%d %d", &cir.cen.xpos, &cir.cen.ypos);
 printf("반지름 입력: ");
 scanf("%f", &cir.rad);
 return cir;
}
void ShowPosition(Circle pos)
{
 printf("[%d %d] \n %g", pos.cen.xpos, pos.cen.ypos, pos.rad);
}
int main()
{
 Circle Cir = GetCircleInfo();
 ShowPosition(Cir);
  
 return 0;
}/* 문자열 형태의 '종업원 이름'과 '주민번호', 정수형태의 '급여정보'를 저장할
employee 구조체 정의하고 변수 하나 선언

프로그램 사용자에게 입력받고 구조체의 데이터 출력
*/

#include <stdio.h>

struct employee
{
 char name[20];
 char num[20];
 int pay;
};

int main()
{
 struct employee arr[3];
 int i;

 for (i = 0; i<3; i++)
 {
  printf("종업원%d 이름 : ", i+1);
  scanf("%s", &arr[i].name);
  printf("종업원%d 주민번호 : ", i+1);
  scanf("%s", &arr[i].num);
  printf("종업원%d 월급 : ", i+1);
  scanf("%d", &arr[i].pay);
 }

 for (i = 0; i<3; i++)
  printf("[이름 : %s 주민번호 : %s 월급 : %d] ", arr[i].name, arr[i].num, arr[i].pay);

 return 0;
}



/*
 point(x-y pos) 구조체와 circle 구조체를 정의하고
 circle구조체(radius, center)의 center멤버는 포인터 변수를 활용해 point 구조체 멤버를 가리키도록 하라.

 그리고 두 구조체의 내용(반지름과 원의 중심)을 출력
*/

#include <stdio.h>

struct point
{
 int xpos;
 int ypos;
};

struct circle
{
 double radius;
 struct point * center;
};

int main()
{
 struct point centerXY = { 1,3 };
 double rad = 4.4;
 
 struct circle ring = { rad, &centerXY };
 printf("원 반지름 : %g \n", ring.radius);
 printf("원의 중심 : %d %d \n", ring.center->xpos, ring.center->ypos);
 
 return 0;
}




/*
 함수로의 구조체 변수 전달과 반환
*/

#include <stdio.h>

typedef struct
{
 int xpos;
 int ypos;
}Point;

void ShowPosition(Point pos)
{
 printf("[%d %d] \n", pos.xpos, pos.ypos); 
}

Point GetCurrentPosition(void)
{
 Point cen;
 printf("현재 x-y좌표 입력: ");
 scanf("%d %d", &cen.xpos, &cen.ypos);
 
 return cen;
 // 구조체를 생성하여 멤버를 초기화하고 그 구조체를 반환(Point형 cen)
}

int main()

 Point curpos = GetCurrentPosition(); // GetCur-()함수의 반환값을 curpos로 복사
 ShowPosition(curpos);
 /* 29-30 == ShowPosition(GetCurrentPosition());   
 구조체 변수 curpos가 ShowPosition 함수의 인자가 됨. */
 return 0;
}




/*
 구조체 멤버로 선언된 배열의 복사
*/

#include <stdio.h>

typedef struct
{
 char name[20];
 char phoneNum[20];
 int age;
}Person;

void ShowPersonInfo(Person man)
{
 printf("name: %s \n", man.name);
 printf("phone: %s \n", man.phoneNum);
 printf("age: %d \n", man.age);
}

Person ReadPersonInfo()
{
 Person man;
 printf("name? ");
 scanf("%s", man.name);
 
 printf("phone number? ");
 scanf("%s", man.phoneNum);

 printf("age? ");
 scanf("%d", &man.age);

 return man;
}

int main()

 Person Man = ReadPersonInfo(); 
 // ReadPer-함수를 호출하면 구조체 변수 man에 값이 담긴다. 반환된 man은 메인함수의 구조체 변수 Man에 복사된다.
 ShowPersonInfo(Man);

 return 0;
}



/*
 구조체 변수를 대상으로 하는 Call-by-ref.
*/

#include <stdio.h>

typedef struct
{
 int xpos;
 int ypos;
}Point;

void OrgSymTrans(Point *ptr) // 원점대칭이동
{
 ptr->xpos = (ptr->xpos)*-1;
 ptr->ypos = (ptr->ypos)*-1;
}

void ShowPosition(Point pos)
{
 printf("[%d, %d] \n", pos.xpos, pos.ypos);
}

int main()

 Point pos = { 7,-5 };
 printf("원래 값 "); ShowPosition(pos);

 OrgSymTrans(&pos);
 printf("원점 대칭이동 "); ShowPosition(pos);

 OrgSymTrans(&pos);
 printf("원점 대칭이동 "); ShowPosition(pos);

 return 0;
}



/*
 구조체 변수간 대입연산 (멤버 대 멤버 복사)
*/

#include <stdio.h>

typedef struct
{
 int xpos;
 int ypos;
}Point;

void ShowPosition(Point pos)
{
 printf("[%d, %d] \n", pos.xpos, pos.ypos);
}

int main()

 Point pos1 = { 7,-5 };
 Point pos2;
 pos2 = pos1; // pos1에서 pos2로 대입연산(멤버 대 멤버 복사)

 printf("pos1 size: %d \n", sizeof(pos1));
 ShowPosition(pos1);

 printf("pos2 size: %d \n", sizeof(pos2));
 ShowPosition(pos2);

 return 0;
}




/*
 구조체 변수를 대상으로 하는 +,- 연산
 */
#include <stdio.h>

typedef struct
{
 int xpos;
 int ypos;
}Point;

Point AddPoint(Point pos1, Point pos2)
{
 Point pos_Sum = { pos1.xpos + pos2.xpos, pos1.ypos + pos2.ypos };
 return pos_Sum;
}
Point MinPoint(Point pos1, Point pos2)
{
 Point pos_Min = { pos1.xpos - pos2.xpos, pos1.ypos - pos2.ypos };
 return pos_Min;
}

void ShowPosition(Point pos)
{
 printf("[%d %d] \n", pos.xpos, pos.ypos);
}

int main()
{
 Point pos1 = { 7,-5 };
 Point pos2 = { 2,9 };
 Point result;

 result = AddPoint(pos1, pos2);
 ShowPosition(result);
 
 result = MinPoint(pos1, pos2);
 ShowPosition(result); 
 return 0;
}




/*
  구조체 두 변수를 대상으로 저장된 값을 바꿔주는 함수를 정의하고 이를 호출하는 예제 작성
 */
#include <stdio.h>

typedef struct
{
 int xpos;
 int ypos;
}Point;


void SwapPoint(Point *ptr1, Point *ptr2)
{
 Point temp = *ptr1;
 *ptr1 = *ptr2;
 *ptr2 = temp;
}


int main()
{
 Point pos1 = { 2, 4 };
 Point pos2 = { 5, 7 };
 printf("pos1 : [%d %d] ", pos1.xpos, pos1.ypos);
 printf("pos2 : [%d %d] \n", pos2.xpos, pos2.ypos);

 SwapPoint(&pos1, &pos2);
 printf("pos1 : [%d %d] ", pos1.xpos, pos1.ypos);
 printf("pos2 : [%d %d] \n", pos2.xpos, pos2.ypos); 
 
 return 0;
}



/*
       구조체를 통해 연관있는 데이터를 하나로 묶을 수 있는 자료형을 정의,

 데이터 표현 및 관리가 용이해진다.

*/

#include <stdio.h>

typedef struct
{
 char name[20];
 char num[20];
 char major[20];

}Student;

void ShowStudent(Student *ptr)
{
 printf("이름 : %s \n", ptr->name);
 printf("학번 : %s \n", ptr->num);
 printf("전공 : %s \n", ptr->major);
}

int main()
{
 Student arr[3];
 int i;

 for (i = 0; i < 3; i++)
 {
  printf("이름 : "); scanf("%s", arr[i].name);
  printf("학번 : "); scanf("%s", arr[i].num);
  printf("전공 : "); scanf("%s", arr[i].major);
 }
 puts("");

 for (i = 0; i < 3; i++)
  ShowStudent(&arr[i]);

 return 0;
}



/*
  중첩된 구조체의 정의
 Point 구조체(x, y pos), Circle 구조체(Point cen, double rad)
 center, radius를 입력받는 함수, 보여주는 함수를 정의하고 메인함수에서 출력
 */

#include <stdio.h>

typedef struct
{
 int xpos;
 int ypos;
}Point;

typedef struct
{
 Point cen;
 double rad;
}Circle;

Circle GetCircleInfo(void)

 Circle cir;
 printf("원의 중심 입력: ");
 scanf("%d %d", &cir.cen.xpos, &cir.cen.ypos);
 printf("반지름 입력: ");
 scanf("%lf", &cir.rad);
 return cir;
}

void ShowPosition(Circle cir_info)
{
 puts("");
 printf("원의 중심 : [%d %d] \n", cir_info.cen.xpos, cir_info.cen.ypos);
 printf("반지름 : %g \n", cir_info.rad);
}

int main()
{
 Circle Cir = GetCircleInfo();
 ShowPosition(Cir);
  
 return 0;
}



/*    ul (0, 0)    (100, 0)  

 (0, 100) (100, 100) lr  

Rectangle 구조체 변수를 인자로 전달받아 직사각형 넓이를 계산해서 출력하는 함수,

직사각형 네 점의 좌표정보 출력 함수 각각 정의.

Rectangle 변수내에는 두 점의 정보만 존재 (좌표평면상 직사각형을 표현하기 위해 필요한 점의 갯수는 ul, lr 2개만 있으면 되므로)

*/

#include <stdio.h>

typedef struct

{

int xpos;

int ypos;

}Point;


typedef struct

{

Point ul; // upper left

Point lr; // lower right

}Rectangle;


void ShowRecArea(Rectangle rec)

{

printf("넓이 : %d \n",

(rec.lr.xpos - rec.ul.xpos)*(rec.lr.ypos - rec.ul.ypos)); // (가로) * (세로)

}


void ShowRecPos(Rectangle rec)

{

printf("좌 상단: [%d %d] \n", rec.ul.xpos, rec.ul.ypos);

printf("좌 하단: [%d %d] \n", rec.ul.xpos, rec.lr.ypos);

printf("우 상단: [%d %d] \n", rec.lr.xpos, rec.ul.ypos);

printf("우 하단: [%d %d] \n", rec.lr.xpos, rec.lr.ypos);

/*(1,1) (4,1)

 (1,4) (4,4)*/

}


int main()

{

Rectangle rec1 = { {1,1},{4,4} };

Rectangle rec2 = { {0,0},{7,5} };

ShowRecArea(rec1);

ShowRecPos(rec1);


ShowRecArea(rec2);

ShowRecPos(rec2);

return 0;

}



/*
 공용체의 필요성
 */

#include <stdio.h>

typedef struct // 상, 하위 각 2바이트
{
 unsigned short upper;
 unsigned short lower;
}UpLowShort;

typedef union uniBuf
{
 int iBuf;
 char arrBuf[4]; // 아스키 코드 1바이트씩 뽑아내기 위한 배열
 UpLowShort ULBuf;
}UniBuf;

int main()
{
 UniBuf buf;
 printf("정수 입력: ");
 scanf("%d", &(buf.iBuf)); // int형 정수 입력받음

 printf("상위 2바이트 : %u \n", buf.ULBuf.upper);
 printf("하위 2바이트 : %u \n", buf.ULBuf.lower);

 printf("최상위 1바이트 아스키 코드: %c \n", buf.arrBuf[0]);
 printf("최하위 1바이트 아스키 코드: %c \n", buf.arrBuf[3]);

 return 0;
}



#include <stdio.h>

typedef enum syllable
{
 Do = 1, Re = 2, Mi = 3, Fa = 4, So = 5, La = 6, Ti = 7
} Syllable;

void Sound(Syllable sy)
{
 switch (sy)
 {
 case Do:
  puts("도는 하얀 도라지 ♪"); return;
 case Re:
  puts("레는 둥근 레코드 ♩"); return;
 case Mi:
  puts("미는 파란 미나리 ♩♪"); return;
 case Fa:
  puts("파는 예쁜 파랑새 ♪♭"); return;
 case So:
  puts("솔은 작은 솔방울 ♩♪♪"); return;
 case La:
  puts("라는 라디오고요~ ♪♩♭♩"); return;
 case Ti:
  puts("시는 졸졸 시냇물 ♩♭♩♪"); return;
 }
 puts("다 함께 부르세~ 도레미파 솔라시도 솔 도~ 짠~");
}

int main(void)
{
 int tone; // == Syllable tone;
 for (tone = Do; tone <= Ti; tone++)
  Sound(tone);
 return 0;
}

728x90

하나의 문자를 출력하는 함수


int putchar(int c);

int fputc(int c, FILE * stream);

 // *stdout은 모니터 출력, putchar함수와 동일한 결과


하나의 문자를 입력받는 함수


int getchar(void);

int fgetc(FILE * stream);  

// *stdin은 키보드로 문자 입력받음


파일의 끝에 도달하거나 함수호출 실패 시 EOF(End Of File) 반환

EOF는 파일의 끝을 알리기 위한 상수 -1 로 정의된다.



/*

  문자열 관련 입출력 함수

*/


#include <stdio.h>


int main()

{

  int ch1 = getchar();  // 문자 입력

  int ch2 = fgetc(stdin);  // 엔터 키 입력


  putchar(ch1);    // 문자 출력

  fputc(ch2, stdout);  // 엔터키 출력

  

  return 0;

}


/* ouput : 

p

p

두 번째 문자가 엔터 키이므로 하나의 문자가 입력되고 출력된것처럼 착각할 수 있음

(아스키 코드값이 10인 "\n")

*/



/*

  문자열 입출력에서의 EOF

  함수 호출 실패 또는 윈도에서 CTRL+Z 가 입력되는 경우(linux : Ctrl+D)

*/


#include <stdio.h>


int main()

{

  int ch;


  while (1)

  {

    ch = getchar();

    if (ch == EOF)

      break;

    putchar(ch);

  }


  return 0;

}



int getchar(void);

int fgetc(FILE * stream);


1바이트 크기 문자를 반환하는데, 반환형이 int인 이유

: char를 unsigned char로 처리하는 컴파일러도 존재


하지만 EOF는 -1로 정의된 상수

변환의 과정에서 양수로 형 변현될 수 있으므로

int형 변수는 기본적으로 signed int이므로 -1 반환에 문제없음



int getchar(void);

int fgetc(FILE * stream);


1바이트 크기 문자를 반환하는데, 반환형이 int인 이유?

: char를 unsigned char로 처리하는 컴파일러도 존재

하지만 EOF는 -1로 정의된 상수인데 

변환의 과정에서 양수로 형 변현될 수 있다.


int형 변수는 기본적으로 signed int이므로 -1 반환에 문제없음



/*

getchar,putchar 함수 이용


소-대문자 변환 출력 (알파벳 이외의 문자는 오류메시지 출력)

*/


#include <stdio.h>


int ConvCase(int ch);


int main()

{

  int ch;


  printf("대<-소문자 변환 : ");

  ch = getchar();

  ch = ConvCase(ch);


  if (ch == -1)  // 예외처리 (문자 아닐때)

  {

    puts("오류");

    return -1;

  }

  putchar(ch);

  puts("");

  

  return 0;

}


int ConvCase(int ch)

{

  int ASCII_Diff = 'a' - 'A';  

  // 아스키 코드에서 모든 영문 대소문자 차가 32로 같음 /  97-65  a-A,  98-66  b-B


  if (ch >= 'A' && ch <= 'Z')

    return ch + ASCII_Diff;

  if (ch >= 'a' && ch <= 'z')

    return ch - ASCII_Diff;

  else

    return -1;

}



/*

문자열 출력함수 puts, fputs


int puts(const char * s);   개행이 포함됨 ("\n")


int fputs(const char * s, FILE * stream);  

두번째 인자 stdout쓸 때 차이는 fputs에는 개행 ("\n")이 포함되지 않음

*/


#include <stdio.h>


int main()

{  

  char * str = "simple string";


  printf("1. puts test \n");

  puts(str);

  puts("So Simple String");


  printf("2. fputs test \n");

  fputs(str, stdout); 

  printf("\n");  //  fputs에는 개행이 포함되지 않음


  printf("3. END \n");


  return 0;

}



/*

  gets, fgets

  char * gets(char * s);

  char * fgets(char * s, int n, FILE * stream);  

  

  // int n은 총 문자열의 길이 / sizeof(str)

  NULL문자를 저장할 공간도 필요

*/


#include <stdio.h>


int main()

{  

  char str[7];  // 6 + NULL


  int i;

  for (i = 0; i < 3; i++)

  {

    fgets(str, sizeof(str), stdin);  // stdin 문자열 입력

    printf("Read %d: %s \n", i + 1, str);

  }


  return 0;

}


// fgets함수는 \n을 만날때까지 문자열을 읽어들이며,

 \n을 버리지않고 문자열의 일부에 포함시킨다.


중간에 삽입된 공백문자도 문자열의 일부로 받아들인다.



출력버퍼를 비우는 fflush 함수

int fflush(FILE * stream);


fflush(stdout);


출력 버퍼를 비운다는 것은 저장된 데이터를 지우는 것이 아님.

출력버퍼에 저장된 데이터를 목적지로 최종전송하는 것.


입력버퍼를 비운다는 것은 삭제의 의미에 가까움.


fflush(stdin); // 컴파일러에 따라 허용되는 경우도 있으나 사용하지 않는 것이 좋다.


입력버퍼를 비우는 함수가 따로 존재하지 않는 것은

read하면 비워지기 때문


이를 이용해 아래와 같은 함수를 정의할 수 있다.



void ClearLineFromReadBuffer(void)

{

while(getchar()!='\n');    // 개행문자를 만날때까지 읽어들임, 읽어들인 문자는 따로 저장하지않고 비워지게 된다.

}



/*

입력버퍼를 비우는 예제

*/

#include<stdio.h>


void ClearLineFrom_ReadBuffer(void);


int main(void)

{

char perID[7];

char name[10];


fputs("주민번호 앞 6자리 입력 : ", stdout);

fgets(perID, sizeof(perID), stdin);

ClearLineFrom_ReadBuffer();


fputs("이름 입력 : ", stdout);

fgets(name, sizeof(name), stdin);

printf("주민번호 : %s \n", perID);

printf("이름 : %s \n", name);


return 0;

}


void ClearLineFrom_ReadBuffer(void)

{

while (getchar() != '\n');

}




/*
문자열 길이를 반환하는 함수 strlen
strlen을 활용하고 문자열의 마지막에 삽입되는 null을 없앤 길이 출력
*/
#include<stdio.h>

void RemoveNULL(char str[]);

int main(void)
{
char str[100];
printf("문자열 입력 : ");
fgets(str, sizeof(str), stdin);
printf("길이 : %d, 내용: %s \n", strlen(str), str);

RemoveNULL(str);
printf("RemoveNULL : %d, 내용: %s \n", strlen(str), str);

return 0;
}

void RemoveNULL(char str[])
{
int lengh = strlen(str);
str[lengh - 1] = 0;
}



문자열을 복사하는 함수 strcpy (string copy)

char * strcpy(char * dest, const char * src);


src에서 dest로 복사 (복사된 문자열의 주소 값 반환)


char * strncpy(char * dest, const char * src, size_t n);

복사되는 문자열의 길이를 n으로 제한


메모리 공간의 효율적인 사용과 프로그램 안정성을 위해 strncpy를 더 많이 쓴다.




/*

문자열을 복사하는 함수 strcpy (string copy)

strncpy (복사되는 문자열의 길이를 n으로 제한)

char * strncpy(char * dest, const char * src, size_t n);

*/

#include<stdio.h>


int main(void)

{

char str1[20] = "1234567890";

char str2[20];

char str3[5];

// str1의 문자열이 str1에 복사됨

strncpy(str2, str1);

puts(str2);


// str1의 문자열이 str3에 복사됨(str3의 길이만큼 제한)

strncpy(str3, str1, sizeof(str3));

puts(str3);


strncpy(str3, str1, sizeof(str3) - 1); // null문자 넣어줄 공간을 빼고 복사

str3[sizeof(str3) - 1] = 0; // null문자를 넣어줌

puts(str3);


return 0;

}



/*

문자열을 덧붙이는 함수 strcat  (STRing conCATenation)

strncat (복사되는 문자열의 길이를 n으로 제한)

char * strncat(char * dest, const char * src, size_t n);

*/

#include<stdio.h>


int main(void)

{

char str1[20] = "First~";

char str2[20] = "Second";


char str3[20] = "Simple num: ";

char str4[20] = "1234567890";


//str2를 str1 뒤에 덧붙임

strcat(str1, str2);

puts(str1);


//n만큼 덧붙이되 null문자 맨뒤에 붙여짐

strncat(str3, str4, 7); // 1~7까지 붙여지고 끝에 NULL이 포함

puts(str3);


return 0;

}




문자열 비교함수 strcmp (string compare)

int strcmp(const char * s1, const char * s2);
int strncmp(const char * s1, const char * s2, size_t n);

두 문자열의 내용이 같으면 0, 같지않으면 0이 아닌값 반환

s1이 더 크면 0보다 큰 값 반환, s2가 더 크면 0보다 작은 값 반환

크고 작음은 아스키 코드값을 근거로 함 
(아스키코드 값이 더 크면 더 큰 문자열로 인식)

문자열에선 가장 앞의 문자를 가지고 비교
(문자열의 끝에는 null이 포함)

/*
문자열 비교함수 strcmp (string compare)

int strcmp(const char * s1, const char * s2);
int strncmp(const char * s1, const char * s2, size_t n);
*/

#include<stdio.h>

int main(void)
{
char str1[20];
char str2[20];
printf("문자열 입력 1: ");
scanf("%s", str1);
printf("문자열 입력 2: ");
scanf("%s", str2);

if (!strcmp(str1, str2)) // 0이 아닌 값은 true, 같을 때 0(false) 반환 == if(strcmp(str1, str2)==0
{
puts("두 문자열 일치");
}
else
{
puts("두 문자열 불일치");

if (!strncmp(str1, str2, 3)) // 3글자까지만 비교
puts("그러나 앞 세 글자는 동일");
}

return 0;
}


문자열의 내용을 변환해주는 함수들


int atoi(const char * str); // atoi : 문자열의 내용 -> int형 변환

long atol(const char * str); // atol : 문자열의 내용 -> long형 변환

double atof(const char * str); // atof : 문자열의 내용 -> double형 변환


/*

그 이외의 변환함수 atoi, atol, atof

*/


#include<stdio.h>


int main(void)

{

char str[20];


fputs("정수 입력: ", stdout);

fgets(str, sizeof(str), stdin);

int result = atoi(str);

printf("%d \n", result*result);


return 0;

}

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

파일 입출력 공부  (0) 2016.08.25
구조체 공부  (0) 2016.08.23
c 복습-문제풀이2  (0) 2016.08.22
섭씨->화씨 변환, 최대 공약수 구하기  (0) 2016.05.03
2016-04-07-업무일지_구조체 및 함수  (0) 2016.04.07
728x90


/*  Pointer Swap (double pointer 이용)  반드시 포인터에 저장된 값을 swap하도록! */

#include<stdio.h>


void SwapPtr(int* *dptr1, int* *dptr2)

{  

  int *temp = *dptr1;  // type이 일치해야 함

  *dptr1 = *dptr2;

  *dptr2 = temp;  

  //(*dptr1 = &num1 = ptr1 / *dptr2 = &num2 = ptr2) 

}


int main(void)

{

  int num1 = 10;

  int num2 = 20;


  int *ptr1;

  int *ptr2;


  ptr1 = &num1;

  ptr2 = &num2;

  printf("*ptr1, *ptr2 : %d  %d \n", *ptr1, *ptr2);


  SwapPtr(&ptr1, &ptr2);

  printf("*ptr1, *ptr2 : %d %d \n", *ptr1, *ptr2);

  return 0;

}



/*  Pointer Array Type 


// int ** dptr = &ptrArr[0]; / *(dptr[0]) = *(ptrArr[0]) = *(ptr1) = num1

// *ptr1=&num1;

*/

#include<stdio.h>


int main(void)

{

  int num1 = 10;

  int num2 = 20;

  int num3 = 30;


  int *ptr1 = &num1;

  int *ptr2 = &num2;

  int *ptr3 = &num3;

  

  int *ptrArr[] = { ptr1, ptr2, ptr3 }; 

  int ** dptr = ptrArr; // ptrArr이 double pointer,  ptrArr의 주소값에 대입하는 것

    

  printf("%d %d %d \n", *(ptrArr[0]), *(ptrArr[1]), *(ptrArr[2]));

  printf("%d %d %d \n", *(dptr[0]), *(dptr[1]), *(dptr[2]));


  return 0;

}



ptrArr은 int형 포인터를 요소로 갖는 배열의 이름이기 때문에 더블 포인터형


따라서 

 int **dptr = ptrArr;





/*  

  int * maxPtr;  int * minPtr;  int arr[5];

  함수 MaxAndMin, 위 배열과 두 포인터 변수에 대한 정보 전달

  maxPtr에 가장 큰값이 저장된 배열요소 주소 값, minPtr에 가장 작은 값이 저장된 배열요소 주소값 저장

*/


#include <stdio.h>


void MaxAndMin(int *arr, int size, int **MaxPtr, int **MinPtr);


int main()

{  

  int * maxPtr;

  int * minPtr;

  int arr[5];


  int i;

  for (i = 0; i < 5; i++)

  {

    printf("%d번째 정수 입력 : ", i + 1);

    scanf("%d", &arr[i]);

  }  


  MaxAndMin(arr, sizeof(arr) / sizeof(int), &maxPtr, &minPtr);

  printf("최대 : %d, 최소 : %d \n", *maxPtr, *minPtr);


  return 0;

}


void MaxAndMin(int *arr, int size, int **MaxPtr, int **MinPtr)

{

  int *max;

  int *min;  

  max = min = &arr[0];


  int i;

  for(i=0; i<size; i++)

  {

    if (*max < arr[i])

      max = &arr[i];


    if (*min > arr[i])

      min = &arr[i];    

  }


  *MaxPtr = max;

  *MinPtr = min;

}


/*  

  2d Array Address

*/


#include <stdio.h>


int main()

{

  int arr2d[3][3];

  printf("%p \n", arr2d);

  printf("%p \n", arr2d[0]);

  printf("%p \n \n", &arr2d[0][0]);


  printf("%p \n", arr2d[1]);  // +12  sizeof(int) x 3

  printf("%p \n", &arr2d[1][0]);  // +12


  printf("sizeof(arr2d): %d \n", sizeof(arr2d));  // 배열 이름의 size는 배열 전체의 크기

  printf("sizeof(arr2d[0]): %d \n", sizeof(arr2d[0]));  // 각 행의 size반환 (12)

  printf("sizeof(arr2d[1]): %d \n", sizeof(arr2d[1]));

  printf("sizeof(arr2d[2]): %d \n", sizeof(arr2d[2]));


  return 0;

}


2차원 배열이름의 포인터 형은 가리키는 대상과 가로 길이에 의존적이다. int [4][2]  [6][2]


int arr[3][4]의 포인터 형은

int (*ptr) [4];  //  [4]는 가로길이

포인터 연산시 4칸씩 건너뛰는 int형 변수 포인터


포인터 연산(+,-)을 했을 때의 주소값을 생각하면 된다.



/*  

  2차원 배열의 포인터 형

*/


#include <stdio.h>


int main()

{

  int arr1[2][2] = {

    {1,2},{3,4}

  };


  int arr2[4][2] = {

    {1,2,},{3,4},{5,6},{7,8}

  };


  int(*ptr)[2];

  int i;


  ptr = arr1;  // arr은 상수, ptr은 변수이므로 ptr이 arr1도 arr2도 가리킬 수 있다.

  printf("** Show 2,2 arr1 **\n");

  for (i = 0; i < 2; i++)

    printf("%d %d \n", ptr[i][0], ptr[i][1]);


  ptr = arr2;

  printf("** Show 2,2 arr1 **\n");

  for (i = 0; i < 4; i++)

    printf("%d %d \n", ptr[i][0], ptr[i][1]);


  return 0;

}


int* whoA [4];  // 포인터 배열 : 포인터 변수로 이루어진 배열

int (*whoB) [4];  // 배열 포인터 : 배열을 가리킬 수 있는 포인터 변수



함수포인터 


(매개변수)(반환형)


무엇을 전달받고 

무엇을 반환하는지 이해


/*  Function Pointer

num1,num2값을 더해서 보여주는 함수

string을 보여주는 함수를 메인함수에서 함수 포인터 변수로 호출

*/

#include<stdio.h>


void ShowAdd(int n1, int n2);

void ShowString(char * str);


int main(void)

{

  int num1 = 10, num2 = 20;

  char * str = "String";


  void(*Add_FuncPointer)(int, int) = ShowAdd;  // (int n1, int n2)

  void(*String_FuncPointer)(char *) = ShowString;


  Add_FuncPointer(num1,num2);

  String_FuncPointer(str);

  

  return 0;

}


void ShowAdd(int n1, int n2)

{

  printf("%d + %d = %d \n", n1, n2, n1 + n2);

}


void ShowString(char * str)

{

  printf("%s \n", str);

}



/*  Useful Function Pointer

*/

#include<stdio.h>


int WhoIsFirst(int age1, int age2, int(cmp)(int n1, int n2));

int OlderFirst(int age1, int age2);

int YoungerFirst(int age1, int age2);


int main(void)

{

  int age1 = 20;

  int age2 = 30;

  int first;


  printf("늙은 사람이 먼저 입장 \n");

  first = WhoIsFirst(age1, age2, OlderFirst);

  printf("%d세와 %d세중 %d세가 먼저 입장 \n\n", age1,age2,first);


  printf("젊은 사람이 먼저 입장 \n");

  first = WhoIsFirst(age1, age2, YoungerFirst);

  printf("%d세와 %d세중 %d세가 먼저 입장\n\n", age1,age2,first);


  return 0;

}


int WhoIsFirst(int age1, int age2, int(cmp)(int n1, int n2))

{

  return cmp(age1, age2);

}


int OlderFirst(int age1, int age2)

{

  if (age1 > age2)

    return age1;

  else if (age1 < age2)

    return age2;

  else

    return 0;

}


int YoungerFirst(int age1, int age2)

{

  if (age1 < age2)

    return age1;

  else if (age1 < age2)

    return age2;

  else

    return 0;

}



/*  

  18-1

*/


#include <stdio.h>


int main()

{

  int * arr1[5];

  int * arr2[3][5];


  int **ptr = arr1;

  int (*ptr2)[5] = arr2;  


  return 0;

}



arr[1][0][1]

== ( (*arr)+1) [0][1]

== (* (* (arr+1) +0 ) )[1]

== *( * ( * ( arr+1) + 0 ) +1 ) )

== ( * ( arr[1]+0 ) )[1]

==  *(*(arr[1]+0)+1) 

==  *(arr[1][0]+1)



/*  

   argument count, argument vector

*/


#include <stdio.h>


int main(int argument_count, char *argument_vector[])

{

  int i = 0;

  printf("전달된 문자열 수 : %d \n", argument_count);


  for (i = 0; i < argument_count; i++)

    printf("%d번째 문자열: %s \n", i + 1, argument_vector[i]);

      

  return 0;

}


/*

output:

전달된 문자열 수 : 4

1번째 문자열: E:\Visual Studio 2015\Projects\C_Study\Debug\C_Study.exe

2번째 문자열: I

3번째 문자열: Hate

4번째 문자열: You!

*/




/*  

   Argument Vector Parameter Type

*/


#include <stdio.h>


void Show_All_String(int argc, char *argv[]);


int main()

{

  char *str[3] = {

    "수박수박쑤",

    "씨발라먹을",

    "씨프로그래밍"

  };

  Show_All_String(4, str);

        

  return 0;

}


void Show_All_String(int argc, char *argv[])

{

  int i;

  for (i = 0; i < argc; i++)

    printf("%s \n", argv[i]);

}



/*

  end of argument vector (null)

*/


#include <stdio.h>


int main(int argument_count, char *argument_vector[])

{

  int i = 0;

  printf("전달된 문자열 수 : %d \n", argument_count);


  while (argument_vector[i] != NULL)

  {

    printf("%d번째 문자열: %s \n", i + 1, argument_vector[i]);

    i++;

  }

  return 0;

}



728x90

ATmega128 포트가 범용 디지털 I/O로 사용될 경우, Read-Modify-Write 기능을 수행할 수 있음

:     I/O포트 입출력 변경하지않고도 동시에 입력과 출력의 기능을 수행할 수 있다.

입출력 방향의 변경없이 포트의 동작 방향이 달라질 수 있음


DDRx 레지스터 : 입출력의 방향을 설정, 각 비트를 1로 설정하면 출력, 0으로 설정하면 입력으로 결정

0 : PORTxn = 입력 -> 입력된 데이터가 PINxn에 저장.

1 : PORTxn = 출력 -> PORTxn의 데이터가 출력됨.


PORTx 레지스터 : 데이터 출력

PINx 레지스터 : 포트 입력 핀, 읽기만 가능하고 쓰기 불가



ATmega128에서 PORTG는 5개의 비트만 사용 (다른 PORT는 0-7까지 8개의 비트 사용)



Atmel/avr tools/avr toolchain/avr/include/avr/iom128.h

 /io.h

에서 SFR 등 비트 정의를 볼 수 있다.




PORT초기화 예)


DDRB = 0xFF;    // 포트 B 출력 설정

PORTB = 0x00;    // 출력 0 설정

DDRD = 0x00;    // 포트 D 입력 설정

PORTD = 0x00;    // 포트 D 내부 풀업 저항을 사용하기 위해 0으로 설정

SFIOR |= 1<<2;    // PUD 비트 1로 설정(풀업 저항 사용안함)



SFIOR의 PUD(Pull-up Disable) 비트를 1로 설정하면 ATmega128 내부에 있는 풀업저항을 사용하지 않는것이고 

PUD를 0으로 설정하면 내부 풀업저항을 사용하는 것이다. 


SFIOR의 PUD 비트의 초기값은 0으로 되어 있기 때문에 이 레지스터를 설정하지 않으면 내부 풀업저항을 사용하도록 설정되는 것과 같다. 





WIn32 API 수업


// 그림판

#include <Windows.h>


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE g_hInst;

LPCTSTR lpszClass = TEXT("Text Out 124");  // Title 명


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst = hInstance;


WndClass.cbClsExtra = 0;

WndClass.cbWndExtra = 0;

WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);

WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

WndClass.hInstance = hInstance;

WndClass.lpfnWndProc = WndProc;

WndClass.lpszClassName = lpszClass;

WndClass.lpszMenuName = NULL;

WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

RegisterClass(&WndClass);


hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, (HMENU)NULL, hInstance, NULL);

ShowWindow(hWnd, nCmdShow);


while (GetMessage(&Message, NULL, 0, 0))

{

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT pa;

static int x = 100;

static int y = 100;

static int a = 0;


static BOOL bNowDraw = FALSE;


switch (iMessage)

{

case WM_DESTROY:

PostQuitMessage(0);    // 종료

return 0;


case WM_PAINT:

hdc = BeginPaint(hWnd, &pa);

TextOut(hdc, x, y, TEXT("A"), 1);

EndPaint(hWnd, &pa);

return 0;


case WM_KEYDOWN:

switch (wParam)

{

case VK_LEFT:

x -= 8;

break;

case VK_RIGHT:

x += 8;

break;

case VK_UP:

y -= 8;

break;

case VK_DOWN:

y += 8;

break;

case VK_SPACE:

a++;

a %= 2;

break;

}

InvalidateRect(hWnd, NULL, FALSE);

return 0;


case WM_LBUTTONDOWN:

x = LOWORD(lParam);

y = HIWORD(lParam);

bNowDraw = true;

return 0;

case WM_MOUSEMOVE:

if (bNowDraw == TRUE)

{

hdc = GetDC(hWnd);

MoveToEx(hdc, x, y, NULL);

x = LOWORD(lParam);

y = HIWORD(lParam);

LineTo(hdc, x, y);

ReleaseDC(hWnd, hdc);

}

return 0;

case WM_LBUTTONUP:

bNowDraw = FALSE;

return 0;

case WM_LBUTTONDBLCLK:

MessageBox(hWnd, TEXT("더블클릭"), TEXT("메세지박스"), MB_YESNO);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}





// 타이머

#include <Windows.h>


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE g_hInst;

LPCTSTR lpszClass = TEXT("Timer");  // Title 명


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst = hInstance;


WndClass.cbClsExtra = 0;

WndClass.cbWndExtra = 0;

WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);

WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

WndClass.hInstance = hInstance;

WndClass.lpfnWndProc = WndProc;

WndClass.lpszClassName = lpszClass;

WndClass.lpszMenuName = NULL;

WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

RegisterClass(&WndClass);


hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, 300, 400, 400, 300, NULL, (HMENU)NULL, hInstance, NULL);

ShowWindow(hWnd, nCmdShow);


while (GetMessage(&Message, NULL, 0, 0))

{

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT pa;

SYSTEMTIME st;

static TCHAR sTime[128];


switch (iMessage)

{

case WM_CREATE:

SetTimer(hWnd, 1, 1000, NULL);

return 0;

case WM_TIMER:

GetLocalTime(&st);

wsprintf(sTime, TEXT("지금 시간은 %d:%d:%d입니다."), st.wHour, st.wMinute, st.wSecond);

InvalidateRect(hWnd, NULL, TRUE);

return 0;


case WM_DESTROY:

PostQuitMessage(0);    // 종료

return 0;


case WM_PAINT:

hdc = BeginPaint(hWnd, &pa);

TextOut(hdc, 100, 100, sTime, lstrlen(sTime));

EndPaint(hWnd, &pa);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}



// 3초에 한번 덧셈문제 출제


#include <Windows.h>


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE g_hInst;

LPCTSTR lpszClass = TEXT("Timer");  // Title 명


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)

{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst = hInstance;


WndClass.cbClsExtra = 0;

WndClass.cbWndExtra = 0;

WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);

WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

WndClass.hInstance = hInstance;

WndClass.lpfnWndProc = WndProc;

WndClass.lpszClassName = lpszClass;

WndClass.lpszMenuName = NULL;

WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

RegisterClass(&WndClass);


hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, 300, 400, 400, 300, NULL, (HMENU)NULL, hInstance, NULL);

ShowWindow(hWnd, nCmdShow);


while (GetMessage(&Message, NULL, 0, 0))

{

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return (int)Message.wParam;

}


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT pa;

SYSTEMTIME st;

static TCHAR sTime[128];


int a = rand() % 30;

int b = rand() % 30;


switch (iMessage)

{

case WM_CREATE:

SetTimer(hWnd, 1, 3000, NULL);

return 0;

case WM_TIMER:

GetLocalTime(&st);

wsprintf(sTime, TEXT("%d+%d=?"), a, b);

InvalidateRect(hWnd, NULL, TRUE);

return 0;


case WM_DESTROY:

KillTimer(hWnd, 1);

PostQuitMessage(0); // 종료

return 0;


case WM_PAINT:

hdc = BeginPaint(hWnd, &pa);

TextOut(hdc, 100, 100, sTime, lstrlen(sTime));

EndPaint(hWnd, &pa);

return 0;

}

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}




'Study > Embedded' 카테고리의 다른 글

아두이노 기초  (0) 2017.05.25
ATMEGA USART-C# App 연동  (0) 2016.12.12
7/8 LCD제어2  (0) 2016.07.08
7/5 LCD 제어  (0) 2016.07.05
5/26 업무일지 PC PWM을 이용한 LED 밝기 조절  (0) 2016.05.26
728x90

LCD 모듈 제어에 기본적으로 수행되야하는 기능


- IR에 명령 쓰기

- DR에 데이터 쓰기

- 명령 처리 시간을 고려한 시간 지연



LCD 컨트롤러 명령어 표


신호 제어선 정의 예


#define  CLR   0x01      //Clear Display 명령
#define  HOME  0x02      //Return Home 명령 
#define  FNC   0x38      //Function Set 명령
                		 //Data Length = 8bit. 행수 2행


void LCD_PortSetting(void)
{
  DDRC = 0xFF;        //데이터 라인
  DDRD = 0xFF;        //0~2 제어핀 사용
  //D0: RS ,D1: R/W ,D2: EN
}


명령 레지스터에 명령어 쓰기 함수


위의 명령어 표와 같은 명령을 수행할 함수이다. 수행결과는 함수를 호출하는 함수에 반환될 필요가 없다.


void IR_Write(unsigned char Data) { PORTD &= 0xFC; // RS = 0, RW = 0 (Write Enable), 0xFC = 1111 1100) _delay_us(1); // Enable 까지 최소 대기 시간 40ns PORTD |= 0x04; // En _delay_us(1); PORTC = Data; _delay_us(1); PORTD = 0b00000010; // Disable. Read Mode }

PORTD &= 0XFC // PORTD= PORTD & 0XFC



데이터 레지스터에 데이터 쓰기 함수


DDRAM 또는 CGRAM에 데이터를 쓰는 함수로서 사용되는 여러가지 명령어가 제시되어 있다.

void DR_Write(unsigned char Data)
{
  PORTD = 0x01;    //RS = 1, Write Enable
  _delay_us(1);    //Enable 까지 최소 대기 시간 40ns
  PORTD |= 0x04;    //Enable 활성화
  _delay_us(1);    //
  PORTC = Data;    //Data
  _delay_us(1);
  PORTD = 0b00000010;  //Disable, Read Mode  
}













'Study > Embedded' 카테고리의 다른 글

ATMEGA USART-C# App 연동  (0) 2016.12.12
7/21 Atmega128 포트/ win32 api  (0) 2016.07.21
7/5 LCD 제어  (0) 2016.07.05
5/26 업무일지 PC PWM을 이용한 LED 밝기 조절  (0) 2016.05.26
2016-05-19 업무일지-Atmega2560_LCD출력  (0) 2016.05.19

+ Recent posts