728x90

https://msdn.microsoft.com/en-us/library/323b6b3k.aspx?f=255&MSPPError=-2147217396


참고



size_t는 대상 플랫폼에 따라 unsigned __int64 또는 unsigned integer를 뜻한다.

CRTDEFS.H 또는 다른 include 파일에 의해 정의된다.



vcruntime.h

// Definitions of common types
#ifdef _WIN64
    typedef unsigned __int64 size_t;
    typedef __int64          ptrdiff_t;
    typedef __int64          intptr_t;
#else
    typedef unsigned int     size_t;
    typedef int              ptrdiff_t;
    typedef int              intptr_t;
#endif


#define의 유용성을 생각해보면 필요해 따라 type을 바꿔 쓸 수도 있을 것이다.

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

enum, auto, 구조체  (0) 2017.03.24
C 복습-콘솔 입출력  (0) 2017.02.11
c 복습  (0) 2016.11.03
파일의 분할과 헤더파일의 디자인  (0) 2016.08.30
선행처리기와 매크로2  (0) 2016.08.30
728x90

열거형

열거형의 멤버는 차례대로 0의 값을 갖는다. 하지만 선언할 때 초기값을 원하는 값으로 설정할 수도 있다.

#include <stdio.h>
 
enum season
{
	spring = 1, summer, fall, winter
}Season;
 
void main(void)
{	
	printf("계절을 고르세요 -> \n 1.봄  2.여름  3.가을  4.겨울 : ");
	scanf("%i" , &Season);
	
	switch (Season)
	{
	case spring:
		printf("봄은 꽃 피는 계절\n");
		break;
	case summer:
		printf("여름은 바캉스의 계절\n");
		break;
	case fall:
		printf("가을은 낙엽이 밟히는 계절\n");
		break;
	case winter:
		printf("겨울은 눈 내리는 계절\n");
		break;
	default:
		break;
	}
}


auto 키워드


원래 C에서의 의미와 C++에서의 의미

https://dojang.io/mod/page/view.php?id=803


