반응형

1. 구조체


우리는 지금까지 int, char, long long, double, array같은 변수, 배열만 배웠다.

어떤 5명의 학생 정보인 이름, 학년, 반, 번호를 담기위해 우리는 어떻게 할 수 있을까?

char name[5][10];

int grade[5];

int class[5];

int number[5];

라고 선언하면 된다.


하지만 구조체라는 개념을 쓰면 다음과 같이 변하게 된다.


struct STUDENT{

char name[10];

int grade;

int class;

int number;

};


STUDENT student[10];


훨씬 가독성 있고 코드가 짜임새 있어짐을 알 수 있다.


이전에는 int student[10]처럼 앞에 type이 int, char 이런것들이었지만 지금은 STUDENT라는 새로운 구조체 type을 형성해주었음도 알 수 있다.


그림으로 보면 다음과 같이 구성됨을 알 수 있다.




구조체의 내부 값을 호출하기 위해서는


student[2].grade 처럼 호출하면 2번학생의 학년을 알 수 있게 된다.


** 주의 **


구조체를 만들 때는 항상 

struct 구조체이름{

들어갈 내용

}; << 세미콜론을 잊으면 안된다.




문제 1.


3명 학생의 이름, 학년, 반, 번호를 입력받고 3명의 학생을 출력해보자


입력은 다음과 같이한다.

(이름, 학년, 반, 번호 순)


고관우 3 2 5

홍길동 1 3 15

김부자 2 3 3


출력은 다음과 같아야한다.

(이름, 학년, 반, 번호 순)

고관우 3 2 5

홍길동 1 3 15

김부자 2 3 3


알아두기

이 문제를 풀며 '반'을 class로 변수 선언을 할 때 무슨 오류가나는지 파악해보자.

그리고 왜 이런오류가 나는지 생각해보자.

그리고 int char[10]; 을 선언해도 왜 안되는지 생각해보자.





2. 함수


int main(){

코드

}

이 속에 모든 코드를 담아도 되지만, 코드를 좀 더 가지런하게 짤 필요가 있거나, 재사용 빈도가 높은 코드는 함수화를 시킬 수 있다.


