[11] 함수 입력 이용하기 (function input variables)

C언어 함수 정리

참고 문헌 (Ch 62): https://dojang.io/mod/page/view.php?id=541

매개변수 사용하기

입력을 받아서 이용하는 방법

1
2
3
반환값자료형 함수이름(자료형 매개변수)
{
}

예시

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

void helloNumber(int num1) // 반환값 없음, int형 매개변수 한 개 지정
{
printf("Hello, %d\n", num1); // Hello, 와 매개변수를 조합하여 문자열 출력
}

int main()
{
helloNumber(10); // Hello, 10: helloNumber에 10을 넣어서 호출
helloNumber(20); // Hello, 20: helloNumber에 20을 넣어서 호출

return 0;
}

덧셈함수 만들기

간단한 함수를 통해 여러 개의 매개변수를 다루는 방법에 대해서 확인

1
2
3
반환값자료형 함수이름(자료형 매개변수1, 자료형 매개변수2)
{
}

예시

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

int add(int a, int b) // 반환값 자료형은 int, int형 매개변수 두 개 지정
{
return a + b; // 매개변수 a와 b를 더한 결과를 반환
}

int main()
{
int num1;

num1 = add(10, 20); // 함수를 호출할 때 10과 20을 전달. 10과 20을 더한 값인 30이 반환됨

printf("%d\n", num1); // 30

return 0;
}

용어 정리

매개변수, 파라미터, 인자, 인수, 아규먼트와 같이 함수에 들어가는 값에는 다양한 명칭 존재\

  • 매개변수, 파라미터, 형식 매개변수, 인자: 함수 바깥에서 전달된 값이 저장되는 변수
    1
    2
    3
    4
    5
    6
    //          매개변수
    // ↓ ↓
    int add(int a, int b)
    {
    return a + b;
    }
  • 인수, 전달인자, 실행 전달인자: 함수를 호출할 때 전달하는 값이나 변수
    1
    2
    3
    4
    5
    6
    //          매개변수
    // ↓ ↓
    int add(int a, int b)
    {
    return a + b;
    }

[10] 반환값을 이용하는 함수 (function using return)

C언어 함수 정리

참고 문헌 (Ch 61): https://dojang.io/mod/page/view.php?id=527

함수에서 반환값 사용하기

함수에서 반환값을 사용하기 위해서는 함수를 정의할 때 반환값의 자료형을 지정해주고, 함수 안에서 return 키워드로 값을 반환하면 된다.

1
2
3
4
반환값자료형 함수이름()
{
return 반환값;
}

중요한 점: 반환값과 반환값의 자료형이 일치해야한다.

1
2
3
4
5
6
/*
↓ 반환값 자료형 */
int one() // 반환값이 int형인 one 함수 정의
{
return 1; // 1을 반환 => 1은 int 형
} // ↑ 반환값
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int one() // 반환값이 int형인 one 함수 정의
{
return 1; // 1을 반환
}

int main()
{
int num1;

num1 = one(); // int형을 반환했으므로 int형 변수에 저장

printf("%d\n", num1); // 1

return 0;
}

int 가 아닌 다른 자료형 반환 예시

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
#include <stdio.h>
#include <stdbool.h> // bool, true, false가 정의된 헤더 파일

float realNumber() // 반환값이 float형인 realNumber 함수 정의
{
return 1.234567f; // 1.234567: float형을 반환
}

bool truth() // 반환값이 bool형인 truth 함수 정의
{
return true; // true: bool형을 반환
}

int main()
{
float num1;
bool b1;

num1 = realNumber(); // float형을 반환했으므로 float형 변수에 저장
b1 = truth(); // bool형을 반환했으므로 bool형 변수에 저장

printf("%f\n", num1); // 1.234567
printf("%d\n", b1); // 1

return 0;
}

물론 return 값의 자료형을 강제로 변환해주면 이용할 수 있긴 하다.

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

int one() // 반환값이 int형인 one 함수 정의
{
float a = 1.1f;

return (int)a; // a를 int로 변환하여 반환
}

int main()
{
int num1;

num1 = one(); // int형을 반환했으므로 int형 변수에 저장

printf("%d\n", num1); // 1

return 0;
}

또한 함수 반환값의 자료형을 함수를 이용할 때 강제로 바꿔줄 수도 있다.

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

float onePointOne() // 반환값이 float형인 onePointOne 함수 정의
{
return 1.1f; // 실수 1.1을 반환
}

int main()
{
int num1;

num1 = (int)onePointOne(); // onePointOne의 반환값을 int로 변환하여 저장

printf("%d\n", num1); // 1

return 0;
}

포인터 반환하기

일반적인 자료형을 반환하는 것과 비슷하지만, *를 추가적으로 붙여준다.

1
2
3
4
반환값자료형 *함수이름()
{
return 반환값;
}