c에서도 다음과 같이 선언하고 실행 결과를 얻을 수 있다. (http://codepad.org/kzaEWQRj)

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

auto func() {
	return 10;
}

void main() {
	int a;
	a = func();
	printf("%d \n", a);
}

Output:
1
10 


구조체 변수를 함수 매개변수에 사용하기


구조체 변수는 대입 연산이 가능하므로 함수의 매개변수로 주거나 반환받을 수 있다.

예를 들어, 함수에서 여러 개의 값을 구조체로 묶어 동시에 반환할 수 있다.


두 변수의 값을 바꾸는 함수는 포인터가 필요하지만, 구조체 변수를 사용해 값을 주고 받으면 

포인터 없이도 두 변수의 값을 바꾸는 함수를 만들 수 있다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
 
typedef struct
{
    double left;
    double right;
} vision;
 
vision exchange(vision);    // 구조체를 통해 두 변수의 값을 교환
 
int main(void) {
    vision robot;
    printf("시력 입력 : ");
    scanf_s("%lf %lf"&(robot.left), &(robot.right));
    robot = exchange(robot);
    printf("바뀐 시력 : %.1lf, %.1lf \n", robot.left, robot.right);
 
    return 0;
}
 
vision exchange(vision robot)
{
    double temp;
    temp = robot.left;
    robot.left = robot.right;
    robot.right = temp;
 
    return robot;
};
cs


Output :

시력 입력 : 0.5 1.5

바뀐 시력 : 1.5, 0.5




비트 필드 구조체

1
2
3
4
5
6
struct children
{
    unsigned int son 2;
    unsigned int daughter 2;
    unsigned int pet 3;
};
cs

비트 필드 구조체를 이용하면, 멤버의 크기를 비트 단위로 설정할 수 있다. colon(:) 뒤의 숫자가 멤버가 사용하는 비트 수 이다.

저장할 수 있는 값의 범위는 비트 수에 따라 2의 n승. (2비트는 0~3, 3비트는 0~7)


signed를 이용하면 음수도 저장할 수 있으나, 부호비트 처리방식이 시스템에 따라 다를 수 있으므로 unsigned를 주로 사용



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
typedef struct
{
    unsigned int son : 4;
    unsigned int daughter : 4;
} children;
 
int main(void) {
    children child = { 12 };    // 구조체 변수 초기화
    int sum;
 
    printf("children 구조체 크기 : %d 바이트 \n"sizeof(child) );
    sum = child.son + child.daughter;
    printf("자식 수 : %d \n", sum);
}
cs

Output : 

children 구조체 크기 : 4 바이트

자식 수 : 3


자료형은 정수형만 사용하며, 멤버가 가질 수 있는 최대 비트 수를 결정

unsigned int형은 최대 32비트까지만 지정 가능. 

위에서 children 구조체 멤버의 비트 수를 모두 합해도 7비트지만, unsigned int형 크기인 32비트가 메모리에 할당된다.

(멤버가 할당되지 않은 영역은 사용하지 않는 영역이 됨)


만약 기본 할당된 영역을 넘어서면 새로운 바이트 공간을 사용하면서 멤버가 할당되게 된다.


패딩 비트 (사용하지 않는 공간)

만약, 멤버의 이름을 생략하면 사용하지 않는 패딩 비트가 된다.



                 

 

 

 b

 b

 b

 패딩

  비트

 a


 

 

 

 

 c 

 c

 c 

 c 



배열을 멤버로 선언하거나 멤버의 주소를 직접 구할 순 없다.

키보드로 입력을 받아들이려면 별도의 변수에 입력한 후에 멤버에 대입해야 한다.

6행의 주석을 제거하고 실행한다면, 에러가 난다. 비트 필드 구조체 멤버의 주소를 직접 구할 수 없기 때문이다.


1
2
3
4
5
6
7
8
int main(void) {
    children child;
    int temp;
    scanf("%d"&temp);
    child.son = temp;
    //* scanf("%d", child.son); /* 에러 */
    printf("%d \n", child.son);
}
cs



구조체 포인터와 -> 연산자


child. 과 같이 구조체 멤버에 직접 접근하는 거 외에도, 구조체를 가리키는 포인터를 통해 멤버에 접근할 수도 있다.


구조체 변수 전체의 주소를 구해, 그 값을 저장하는 것이 바로 구조체 포인터이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
 
typedef struct
{
    int son;
    int daughter;
} children;
 
int main(void) {
    children child = { 12 };
    children *ps = &child;
        
    printf("아들은 몇 명? : %d \n", (*ps).son);
    printf("딸은 몇 명? : %d \n", ps->daughter);
}
cs


children 구조체를 가리키는 포인터 변수 ps에, child의 주소 연산 (&)을 통해 구한 변수의 주소값을 저장하는 것이다.

그러면 ps는 구조체 변수 child를 가리키게 된다.


멤버 접근 연산자인 . 연산자가 * 연산자 보다 우선 순위가 높기 때문에, * 연산자가 먼저 수행될 수 있도록 다음과 같이 괄호로 묶는 것이다.

(*ps).son


이것을 사용하기 편하게 해주는 것이 -> 연산자이다.

(*ps).son    ==      ps->son


 

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

size_t  (0) 2017.05.29
C 복습-콘솔 입출력  (0) 2017.02.11
c 복습  (0) 2016.11.03
파일의 분할과 헤더파일의 디자인  (0) 2016.08.30
선행처리기와 매크로2  (0) 2016.08.30
728x90

 

 

// 문자를 입력받고 입력된 문자의 수 출력하기

#include <stdio.h>


void ClearLineFrom_ReadBuffer(void)

{

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

}


int main(void)

{

int total = 0;

char input;


while (1)

{

fputs("문자 입력 (종료하려면 Ctrl+Z): ", stdout);

input = getchar();

if (input == EOF)

break;


ClearLineFrom_ReadBuffer();

total++;

}


printf("입력된 문자 갯수: %d \n", total);

return 0;

}



// 숫자 n개만큼 입력받고 최대,최소값, 합, 평균 출력하기

#include <stdio.h>


#define data_num 7


int main(void)

{

int iNum;

int cnt=1; // 최초1회 입력받고 이후 loop

int max, min, sum=0;

double avg;

// 최소값과 최대값이 0으로 고정되지 않도록 최초 1회 입력받음

printf("정수 입력 : ");

scanf("%d", &iNum);

max = min = sum = iNum;


// 이후 남은 자료갯수만큼 입력받음

for (cnt; cnt < data_num; cnt++)

{

printf("정수 입력 : ");

scanf("%d", &iNum);

if (iNum > max)

max = iNum;


if (iNum < min)

min = iNum;

sum = sum + iNum;

}


avg = (double)sum / data_num;

printf("최대값 %d, 최소값 %d, 합 %d, 평균 %f \n", max, min, sum, avg);


return 0;

}


// 10진수->n진수 변환 (재귀함수)

#include <stdio.h>

#define digit 2    // 2진수


void conv_bin(num)

{

int bin;

if (num > 0)

{

bin = num % digit;

num = num / digit;

conv_bin(num);

printf("%d", bin);

}

else

return;

}


int main(void)

{

int num;

scanf("%d", &num);

conv_bin(num);


return 0;

}

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

size_t  (0) 2017.05.29
enum, auto, 구조체  (0) 2017.03.24
c 복습  (0) 2016.11.03
파일의 분할과 헤더파일의 디자인  (0) 2016.08.30
선행처리기와 매크로2  (0) 2016.08.30
728x90
공용체 union 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
union smart
{
    int A;    short B;
    int C;    char D;
}obj;
 
int main()
{    
    obj.A = 0x12345678;    obj.B = 0x12345678;
    obj.C = 0x12345678;    obj.D = 0x12345678;
        
    printf("Size of Smart: %d\n"sizeof(union smart));
    printf("%x\n", obj.A);    printf("%x\n", obj.B);
    printf("%x\n", obj.C);    printf("%x\n", obj.D);
    return 0;
}
cs


위 공용체의 size는 4byte


Little Endian

78 

 56

 34

 12


obj.A=12345678   // int A

obj.B=5678        // short B

obj.C=12345678  // int C

obj.D=78           // char D



함수 포인터


: 함수를 가리키는 포인터 


int SoSimple(int num1, int num2) { ....}


위 함수에 대한 함수 포인터 변수 선언

int (*fptr) (int, int);



함수 포인터 변수에 SoSimple 주소값 저장

fptr = SoSimple



이는 fptr(3, 4);    // SoSimple(3, 4)와 동일한 결과


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
 
void Add(int n1, int n2)
{
    printf("%d + %d = %d \n", n1, n2, n1 + n2);
}
 
int main()
{    
    int num1 = 10, num2 = 20;
 
    void(*fptr)(intint= Add;
    fptr(num1, num2);
    return 0;
}
cs

Output :

10 + 20 = 30



void형 포인터 변수는 어떠한 변수의 주소 값이든 담을 수 있지만(함수의 주소값도)

값의 변경이나 참조를 비롯한 어떤 포인터 연산도 불가

: type 정보가 없기 때문에 당연


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
void voidFunc(void)
{
    printf("void ptr test\n");
}
 
int main()
{
    void(*fptr)(void= voidFunc;
    fptr();
    return 0;
}
cs

Output :

void ptr test


함수 포인터 사용 이유 (참고 : http://norux.me/8)  


①프로그램 코드의 간결

②중복 코드를 줄이기 위해서

③상황에 따라 해당되는 함수를 호출할 수 있음


그외에 참고 https://kldp.org/node/133026




가변 인자 함수


num = Sum(3, 1, 2, 3);    // 3은 이후에 전달되는 인자의 갯수 (1,2,3)


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int Sum(int n, ...)    // 가변 인자에 대한 선언
{
    printf("n=%d \n", n);    
    return 0;
}
 
int main()
{    
    Sum(3123);
    return 0;
}
cs

Output :

n=3


가변 인자 함수의 호출을 통해 전달되는 인자 정보 추출


#include <stdarg.h>

1. 가변인자를 가리킬 수 있는 참조자 선언     : va_list

2. 참조자가 가변인자를 실제 참조                : va_start

3. 참조자를 통해 전달된 정보 추출               : va_arg

4. 참조자가 가변인자를 가리키지 않도록 해제 : va_end


Output :

3+5=2=10



22행 : (vlist, int) 첫번째 인자는 참조자 이름, 두번째 인자는 참조대상의 type



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

enum, auto, 구조체  (0) 2017.03.24
C 복습-콘솔 입출력  (0) 2017.02.11
파일의 분할과 헤더파일의 디자인  (0) 2016.08.30
선행처리기와 매크로2  (0) 2016.08.30
선행처리기와 매크로  (0) 2016.08.29
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;
}

+ Recent posts