예를들어보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
int main()
{
    int a[20];
 
    for (int i = 0; i < 20; i++)
        a[i] = i*i;
 
    printf("%d\n", a[0+ a[2]);
    printf("%d\n", a[3- a[1]);
    printf("%d\n", a[10+ a[15]);
    printf("%d\n", a[12+ a[14]);
    printf("%d\n", a[13- a[13]);
    printf("%d\n", a[14+ a[12]);
 
    return 0;
}
        //                                                       This source code Copyright belongs to Crocus
        //                                                        If you want to see more? click here >>
Crocus


위의 코드는 덧셈 혹은 뺄셈을 계속 하고 있다.


덧셈 뺄셈을 위해 우리는 계속해서 printf 와 +, -를 적고 있다.


이 부분을 간결화 시키기 위해 함수를 한번 써보자.


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>
 
void func(int a, int b)
{
    printf("%d\n", a + b);
}
int main()
{
    int a[20];
 
    for (int i = 0; i < 20; i++)
        a[i] = i*i;
 
    func(a[0], a[2]);
    func(a[3], -a[1]);
    func(a[10], a[15]);
    func(a[12], a[14]);
    func(a[13], -a[13]);
    func(a[14], a[12]);
 
    return 0;
}
        //                                                       This source code Copyright belongs to Crocus
        //                                                        If you want to see more? click here >>
Crocus


func라는 함수를 호출만 하면 우리가 원하는 값을 얻을 수 있다.


참고로 지금까지 해본 printf, scanf도 함수의 일종이다.


이제 함수를 어떻게 쓴지 알아보자.


func(a[0], a[2]);

a[0]와 a[2]의 값을 함수로 보내려 한다.


이러한 함수로 보내주는 a[0]와 a[2]는 인자(Argument)라고 한다.


void func(int a, int b)를 보자.


이제 a[0], a[2]를 보냈으니 func에서 받아야한다.


그렇게 a[0]는 a라는 변수에 받는다는 의미이고 a[2]는 b라는 변수에 받겠다는 의미이다.


이때 int a, int b처럼 받아오는 것들은 매개변수(Parameter)라고 한다.


이때 왜 a, b가 int냐면 우리가 인자로 보낸 a[0], a[2]가 int형이니 받을때도 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
#include <stdio.h>
 
int func(int a, int b)
{
    return a + b;
}
int main()
{
    int a[20];
 
    for (int i = 0; i < 20; i++)
        a[i] = i*i;
 
    printf("%d\n", func(a[0], a[2]));
    printf("%d\n", func(a[3], -a[1]));
    printf("%d\n", func(a[10], a[15]));
    printf("%d\n", func(a[12], a[14]));
    printf("%d\n", func(a[13], a[13]));
    printf("%d\n", func(a[14], a[12]));
 
    return 0;
}
        //                                                       This source code Copyright belongs to Crocus
        //                                                        If you want to see more? click here >>
Crocus


위의 코드는 앞서 본 코드와 다른점이 return이라는 것이다.


return은 내가 함수에서 계산한 어떤 값을 돌려주는 역할을 한다.


그림으로 파악해보자.



우선 func를 호출하게 되면 1번에 의해 func함수를 보게되고 2,3번에 의해 매개변수를 받게 된다.


그리고 a + b를 계산하고 return을 해주는데 그 return을 아래 printf에 있는 func가 다시 받는 형식이 된다.


따라서 printf("%d\n", func(a[0], a[2]));는 return 받은 값을 출력하게 된다.


이때 int func(int a, int b)에서 int func의 int는 return할 형식에 맞게 작성해야 한다.


이때 func함수에서는 int a, int b라는 값을 받게 되는 것을 call by value라고 한다.


즉, return 정수 int 혹은 long long같은 형식의 함수를 작성해야하고


return 소수라면 double func 혹은 float func처럼 작성해야한다.


만약 배열을 return하고 싶다면?


조금있다 배울 포인터에 가서 생각해보도록 하자.

 



이제 문제를 몇가지 풀어보자.



문제 1.

사칙연산(+, -, /, *)를 하는 함수를 작성해보자



문제 2.


아래 그림은 Bubble Sort(버블 정렬)이 이루어지는 과정을 나타낸 것이다.


버블 정렬을 한번 작성해보자.






3. 포인터


이제껏 우리가 변수, 배열을 써온 과정은 다음과 같다.




어떤 변수 a를 선언하면 메모리 주소공간에 10이라는 값이 생기게 되고 그걸 a가 가리키고 있는 방식이다.


배열 역시 arr을 선언하면 메모리 주소공간에 연속적으로 공간이 생겨 그 속에 값을 넣을 수 있도록 만들었고 배열은 arr[0], arr[1]처럼 이용할 수 있게 되는 것이다.


이전에는 함수에 인자로 값을 보내고 받은 매개변수로 계산하여 return해주는 방식이었는데


인자로 값을 보낼 때 실제로 보낸 그 변수의 값을 변경시키는 방법은 없을까?


예를들어보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
?? swap(?? ?, ?? ?)
{
    ??
}
 
int main()
{
    int a = 2, b = 3;
    swap(a, b);
 
    // a에는 3이 b에는 2가 저장되도록 하고싶다.
    printf("%d %d", a, b);
}
 
        //                                                       This source code Copyright belongs to Crocus
        //                                                        If you want to see more? click here >>
Crocus


이럴때 a,b에 2,3이 들어있는데 a에 3이 b에 2가 들어있게 하려면 어떻게 해야할까?


우리는 생각해보면 함수를 이용할 때 간접적으로 값을 참조해서 이용하였다.


즉, a와 b를 func(a,b)에 보내게 되면 func함수에서는 a가 가지던 값, b가 가지던 값을 이용했을 뿐이지 a와 b자체를 가져와서 처리한 적은 없었다는 것이다.


이를 위해 포인터라는 것이 생겨났다.



포인터는 주소를 이용하는데 여기서 부터는 집중을 해야한다.

(보통 c를 배우면 포인터에서 다들 지치게 된다. 필자도 그런 시절이 있었기에 최대한 쉽게 알려주려 한다.)



바로 코드와 그림을 보고 포인터를 파악해보자.


현재 a = 2, b = 3으로 메모리 주소공간 1000번에는 a변수가 선언해둔 2라는 값이 있고 1004번에는 b변수가 선언해둔 3이라는 값이 있다.


그 값이 printf에 의해 1,2번 a,b에서 파악이 가능하다.


이제 3,4번을 보자.


swap가 우리가 알던 방식으로 인자를 보내고 있지 않다.


&라는 기호가 생겼는데 이것은 a변수의 주소를 가르키고 있다.


따라서 swap(&a, &b)는 swap(a주소, b주소)를 보내고 있게 된다.



함수 void swap(int *c, int *d)에서 의미를 좀더 명료하게 보자.


int *c = &a, int *d = &b라는 의미를 가지게 된다.



따라서 int *c는 a의 값인 2가 아닌 a의 주소인 1000을 보고있고 

int *d는 b의 값인 3이 아닌 b의 주소인 1004를 보고있게된다.


정말인지 알아보기 위해 
void swap(int *c, int *d)
printf("%d %d",c, d);
...

}
를 한번 작성해보자.


그럼 이제 우리는 주소를 이용하여 값을 조작하기에 매개변수로 받은 값을 조작 할 수 있게 된다.

이러한 것을 call by reference라고 한다.

다음을 실행해보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
using namespace std;
 
int main()
{
    int n;
    scanf("%d"&n);
 
    printf("n 값 :: %d n 주소 :: %d n 주소 :: %p\n", n, &n, &n);
 
    int *= &n;
    printf("p가 가리키는 값 :: %d p가 가리키는 주소 :: %d p의 주소 :: %p\n"*p, p, &p);
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus



이때 알아두어야 할 점은 포인터 크기는 운영체제에 따라 4byte(32비트) 혹은 8byte(64비트)로 나뉜다.


char *a이건 double *a이건간에 포인터는 주소를 담는 변수이기에 항상 4 또는 8바이트를 가진다.



문제 1.


~~에 해당하는 코드를 작성하여 출력값이 10이 되도록 해보시오.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
using namespace std;
 
~~ func(~~)
{
    ~~
}
int main()
{
    int n = 1;
    func(~~);
    printf("%d", n);
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus





문제 2.


~~에 해당하는 코드를 작성하여 출력값이 abzd가 되도록 해보시오.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
using namespace std;
 
~~ func(~~)
{
    ~~
}
int main()
{
    char str[5= "abcd";
    func(str);
    printf("%s", str);
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus






문제 1.


포인터에는 *p++. *++p, ++*p, (*p)++ 같은 것들이 있다. 모두 무슨 의미일까?



문제 2.


문자열 함수에는 strlen, strcpy, strcmp같은 것들이 string.h 헤더파일에 담겨있다.


한번씩 사용해보자.


예시

int len = strlen(str);

strcpy(복사시킬 배열, 복사할 값이있는 문자열) -> strcpy(tmp, str);

strcmp(비교 문자열1, 비교 문자열2) -> strcmp(str1, str2)


strcmp의 리턴 값은 다음과 같다.

-1 : str1이 사전상 앞선다. 

0 : str1과 str2는 같은 문자열 

1 : str1이 사전상 늦다.



직접 구현도 해보자.

정답은 다음 링크에 있다.

http://www.crocus.co.kr/1120



반응형