예시 (잘못된 코드)

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

int *ten() // int 포인터를 반환하는 ten 함수 정의
{
int num1 = 10; // num1은 함수 ten이 끝나면 사라짐

return &num1; // 함수에서 지역 변수의 주소를 반환하는 것은 잘못된 방법
} // ↑ warning C4172: 지역 변수 또는 임시 변수의 주소를 반환하고 있습니다.

int main()
{
int *numPtr;

numPtr = ten(); // 함수를 호출하고 반환값을 numPtr에 저장

printf("%d\n", *numPtr); // 10: 값이 나오긴 하지만 이미 사라진 변수를 출력하고 있음

return 0;
}

지역변수의 주소를 반환하는 경우, 함수에서 이용된 다음 값이 사라지기 때문에 좋은 방법이 아님. 메모리를 할당한 다음, 해당 주소를 반환해야 한다.
예시 (제대로 된 코드)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

int *ten() // int 포인터를 반환하는 ten 함수 정의
{
int *numPtr = malloc(sizeof(int)); // int 크기만큼 동적 메모리 할당

*numPtr = 10; // 역참조로 10 저장

return numPtr; // 포인터 반환. malloc으로 메모리를 할당하면 함수가 끝나도 사라지지 않음
}

int main()
{
int* numPtr;

numPtr = ten(); // 함수를 호출하고 반환값을 numPtr에 저장

printf("%d\n", *numPtr); // 10: 메모리를 해제하기 전까지 안전함

free(numPtr); // 다른 함수에서 할당한 메모리라도 반드시 해제해야 함

return 0;
}

대신 이용한 다음에는 꼭 할당된 동적 메모리를 해제해주어야한다. 그렇지 않은 경우 메모리 누수가 발생한다.
또 다른 예시 (문자열 반환)

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
30
31
32
33
34
35
36
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
#include <string.h> // strcpy 함수가 선언된 헤더 파일

char *helloLiteral() // char 포인터를 반환하는 helloLiteral 함수 정의
{
char *s1 = "Hello, world!";

return s1; // 문자열 Hello, world!는 메모리에 저장되어 있으므로 사라지지 않음
// 문자열 포인터 리턴
}

char *helloDynamicMemory() // char 포인터를 반환하는 helloDynamicMemory 함수 정의
{
char *s1 = malloc(sizeof(char) * 20); // char 20개 크기만큼 동적 메모리 할당

strcpy(s1, "Hello, world!"); // Hello, world!를 s1에 복사

return s1; // 문자열 포인터 리턴
}

int main()
{
char *s1;
char *s2;

s1 = helloLiteral();
s2 = helloDynamicMemory();

printf("%s\n", s1); // Hello, world!
printf("%s\n", s2); // Hello, world!

free(s2); // 동적 메모리 해제

return 0;
}

void 포인터 반환하기

자료형에 상관없이 값을 꺼내오고 싶을 때 이용하는 방법

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
30
31
32
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
#include <string.h> // strcpy 함수가 선언된 헤더 파일

void *allocMemory() // void 포인터를 반환하는 allocMemory 함수 정의
{
void *ptr = malloc(100); // 100바이트만큼 동적 메모리 할당

return ptr; // void 포인터 반환
}
/* 코드 줄이는 방법
void *allocMemory()
{
return malloc(100); // malloc 함수를 호출하면서 바로 반환
}
*/

int main()
{
char *s1 = allocMemory(); // void 포인터를 char 포인터에 넣어서 문자열처럼 사용
strcpy(s1, "Hello, world!"); // s1에 Hello, world! 복사
printf("%s\n", s1); // Hello, world!
free(s1); // 동적 메모리 해제

int *numPtr1 = allocMemory(); // void 포인터를 int 포인터에 넣어서 정수 배열처럼 사용
numPtr1[0] = 10; // 첫 번째 요소에 10 저장
numPtr1[1] = 20; // 두 번째 요소에 20 저장
printf("%d %d\n", numPtr1[0], numPtr1[1]); // 10 20
free(numPtr1); // 동적 메모리 해제

return 0;
}

구조체와 구조체 포인터 반환하기

함수의 반환값으로 구조체를 이용하는 방법!

1
2
3
4
struct 구조체이름 함수이름()
{
return 구조체변수;
}

예시

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
30
31
32
33
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

struct Person {
char name[20];
int age;
char address[100];
};

struct Person getPerson() // Person 구조체를 반환하는 getPerson 함수 정의
{
struct Person p;

strcpy(p.name, "홍길동");
p.age = 30;
strcpy(p.address, "서울시 용산구 한남동");

return p; // 구조체 변수 반환
}

int main()
{
struct Person p1;

p1 = getPerson(); // 반환된 구조체 변수의 내용이 p1로 모두 복사됨

// getPerson에서 저장한 값이 출력됨
printf("이름: %s\n", p1.name); // 홍길동
printf("나이: %d\n", p1.age); // 30
printf("주소: %s\n", p1.address); // 서울시 용산구 한남동

return 0;
}

구조체 변수를 반환하여 다른 변수에 저장하면, 반횐된 구조체의 내용을 모두 복사하게 되는데, 이는 구조체 크기가 커지면 메모리를 많이 잡아먹게 된다.
따라서 구조체 복사가 일어나지 않도록 malloc 함수로 동적 메모리를 할당한 뒤 구조체 포인터를 반환하는 것이 좋음

1
2
3
4
struct 구조체이름 *함수이름()
{
return 구조체포인터;
}

예시

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
30
31
32
33
34
35
36
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

struct Person {
char name[20];
int age;
char address[100];
};

struct Person *allocPerson() // Person 구조체 포인터를 반환하는 allocPerson 함수 정의
{
struct Person *p = malloc(sizeof(struct Person)); // 구조체 포인터에 동적 메모리 할당;

strcpy(p->name, "홍길동");
p->age = 30;
strcpy(p->address, "서울시 용산구 한남동");

return p; // 구조체 포인터 반환
}

int main()
{
struct Person *p1;

p1 = allocPerson(); // 포인터를 반환하여 p1에 메모리 주소 저장

// allocPerson에서 저장한 값들이 출력됨
printf("이름: %s\n", p1->name); // 홍길동
printf("나이: %d\n", p1->age); // 30
printf("주소: %s\n", p1->address); // 서울시 용산구 한남동

free(p1); // 동적 메모리 해제

return 0;
}

만약 구조체 별칭을 사용하는 경우라면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct _Person {
char name[20];
int age;
char address[100];
} Person, *PPerson; // 구조체 별칭 Person, 구조체 포인터 별칭 PPerson

PPerson allocPerson() // Person 구조체 포인터의 별칭을 반환값 자료형으로 지정
{
PPerson p = malloc(sizeof(Person)); // 구조체 포인터에 동적 메모리 할당

strcpy(p->name, "홍길동");
p->age = 30;
strcpy(p->address, "서울시 용산구 한남동");

return p; // 구조체 포인터 반환
}

[09] 기본적인 함수 내용 (function basic)

C언어 함수 정리

참고 문헌 (Ch 60): https://dojang.io/mod/page/view.php?id=521

Hello world! 출력 함수 만들기

함수를 정의하는 방법

1
2
3
4
반환값자료형 함수이름()
{
코드;
}

예시

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

void hello(){

printf("Hello, world!\n");
}


int main(){

hello();

return 0;
}

함수 선언과 정의 분리하기

코드 가독성을 높이기 위해서 메인 함수를 맨 위로 올리기 위해서 필요한 작업. 함수의 원형을 메인 함수 위에 선언해주고, 함수의 내용은 다른 곳에 작성

반환값자료형 함수이름();

용어 설명:

  • 함수 원형(function prototype): 함수 선언 부분
  • 함수 헤더(function header)와 함수 본체(function body): 함수 정의 부분
    예시
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <stdio.h>

    void hello(); // 반환값이 없는 hello 함수 원형 선언,

    int main()
    {
    hello(); // hello 함수 호출

    return 0;
    }

    void hello() // 반환값이 없는 hello 함수 정의
    {
    printf("Hello, world!\n"); // Hello, world! 출력
    }

2020.01.10

오늘의 개발

오늘은 논문 포멧팅을 위해서 Latex 문법을 공부한 날이다. 문법 공부하면서 포멧팅도 해보고 굉장히 뿌듯했다.

내비 처리 관련해서 코딩을 마저 해야하는데 내키지 않아서 계속 미루게 된다…

메모

Latex가 생각보다 코딩하기가 빡셌다. 아직 많이 다뤄보지 않아서 그런 것도 있는데, 어떤 기능을 어떻게 구현해야하는지가 아직 와닿지 않아서 그런 것 같다. 그래도 HTML이나 Markdown류의 문서 및 코딩 스타일을 작성해본 경험 때문인지 금방 배운 것 같다.

[08] 열거형 (enum)]

열거형 사용하기

참고 문헌 (Ch 57): https://dojang.io/mod/page/view.php?id=480

열거형 정의하기

열거형을 사용하면 상수를 좀 더 편하게 정의 가능!

enum 을 이용하여 정의

1
2
3
4
5
enum 열거형이름 {
1 = 초깃값,
2,
3
};

열거형 변수의 선언: enum (열거형 이름) (변수 이름);

예시 (요일)

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

enum DayOfWeek { // 열거형 정의
Sunday = 0, // 초깃값 할당
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};

int main()
{
enum DayOfWeek week; // 열거형 변수 선언

week = Tuesday; // 열거형 값 할당

printf("%d\n", week); // 2: Tuesday의 값 출력

return 0;
}

물론 모든 값을 하나하나 정의할 수도 있음

1
2
3
4
5
6
enum Value {
ValueA = 1,
ValueB = 3,
ValueC = 7,
ValueD = 11
};

역시 typedef 를 적용해서 익명 열거형으로도 이용할 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef enum 열거형이름 {
1 = 초깃값,
2,
3
} 열거형별칭;

typedef enum {
1 = 초깃값,
2,
3
} 열거형별칭;

// e.g.
typedef enum { // 익명 열거형 정의
Sunday = 0,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
} DayOfWeek; // typedef를 사용하여 열거형 별칭을 DayOfWeek로 정의

DayOfWeek week; // 열거형 별칭으로 공용체 변수 선언

구조체와 마찬가지로 열거형을 정의하는 동시에 변수 선언도 가능

1
2
3
enum 열거형이름 {
자료형 멤버이름;
} 변수;

열거형을 switch에 활용하기

활용하는 방법

1
2
3
4
5
6
switch (열거형변수)
{
case 열거형값:
실행할코드;
break;
}

예시

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
30
31
32
33
34
35
36
#include <stdio.h>

enum LuxSkill {
LightBinding = 1,
PrismaticBarrier,
LucentSingularity,
FinalSpark
};

int main()
{
enum LuxSkill skill; // 열거형 변수 선언

skill = LightBinding; // 열거형 값 할당

switch (skill)
{
case LightBinding: // 열거형 값이 LightBinding일 때
printf("LightBinding\n");
break;
case PrismaticBarrier: // 열거형 값이 PrismaticBarrier일 때
printf("PrismaticBarrier\n");
break;
case LucentSingularity: // 열거형 값이 LucentSingularity일 때
printf("LucentSingularity\n");
break;
case FinalSpark: // 열거형 값이 FinalSpark일 때
printf("FinalSpark\n");
break;
default:
break;
}

return 0;
}
// 결과 : LightBinding

[07] 구조체 배열 (struct array)

구조체 배열 사용하기

참고 문헌 (Ch 53): https://dojang.io/mod/page/view.php?id=445

구조체 배열 선언하기

선언하는 방법: struct (구조체 이름) (변수 이름)[배열 크기];

선언한 구조체의 멤버에 접근하는 방법: (변수 이름)[인덱스].(멤버 이름)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

struct Point2D {
int x;
int y;
};

int main()
{
struct Point2D p[3]; // 크기가 3인 구조체 배열 생성

p[0].x = 10; // 인덱스로 요소에 접근한 뒤 점으로 멤버에 접근
p[0].y = 20;
p[1].x = 30;
p[1].y = 40;
p[2].x = 50;
p[2].y = 60;

printf("%d %d\n", p[0].x, p[0].y); // 10 20
printf("%d %d\n", p[1].x, p[1].y); // 30 40
printf("%d %d\n", p[2].x, p[2].y); // 50 60

return 0;
}

선언하는 동시에 초기화 하는 방법: 중괄호 안에 중괄호를 사용하여 멤버별 초기 값을 설정해줘야함.

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
#include <stdio.h>

struct Point2D {
int x;
int y;
};

int main()
{
// 구조체 배열을 선언하면서 초기화
struct Point2D p1[3] = { { .x = 10, .y = 20 }, { .x = 30, .y = 40 }, { .x = 50, .y = 60 } };
// struct Point2D p[3] = {0,}: 그냥 전체 다 0으로 초기화하는 방법

printf("%d %d\n", p1[0].x, p1[0].y); // 10 20
printf("%d %d\n", p1[1].x, p1[1].y); // 30 40
printf("%d %d\n", p1[2].x, p1[2].y); // 50 60

// 구조체 배열을 선언하면서 초기화
struct Point2D p2[3] = { { 10, 20 }, { 30, 40 }, { 50, 60 } };

printf("%d %d\n", p2[0].x, p2[0].y); // 10 20
printf("%d %d\n", p2[1].x, p2[1].y); // 30 40
printf("%d %d\n", p2[2].x, p2[2].y); // 50 60

return 0;
}

구조체 포인터 배열 선언하기

선언하는 방법: struct (구조체 이름) (*포인터이름)[크기];

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
30
31
32
33
34
35
36
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

struct Point2D {
int x;
int y;
};

int main()
{
struct Point2D *p[3]; // 크기가 3인 구조체 포인터 배열 선언

// 구조체 포인터 배열 전체 크기에서 요소(구조체 포인터)의 크기로 나눠서 요소 개수를 구함
for (int i = 0; i < sizeof(p) / sizeof(struct Point2D *); i++) // 요소 개수만큼 반복
{
p[i] = malloc(sizeof(struct Point2D)); // 각 요소에 구조체 크기만큼 메모리 할당
}

p[0]->x = 10; // 인덱스로 요소에 접근한 뒤 화살표 연산자로 멤버에 접근
p[0]->y = 20;
p[1]->x = 30;
p[1]->y = 40;
p[2]->x = 50;
p[2]->y = 60;

printf("%d %d\n", p[0]->x, p[0]->y); // 10 20
printf("%d %d\n", p[1]->x, p[1]->y); // 30 40
printf("%d %d\n", p[2]->x, p[2]->y); // 50 60

for (int i = 0; i < sizeof(p) / sizeof(struct Point2D *); i++) // 요소 개수만큼 반복
{
free(p[i]); // 각 요소의 동적 메모리 해제
}

return 0;
}

[06] 구조체 멤버값 설정 (memset)

구조체와 메모리 활용하기

참고 문헌 (Ch 52): https://dojang.io/mod/page/view.php?id=438

구조체와 메모리를 간단하게 0으로 설정하기

구조체 변수나 메모리의 내용을 한꺼번에 값을 설정하려면 memset 함수를 사용

memset(구조체 포인터, 설정할 값, sizeof(struct 구조체이름))

예시 - 구조체 변수를 이용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <string.h> // memset 함수가 선언된 헤더 파일

struct Point2D {
int x;
int y;
};

int main()
{
struct Point2D p1;

memset(&p1, 0, sizeof(struct Point2D)); // p1을 구조체 크기만큼 0으로 설정

printf("%d %d\n", p1.x, p1.y); // 0 0: memset을 사용하여 0으로 설정했으므로
// x, y 모두 0

return 0;
}

예시 - malloc으로 만들어진 배열을 이용

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
30
31
32
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
#include <string.h> // memset 함수가 선언된 헤더 파일

struct Point2D {
int x;
int y;
};

int main()
{
struct Point2D *p1 = malloc(sizeof(struct Point2D)); // 구조체 크기만큼 메모리 할당

memset(p1, 0, sizeof(struct Point2D)); // p1을 구조체 크기만큼 0으로 설정

printf("%d %d\n", p1->x, p1->y); // 0 0: memset을 사용하여 0으로 설정했으므로
// x, y 모두 0
// 출력: 0 0

// 0 이 아닌 값을 넣는 경우 => 값이 이상한 것 같음
memset(p1, 1, sizeof(struct Point2D));
printf("%d %d\n", p1->x, p1->y);
// 출력: 16843009 16843009

memset(p1, 2, sizeof(struct Point2D));
printf("%d %d\n", p1->x, p1->y);
// 출력: 33686018 33686018

free(p1); // 동적 메모리 해제

return 0;
}

구조체와 메모리 복사하기

아주 중요한 기능!!

이미 생성하여 값을 저장한 구조체나 메모리를 다른 곳에 복사할 경우 이용

memcpy( 목적지포인터 , 원본포인터 , 크기); => 목적지 포인터를 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h> // memcpy 함수가 선언된 헤더 파일

struct Point2D {
int x;
int y;
};

int main()
{
struct Point2D p1;
struct Point2D p2;

p1.x = 10; // p1의 멤버에만 값 저장
p1.y = 20; // p1의 멤버에만 값 저장

memcpy(&p2, &p1, sizeof(struct Point2D)); // Point2D 구조체 크기만큼 p1의 내용을 p2로 복사

printf("%d %d\n", p2.x, p2.y); // 10 20: p1의 내용을 p2로 복사했으므로 10 20

return 0;
}
// 사이즈가 같은 경우 위와 같이 되는데 다른 경우는 어떻게 될까?

복사하는 포인터와 목적지 포인터의 사이즈가 다를 경우

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
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <string.h> // memcpy 함수가 선언된 헤더 파일

struct Point2D {
int x;
int y;
};

struct Point1D {
int x;
};

struct Point3D {
int x;
int y;
int z;
};


int main()
{
struct Point1D p1;
struct Point2D p2;
struct Point3D p3;

p2.x = 10; // p1의 멤버에만 값 저장
p2.y = 20; // p1의 멤버에만 값 저장

memcpy(&p1, &p2, sizeof(struct Point1D)); // Point2D 구조체 크기만큼 p2의 내용을 p1로 복사
printf("%d \n", p1.x); // 10: p2의 내용을 p1로 복사, 한 칸만 복사했으므로 10

memcpy(&p3, &p2, sizeof(struct Point3D)); // Point2D 구조체 크기만큼 p2의 내용을 p3로 복사
printf("%d %d %d\n", p3.x, p3.y, p3.z); // 10 20 10: p2의 내용을 p3로 복사 3칸을 복사했는데, 부족한지 맨 첫 값이 다시 나옴

// 그 외에도 메모리 사이즈가 작은 데이터를 큰 데이터로 옮기는 경우, 해당하는 범위를 넘어서면 원래 값이 남아있다.
return 0;
}

[05] 구조체와 포인터 (struct and pointer)

구조체 포인터 사용하기

참고 문헌 (Ch 49) : https://dojang.io/mod/page/view.php?id=418

구조체 포인터를 선언하고 메모리 할당하기

다른 자료형과 마찬가지로 malloc 함수를 이용하여 동적 메모리 할당

할당하는 방법: struct 구조체이름 (*포인터이름) = malloc(sizeof(struct 구조체 이름));

예시

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
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

struct Person { // 구조체 정의
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
};

int main()
{
struct Person *p1 = malloc(sizeof(struct Person)); // 구조체 포인터 선언, 메모리 할당

// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
strcpy(p1->name, "홍길동");
p1->age = 30;
strcpy(p1->address, "서울시 용산구 한남동");

// 화살표 연산자로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1->name); // 홍길동
printf("나이: %d\n", p1->age); // 30
printf("주소: %s\n", p1->address); // 서울시 용산구 한남동

free(p1); // 동적 메모리 해제

return 0;
}

참고: 구조체의 멤버가 포인터일 때 역참조를 하려면 맨 앞에 *를 붙여야 함. 이 때 구조체 변수 앞에 *가 붙어있더라도 멤버의 역참조이지 구조체 변수의 역참조가 아님.

예시

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
30
31
32
#include <stdio.h>
#include <stdlib.h>

struct Data {
char c1;
int *numPtr; // 포인터
};

int main()
{
int num1 = 10;
struct Data d1; // 구조체 변수
struct Data *d2 = malloc(sizeof(struct Data)); // 구조체 포인터에 메모리 할당

d1.numPtr = &num1; // d1의 numPtr에 &num1을 할당
d2->numPtr = &num1; // d2의 numPtr에 &num1을 할당

printf("%d\n", *d1.numPtr); // 10: 구조체의 멤버를 역참조
printf("%d\n", *d2->numPtr); // 10: 구조체 포인터의 멤버를 역참조

d2->c1 = 'a';
printf("%c\n", (*d2).c1); // a: 구조체 포인터를 역참조하여 c1에 접근
// d2->c1과 같음
printf("%d\n", *(*d2).numPtr); // 10: 구조체 포인터를 역참조하여 numPtr에 접근한 뒤 다시 역참조
// *d2->numPtr과 같음
printf("%d\n", d2->numPtr); // 비교를 위한 코드
printf("%d\n", *d2->numPtr); // 비교를 위한 코드

free(d2);

return 0;
}

구조체 별칭으로 포인터를 선언하고 메모리 할당하기

구조체 별칭을 이용하여 구조체 포인터의 메모리 할당 방법: (구조체 별칭) (*포인터 이름) = malloc(sizeof(구조체 별칭))

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
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

typedef struct _Person { // 구조체 이름은 _Person
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
} Person; // typedef를 사용하여 구조체 별칭을 Person으로 정의

int main()
{
Person *p1 = malloc(sizeof(Person)); // 구조체 별칭으로 포인터 선언, 메모리 할당

// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
strcpy(p1->name, "홍길동");
p1->age = 30;
strcpy(p1->address, "서울시 용산구 한남동");

// 화살표 연산자로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1->name); // 홍길동
printf("나이: %d\n", p1->age); // 30
printf("주소: %s\n", p1->address); // 서울시 용산구 한남동

free(p1); // 동적 메모리 해제

return 0;
}

구조체 포인터에 구조체 변수의 주소 할당하기

동적 메모리를 할당하지 않고 구조체 포인터를 사용하는 방법: (구조체 포인터) = (&구조체 변수)

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

struct Person { // 구조체 정의
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
};

int main()
{
struct Person p1; // 구조체 변수 선언
struct Person *ptr; // 구조체 포인터 선언

ptr = &p1; // p1의 메모리 주소를 구하여 ptr에 할당

// 화살표 연산자로 구조체 멤버에 접근하여 값 할당
ptr->age = 30;

printf("나이: %d\n", p1.age); // 나이: 30: 구조체 변수의 멤버 값 출력
printf("나이: %d\n", ptr->age); // 나이: 30: 구조체 포인터의 멤버 값 출력

return 0;
}

[04] 구조체 (struct)

구조체 사용하기

참고 문헌 (ch48) : https://dojang.io/mod/page/view.php?id=407

구조체를 만들고 사용하기

구조체 정의 : struct 이용

1
2
3
struct 구조체이름{
자료형 멤버이름;
};

구조체 선언 : struct (구조체이름) (변수이름);

예시

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
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

struct Person { // 구조체 정의
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
};

int main()
{
struct Person p1; // 구조체 변수 선언

// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "서울시 용산구 한남동");

// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name); // 이름: 홍길동
printf("나이: %d\n", p1.age); // 나이: 30
printf("주소: %s\n", p1.address); // 주소: 서울시 용산구 한남동

return 0;
}

구조체의 정의와 선언을 동시에 하는 방법

1
2
3
struct 구조체이름 {
자료형 멤버이름;
} 변수;

예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

struct Person { // 구조체 정의
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
} p1; // 구조체를 정의하는 동시에 변수 p1 선언

int main()
{
// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "서울시 용산구 한남동");

// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name); // 이름: 홍길동
printf("나이: %d\n", p1.age); // 나이: 30
printf("주소: %s\n", p1.address); // 주소: 서울시 용산구 한남동

return 0;
}

typedef로 struct 키워드 없이 구조체 선언하기

struct 키워드를 생략하고 선언하는 방법: typedef로 정의하면서 별칭(alias)을 정해주기

1
2
3
typedef struct 구조체이름 {
자료형 멤버이름;
} 구조체별칭;

typedef로 정의한 변수 선언하는 방법: (구조체 별칭) (변수 이름);

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
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

typedef struct _Person { // 구조체 이름은 _Person
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
} Person; // typedef를 사용하여 구조체 별칭을 Person으로 정의

int main()
{
Person p1; // 구조체 별칭 Person으로 변수 선언

// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "서울시 용산구 한남동");

// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name); // 이름: 홍길동
printf("나이: %d\n", p1.age); // 나이: 30
printf("주소: %s\n", p1.address); // 주소: 서울시 용산구 한남동

return 0;
}

또한 모든 자료형의 별칭을 설정할 수도 있음

1
2
3
4
5
6
7
8
typedef int MYINT;      // int를 별칭 MYINT로 정의
typedef int* PMYINT; // int 포인터를 별칭 PMYINT로 정의

MYINT num1; // MYINT로 변수 선언
PMYINT numPtr1; // PMYINT로 포인터 변수 선언

numPtr1 = &num1; // 포인터에 변수의 주소 저장
// 사용 방법은 일반 변수, 포인터와 같음

익명 구조체 사용하기

익명 구조체 정의하기: 구조체를 정의할 때 구조체의 이름 없이 별칭만을 이용해서 정의하는 구조체, 이 경우 별칭을 무조건 정해줘야한다.

1
2
3
typedef struct {
자료형 멤버이름;
} 구조체별칭;

익명 구조체 선언하기: (구조체 별칭) (변수 이름);

예시

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
#include <stdio.h>
#include <string.h> // strcpy 함수가 선언된 헤더 파일

typedef struct { // 구조체 이름이 없는 익명 구조체
char name[20]; // 구조체 멤버 1
int age; // 구조체 멤버 2
char address[100]; // 구조체 멤버 3
} Person; // typedef를 사용하여 구조체 별칭을 Person으로 정의

int main()
{
Person p1; // 구조체 별칭 Person으로 변수 선언

// 점으로 구조체 멤버에 접근하여 값 할당
strcpy(p1.name, "홍길동");
p1.age = 30;
strcpy(p1.address, "서울시 용산구 한남동");

// 점으로 구조체 멤버에 접근하여 값 출력
printf("이름: %s\n", p1.name); // 이름: 홍길동
printf("나이: %d\n", p1.age); // 나이: 30
printf("주소: %s\n", p1.address); // 주소: 서울시 용산구 한남동

return 0;
}

[03] 배열과 포인터 (array and pointer)

포인터와 배열 응용하기

참고 문헌 (Ch38) : https://dojang.io/mod/page/view.php?id=316

생각의 시작

크기가 고정된 배열을 사용 => 크기가 변할 수 있는 배열을 어떻게 선언할 수 있는지에 대한 고민에서 출발하는 일!

C언어에서 배열을 선언: 고정 크기의 배열 선언

1
2
int numArr[10]
// 10 : 고정크기 => 10개의 요소를 가지는 배열

직관적으로 가변 길이 배열 선언해보기

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

int main()
{
int size;

scanf("%d", &size); // 배열의 크기를 입력받음

int numArr[size]; // GCC에서는 사용 가능, Visual Studio 2015에서는 컴파일 에러

return 0;
}

컴파일러마다 다르지만, VS2015의 경우 컴파일 에러가 발생한다고 한다
=> 동적으로 지정하기 위해서는 포인터를 선언하고, 메모리를 할당한 뒤 메모리를 배열처렁 사용해야한다!


포인터에 할당된 메모리를 배열처럼 사용하기

포인터를 배열처럼 사용하는 방법: 포인터에 malloc 함수로 메모리를 할당하여 사용하기!
malloc: dynamic memory allocation 방법. 메모리를 필요할 때 할당하여 사용, 다 쓴 메모리는 free를 통해 반드시 해제해줘야함!
malloc을 이용하기 위해서는 stdlib을 추가해줘야한다!

할당하는 방법: (자료형) (*포인터이름) = malloc(sizeof(자료형) * 크기);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

int main()
{
int *numPtr = malloc(sizeof(int) * 10); // int 10개 크기만큼 동적 메모리 할당

numPtr[0] = 10; // 배열처럼 인덱스로 접근하여 값 할당
numPtr[9] = 20; // 배열처럼 인덱스로 접근하여 값 할당

printf("%d\n", numPtr[0]); // 배열처럼 인덱스로 접근하여 값 출력
printf("%d\n", numPtr[9]); // 배열처럼 인덱스로 접근하여 값 출력

free(numPtr); // 동적으로 할당한 메모리 해제 => 해제하지 않으면 계속 잡혀있음

return 0;
}

입력한 크기만큼 메모리를 할당하여 배열처럼 사용하기

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
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

int main()
{
int size;

scanf("%d", &size);

int *numPtr = malloc(sizeof(int) * size); // (int 크기 * 입력받은 크기)만큼 동적 메모리 할당

for (int i = 0; i < size; i++) // 입력받은 크기만큼 반복
{
numPtr[i] = i; // 인덱스로 접근하여 값 할당
}

for (int i = 0; i < size; i++) // 입력받은 크기만큼 반복
{
printf("%d\n", numPtr[i]); // 인덱스로 접근하여 값 출력
}

free(numPtr); // 동적으로 할당한 메모리 해제

return 0;
}

포인터에 할당된 메모리를 2차원 배열처럼 사용하기

할당하는 방법: (자료형) (*포인터이름) = malloc(sizeof(자료형\) x 세로크기 를 이용하여 행 공간 메모리 할당 -> 행의 크기만큼 반복하면서 ((포인터)[i]) = malloc(sizeof(자료형) x 가로크기) 를 이용하여 열 공간 메모리 할당

해제하는 방법: 행 크기만큼 반복하면서 free(포인터[i]); 와 같이 열 공간 메모리 해제 -> free((포인터)); 로 행 공간 메모리 해제

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
30
31
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

int main()
{
int **m = malloc(sizeof(int *) * 3); // 이중 포인터에 (int 포인터 크기 * 행 크기)만큼
// 동적 메모리 할당. 배열의 행

for (int i = 0; i < 3; i++) // 행 크기만큼 반복
{
m[i] = malloc(sizeof(int) * 4); // (int 크기 * 열 크기)만큼 동적 메모리 할당.
// 배열의 열
}

m[0][0] = 1; // 행 인덱스 0, 열 인덱스 0인 요소에 값 할당
m[2][0] = 5; // 행 인덱스 2, 열 인덱스 0인 요소에 값 할당
m[2][3] = 2; // 행 인덱스 2, 열 인덱스 3인 요소에 값 할당

printf("%d\n", m[0][0]); // 1: 행 인덱스 0, 열 인덱스 0인 요소의 값 출력
printf("%d\n", m[2][0]); // 5: 행 인덱스 2, 열 인덱스 0인 요소의 값 출력
printf("%d\n", m[2][3]); // 2: 행 인덱스 2, 열 인덱스 3인 요소의 값 출력

for (int i = 0; i < 3; i++) // 행 크기만큼 반복
{
free(m[i]); // 2차원 배열의 열 공간 메모리 해제
}

free(m); // 2차원 배열의 행 공간 메모리 해제

return 0;
}

입력한 크기만큼 메모리를 할당하여 포인터를 2차원 배열처럼 사용하기

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일

int main()
{
int row, col;

scanf("%d %d", &row, &col);

// 2차원 배열 선언 시작
int **m = malloc(sizeof(int *) * row); // 이중 포인터에 (int 포인터 크기 * row)만큼
// 동적 메모리 할당. 배열의 행

for (int i = 0; i < row; i++) // 세로 크기만큼 반복
{
m[i] = malloc(sizeof(int) * col); // (int의 크기 * col)만큼 동적 메모리 할당. 배열의 열
}
// 2차원 배열 선언 끝

// 2차원 배열에 값 할당 시작
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
for (int j = 0; j < col; j++) // 열 크기만큼 반복
{
m[i][j] = i + j; // 2차원 배열의 각 요소에 i + j 값을 할당
}
}
// 2차원 배열에 값 할당 끝

for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
for (int j = 0; j < col; j++) // 열 크기만큼 반복
{
printf("%d ", m[i][j]); // 2차원 배열의 인덱스에 반복문의 변수 i, j를 지정
}
printf("\n"); // 가로 요소를 출력한 뒤 다음 줄로 넘어감
}

// 2차원 배열 메모리 해제 시작
for (int i = 0; i < row; i++) // 행 크기만큼 반복
{
free(m[i]); // 2차원 배열의 열 공간 메모리 해제
}

free(m); // 2차원 배열의 행 공간 메모리 해제
// 2차원 배열 메모리 해제 끝

return 0;
}