(C언어) scanf() 함수

C,C++ 2016.02.01 18:45

 함수 scanf()는 (보통은 키보드로) 입력을 받아들이는 기능을 수행하면 stdio.h 에 정의되어 있다. 만약 하나의 정수를 입력받아서 a라는 변수에 저장하고 싶다면 다음과 같이 해야 한다.


scanf(“%d”, &a);


이렇게 하면 사용자로보터 숫자 입력을 받아서 변수 a에 저장한다. ‘&’는 주소 연산자로서 포인터를 설명하는 장에서 자세히 알아볼 것이고 여기에서는 scanf()함수에서는 변수 앞에 &를 붙여야 한다고만 알아두고 넘어가자.


ex02-04.c

#include <stdio.h>

int main() {
int ia;
printf("input an integer :");
scanf("%d", &ia);
printf("your input is %d.",ia);
}

실행 결과

input an integer :1234
your input is 1234.


실수를 입력받을 때는 %f (float형) 나 %lf (double 형) 지시자를 사용하면 된다.


ex02-05.c

#include <stdio.h>

int main() {
float fa;
printf("input a real number :");
scanf("%f", &fa);
printf("your input is %f.", fa);
}

실행 결과

input a real number :1.23
your input is 1.230000.


두 개 이상의 입력을 한 번에 받으려면 다음과 같이 하면 된다.


ex02-06.c

#include <stdio.h>

int main() {
int ia, ib;
printf("input two integers :");
scanf("%d %d", &ia, &ib);
printf("your inputs are %d and %d.", ia, ib);
}

실행 결과

input two numbers :11 22
your inputs are 11 and 1.


여기서 scanf() 함수 내에서 "%d %d"와 같이 %d와 %d가 공백문자로 구분되어 있다면 사용자가 숫자르 입력할 때에도 공백문자로 구별해야 한다. 만약


scanf("%d,%d", &ia, &ib);


와 같이 콤마(,)로 구분되어 있으면 사용자도 11,22 와 같이 콤마로 구별해서 숫자를 입력해야 한다. 그렇지 않으면 오동작이 일어난다.

'C,C++' 카테고리의 다른 글

1.2 C 언어 소개  (0) 2016.03.04
1.1 프로그래밍 개요  (0) 2016.03.04
(C언어) scanf() 함수  (0) 2016.02.01
(C언어) 구조체의 포인터  (0) 2016.02.01
(C언어) 구조체 배열  (0) 2016.02.01
(C언어) 구조체 변수를 필드로 가지는 구조체  (0) 2016.02.01
Posted by 살레시오

구조체 변수도 포인터로 선언할 수 있으며 문법은 기본 자료형의 경우와 동일하다.


Point *pa;


이렇게 선언하면 pa는 Point 구조체 포인터 변수이다. 포인터 변수의 경우 필드는 다음과 같이 ‘->’ 연산자를 사용하여 접근할 수 있다.


pa->x = 0;
pa->y = 1;


즉, 구조체 포인터의 접근자는 점(.)이 아니라 ‘->’ 이다. 혹은 미리 정의된 Point 변수를 대입시킬 수도 있다.


Point a = {0,1};
Point *pa;
*pa = a;


 함수로 구조체 변수를 넘길 때 복사본이 넘어가는 것은 기본 자료형과 같다. 따라서 어떤 함수에서 넘겨받은 구조체 변수의 필드를 변경하는 것은 원래 변수에 아무런 영향을 미치지 않는다.


ex08-10.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

void showInfo(Point a) {
printf("(%f, %f)", a.x, a.y);
}

void changeToOrigin(Point a) {
a.x = 0;
a.y = 0;
}

int main(int argc, char **argv) {
Point a = {11,12};
printf("a:(%.2f, %.2f)\n", a.x, a.y);
changeToOrigin(a);
printf("a:(%.2f, %.2f)\n", a.x, a.y);
}

실행 결과

a:(11.00, 12.00)
a:(11.00, 12.00)


위 예에서 changeToOrigin()함수를 호출할 때 Point 변수 a를 넘기고 이것이 함수 내부에서 원점으로 변경되었지만 원래 변수에는 전혀 영향을 미치지 않느다. 왜냐면 구조체 변수의 복사본이 함수로 넘어가기 때문이다.


 하지만 함수로 포인터를 넘기면 원래 구조체 필드를 직접 접근할 수 있다.


ex08-10b.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

void showInfo(Point a) {
printf("(%f, %f)", a.x, a.y);
}

void changeToOrigin(Point *a) {
a->x = 0;
a->y = 0;
}

int main(int argc, char **argv) {
Point a = {11,12};
printf("a:(%.2f, %.2f)\n", a.x, a.y);
changeToOrigin(&a); //<- 포인터를 넘긴다.
printf("a:(%.2f, %.2f)\n", a.x, a.y);
}

실행 결과

a:(11.00, 12.00)
a:(0.00, 0.00)


위에서는 changeToOrigin()함수가 Point형 포인터를 받아서 필드를 변경한다. 호출하는 쪽에서도 a변수의 포인터(&a)를 넘겨준다. 이러면 changeToOrigin()함수 내부에서 원본을 변경할 수 있다. 실행 결과에서 보면 변수 a의 필드들이 변경되었음을 알 수 있다.

Posted by 살레시오

 기본 자료형과 마찬가지로 구조체 변수도 배열을 형성할 수 있다. 예를 들어 Point 형 변수 세 개를 배열로 생성하고 싶다면 다음과 같이 하면 된다.


Point pa[3];


이렇게 하면 크기가 3인 Point형 배열 변수 pa가 생성된다. 초기화 방법은 배열의 초기화와 동일하다.


Point p0 = {0,0};
Point p1 = {1,1};
Point p2 = {2,2};
Point pa[3] = {p0, p1, p2};


또는 다음과 같이 바로 초기화를 할 수도 있다.


Point pa[3] = {{0,0}, {1,1}, {2,2}};


배열 각 요소를 통하여 구조체의 필드를 접근할 수 있다.


Point pa[3];
pa[0].x = 0;
pa[0].y = 0;
pa[1].x = 1;
pa[1].y = 1;
pa[2].x = 2;
pa[2].y = 2;


동일한 방법으로 초기화 이후에도 각 필드의 값을 자유롭게 접근하여 읽거나 변경할 수 있다.


 배열의 크기가 커지면 반복문을 이용하여 구조체 필드를 초기화하는 것이 일반적이다. 예를 들면 다음과 같다.


ex08-09.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

int main(int argc, char **argv) {
Point pta[100];
for (int k=0; k<100; k++) {
pta[k].x = 1;
pta[k].y = 1;
}
}


또는 미리 정의된 Point형 변수를 이용할 수도 있다.


ex08-09.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

int main(int argc, char **argv) {
Point pt = {1,1};
Point pta[100];
     // 100개의 요소를 pt로 초기화시킨다.
for (int k=0; k<100; k++) {
pta[k] = pt;
}
}


이와 같이 구조체도 배열을 생성하여 사용할 수 있다.

Posted by 살레시오

 어떤 구조체의 필드가 다른 구조체의 변수가 될 수도 있다. 직사각형의 정보를 갖는 Rect라는 구조체를 작성해 보자. 이 구조체는 마주보는 두 꼭지점의 의 좌표를 가지고 있어야 한다.


typedef struct {
double x;
double y;
} Point;

typedef struct {
Point leftTop;
Point rightBot;
} Rect;

여기에서 보면 Rect 구조체는 두 개의 Point 구조체 변수를 가지고 있다. 이런 식으로 구조체의 필드가 다른 구조체의 변수가 될 수 있다.


 구조체 Rect를 초기화 시키는 방법은 다음과 같이 몇 가지 방법이 있다.


// 초기화 방법 1 : 모든 구조체 변수를 생성과 동시에 초기화함
Rect ra = {{0, 1}, {11, 12}};
// 초기화 방법 2 : 이미 정의된 Point 변수 이용
Point p1 = {0, 1}, p2 = {11, 12};
Rect rb = {p1, p2};
// 초기화 방법 3: 선언 후 각각의 필드에 초기값 대입
Rect rc;
rc.leftTop.x = 0;
rc.leftTop.y = 1;
rc.rightBot.x = 11;
rc.rightBot.y = 12;


이제 사각형의 면적을 구하는 함수를 추가한 전체 소스 코드는 다음과 같다.


ex08-08.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

typedef struct {
Point leftTop;
Point rightBot;
} Rect;

double calcArea(Rect);

int main(int argc, char **argv) {
// 초기화 방법 1 : 모든 구조체 변수를 생성과 동시에 초기화함
Rect ra = {{0, 1}, {11, 12}};
// 초기화 방법 2 : 기정의 된 Point 변수 이용
Point p1 = {0,1}, p2 = {11, 12};
Rect rb = {p1, p2};
// 초기화 방법 3: 선언 후 각각의 필드에 초기값 대입
Rect rc;
rc.leftTop.x = 0;
rc.leftTop.y = 1;
rc.rightBot.x = 11;
rc.rightBot.y = 12;
printf("area of ra : %f\n", calcArea(ra));
printf("area of rb : %f\n", calcArea(rb));
printf("area of rc : %f\n", calcArea(rc));
}

double calcArea(Rect r) {
double dx = r.rightBot.x - r.leftTop.x;
double dy = r.rightBot.y - r.leftTop.y;
return dx*dy;
}

실행 결과

area of ra : 121.000000
area of rb : 121.000000
area of rc : 121.000000


이와 같이 구조체의 필드로 다른 구조체 변수도 얼마든지 사용할 수 있다. 다만 구조체를 정의하는 순서에 유의하면 된다. 당연한 얘기지만 Rect 구조체가 Point 구조체 뒤에 와야 한다.

Posted by 살레시오

 구조체는 정의되면 새로운 자료형처럼 사용할 수 있으며 기본 자료형을 함수에 넘기는 것과 동일한 방법으로 구조체 변수도 사용할 수 있다. 전 절에서 예로 든 Point 구조체를 예로 들어서 두 점간의 거리를 구하는 함수를 작성해 보자. 함수의 이름은 getDist()라고 하고 Point 구조체 변수 두 개를 받아서 double형 값(거리)를 반환해야 한다. 함수 본체는 다음과 같이 작성할 수 있다.


double getDist(Point a, Point b) {
double dx = b.x - a.x;
double dy = b.y - a.y;
double dist = sqrt(dx*dx + dy*dy);
return dist;
}


여기서 함수 헤더를 보면 기본 자료형의 경우와 동일한 방법으로 포인터 변수를 받음을 알 수 있다. 지역변수 a와 b는 Point형 변수이므로 Point구조체의 필드를 사용할 수 있다.


 두 점의 거리를 구하는 전체 프로그램은 다음과 같다.


ex08-06.c

#include <stdio.h>
#include <math.h>

typedef struct {
double x;
double y;
} Point;

double getDist(Point a, Point b);

int main(int argc, char **argv) {
Point a = {1.1, 2.2};
Point b = {3.3, 4.4};

double dist = getDist(a, b); //함수 호출
printf("a = {%f, %f}\n", a.x, a.y);
printf("b = {%f, %f}\n", b.x, b.y);
printf("dist = %f\n", dist);
}

double getDist(Point a, Point b) {
double dx = b.x - a.x;
double dy = b.y - a.y;
double dist = sqrt(dx*dx + dy*dy);
return dist;
}

실행 결과

a = {1.100000, 2.200000}
b = {3.300000, 4.400000}
dist = 3.111270


함수 getDist() 내에서 수학함수 sqrt()를 사용하기 위해서 math.h를 인클루드 시켰다.


 이와 같이 구조체를 정의하는 것은 새로운 자료형을 만드는 것과 같다. 구조체가 한 번 정의되면 기본 자료형과 동일한 방법으로 변수를 생성할 수 있고 함수의 인자로 넘길 수 있으며 함수의 반환값이 될 수도 있다. 예를 들어서 두 점의 좌표를 받아서 Point형 변수를 반환하는 간단한 함수를 작성해 보자.


ex08-07.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

Point getPoint(double, double); //← (1)

int main(int argc, char **argv) {
Point a = getPoint(0,0);
Point b = getPoint(1,1);
printf("a = {%f, %f}\n", a.x, a.y);
printf("b = {%f, %f}\n", b.x, b.y);
}

Point getPoint(double x, double y) {
Point a = {x, y};
return a;
}

실행 결과

a = {0.000000, 0.000000}
b = {1.000000, 1.000000}


이 예에서 getPoint()함수는 Point형 변수를 반환한다. 따라서 함수를 선언할 때 반환형을 (1)과 같이 명시해야 한다. 그리고 함수 내부에서는 Point형 변수를 return해야 한다.

Posted by 살레시오

 구조체를 초기화하는 방법은 선언과 동시에 초기화하는 방법과 이미 선언된 구조체를 초기화시키는 방법 두 가지가 있다. 선언과 동시에 초기화하는 예를 들면 다음과 같다.


ex08-01

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

int main(int argc, char **argv) {
Point a = {1.0, 1.0}; //<--
}


이렇게 Point타입으로 a란 변수를 만들면서 해당하는 값을 바로 넣어주는 방법을 사용할 수 있으며 이 방법을 사용할 때에는 해당 구조체의 멤버변수 순서와 같은 차례로 초기값을 넣어야 한다.

 구조체 변수를 먼저 선언하고 나중에 필드를 초기화시키는 방법도 있다. 같은 예를 들면 다음과 같다.


ex08-02

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

int main(int argc, char **argv) {
   Point a;
   a.x = 1.0; //<--
   a.y = 1.0; //<--
}


여기서 a.x는 a라는 구조체 변수의 필드 x를 나타낸다. a.y는 필드 y를 나타낸다. 이와 같이 구조체 변수의 필드는 점(.)으로 구분하여 접근한다.


 다른 예로 Person이라는 구조체를 작성해 보자. 필드로는 나이와 키 그리고 이름 정보를 가지고 있어야 한다..


ex08-03.c
#include <stdio.h>

typedef struct {
char strName[10];
int iAge;
float fHeight;
} Person;

int main(int argc, char **argv) {
Person park = {"salesio", 45, 171.5};
Person jang = {"sophia", 45, 165.0};
printf("name:%s, age:%d, height:%.1f\n",
             park.strName, park.iAge, park.fHeight);
printf("name:%s, age:%d, height:%.1f\n",
             jang.strName, jang.iAge, jang.fHeight);
}
실행 결과
name:salesio, age:45, height:171.5
name:sophia, age:45, height:165.0


이 예에서 Person구조체의 필드는 세 개로 strName, iAge, fHeight 이고 각각 문자열 변수, 정수형 변수, 실수형 변수이다. park과 jang이라는 Person 구조체 변수를 생성한 뒤 각각의 필드를 출력하는 예제이다.


   같은 구조체형이라면 대입연산자 =를 이용해서 모든 멤버변수의 값을 복사할 수 있다. 즉, 다른 구조체 변수의 필드값으로 새로운 구조체 변수의 필드를 초기화할 수 있다. 앞에서 예를 든 Point 구조체를 이용하여 예를 들어보자.


ex08-04.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

int main(int argc, char **argv) {
Point a = {1.1, 1.2};
Point b = a; // a를 이용하여 b를 초기화
Point c = b; // b를 이용하여 c를 초기화
c.y = 2.0;
printf("a = {%f, %f}\n", a.x, a.y);
printf("b = {%f, %f}\n", b.x, b.y);
printf("c = {%f, %f}\n", c.x, c.y);
}

실행 결과

a = {1.100000, 1.200000}
b = {1.100000, 1.200000}
c = {1.100000, 2.000000}


위에서 Point변수 c는 필드 y가 2.0으로 변경되었으며 출력 결과에 그것이 반영되어 있음을 알 수 있다.

Posted by 살레시오

 지금까지 C언어가 제공하는 여러 타입의 데이터를 써왔다. char, int, float 등의 기본 자료형  변수에는 하나의 데이터만 담을 수 있다. 어떤 대상이 여러 종류의 데이터를 가져야 하는 경우에는 구조체를 사용하는데 구조체는 단일 변수들을 묶어서 하나의 이름으로 관리할 수 있는 방법을 제공한다. 예를 들어 어떤 회사원의 정보를 저장하려면 이름도 필요하고 생년월일이나 주소, 사번 등등 많은 요소가 그 한사람에 관련된 정보가 될 것이다. 이들 요소를 모두 따로 만들어도 나타내는 일이 가능은 하지만 한 이름으로 모든 요소를 관리하면 효율적일 것이다. 즉, 구조체는 간단히 말해서 변수들의 모임이라고 할 수 있다. 변수들의 모임이라는 점에서 배열과 유사한 점이 있지만 배열은 같은 형의 데이터들의 모임이고 구조체는 서로 다른 형의 데이터들의 모임이라고 생각하면 된다.


 예를 들어 점의 좌표 정보를 갖고 있는 구조체를 작성해 보자. 좌표는 x값과 y값을 가진다. 구조체는 struct 이라는 키워드를 이용하여 선언한다.


struct {
   double x;
   double y;
};


이것은 double형 변수 x와 double형 변수 y를 갖는 구조체를 선언한 것이다. x와 y를 구조체의 필드(field)라고 한다.


 하지만 이것만으로는 이 구조체를 이용하여 데이터(변수)를 생성할 수 없다. 이 구조체를 이용하여 변수를 생성하려면 이 구조체에 이름을 지정해주어야 한다. 이를 위해서 보통  typedef 명령과 조합하여 구조체를 정의하는 방법이 많이 사용된다. typedef는 전에도 나왔지만 새로운 변수형을 선언하는 명령이다. 다음 예를 보자.


typedef struct {
   double x;
   double y;
} Point;


이와 같이 작성하면 struct {,,,} 구조체를 Point라는 이름으로 정의한 새로운 자료형이 생긴 것과 같이 쓸 수 있다. 그리고 이후로는 Point란 이름으로 변수를 선언할 수 있다.


Point a, b;


이렇게 하면 변수 a와 b는 구조체인 Point 변수이며 각각 별도의 필드 x와 y를 갖는다.

Posted by 살레시오


 윈도에서 gcc/g++ 을 사용할 수 있도록 하는 툴이 minGW 이다. IDE(eclipse, netbeans, codelite 등등 어느 것이나)를 설치한 후 MinGW를 깔고 적절히 설정해야 프로그램을 개발할 수 있다. (MinGW나 cygwin 이나 설치 과정과 설정이 상당히 번거롭다는 점은 감안하자. 제대로만 하면 엄청난 툴을 무료로 사용할 수 있다.)


설치 과정

먼저  http://www.mingw.org 로 접속하여 인스톨 프로그램을 다운로드 받는다.



오른쪽 [Download Installer]를 클릭하면 인스톨러를 다운로드 받을 수 있다.



실행하면 아래와 같은 화면이 나타난다.



설치 디렉토리를 설정하고 설치를 진행한다. (보통은 폴더를 바꾸지 않는다.)


설치가 완료 되면 아래와 같은 MinGW Installation Manager가 실행된다.



이때 Basic Setup Tree에서 mingw-developer-toolkit, mingw32-base, mingw32-gcc-g++, msys-base를 선택한다. 각 항목에서 클릭한 후 ‘Mark’메뉴를 선택한다.



메뉴 “Installation > Apply Changes”를 통해 시스템에 적용한다.





[Apply] 를 눌러 설치를 진행한다. (이 단계에서 시간이 좀 걸린다.)




기본 설치가 완료되면 [Close]를 눌러 Applying scheduled changes 창을 닫는다. MinGW 인스톨러도 종료한다.


환경 설정

 설치만 했다고 끝나는 것은 아니고 Windows의 환경변수에 MinGW에 대한 경로를 설정해야 한다.


먼저 시작버튼>내 컴퓨터(마우스 오른쪽 버튼)>속성 메뉴를 선택한다.


좌상단 맨 밑의 “고급 시스템 설정”을  선택한다.


“고급 탭” 선택 후 하단의 [환경변수(N)] 버튼을 클릭한다.


아래 “시스템 변수(S)” 항목 중 “Path” 항목을 찾아 선택하고 [편집…] 버튼을 선택한다.




“변수 값(V)” 항목에서 이전에 있던 값은 그대로 두고 가장 오른쪽에 커서를 위치한 뒤

“;C:\MinGW\bin;C:\MinGW\msys\1.0\bin” 를 추가한다. 앞의 경로는 gcc/g++/gdb/make 를 실행가능하게 해주고 뒤에 것은 리눅스 명령을 윈도환경에서 사용 가능하게 해주는 것이다. 맨 앞의 세미콜론(;)을 빠뜨리지 말자.


[확인]버튼을 누른다.


테스트



“시작버튼/실행창&검색창”을 통해 “cmd"를 실행하여 도스창을 연다. 그리고 ls 명령을 입력하여 실행되어야 정상이다.



이제 gcc와 g++의 버전을 확인해 보자.



이렇게 나왔다면 MinGW설치가 완료된 것이다.



Posted by 살레시오

 C++ 에서 동적으로 메모리를 생성하고 반환하는 명령은 new와 delete인데 이것을이용하면 객체의 배열도 동적으로 생성하고 반환할 수 있다. new를 이용하여 Rect 라는 클래스 객체의 배열을 동적으로 생성하는 구문은 다음과 같다.


Rect *pRect = new Rect[3];


이렇게 하면 연속된 세 개의 Rect 객체가 생성되는데 이 때 각 객체에 대해 기본 생성자 Rect() 가 호출된다. 이렇게 생성된 객체 배열은 보통의 배열처럼 사용된다.


pRect[0].setWidth(11);

(pRect+1)->setWidth(21); //포인터의 멤버 함수 호출 시 -> 사용

pRect[2].setWidth(33);


이렇게 할당 받은 배열의 메모리를 반환하는 형식은 다음과 같다.


delete [] pRect;


delete 명령과 pRect 포인터 이름 사이의 [] 를 빠뜨리면 안 된다. 이렇게 하면 세 개의 Rect 객체가 소멸되는데 이 때 각 객체의 소멸자가 호출된다.


 한 가지 주의할 점은 new를 이용하여 동적으로 객체 배열을 생성할 때, 매개 변수가 있는 생성자를 호출할 수 없다는 점이다. 만약 어떤 객체를 배열로 생성하여 사용할 경우 이 점을 염두에 두고 생성자와 멤버 함수를 설계해야 한다.


class Rect {
public :
   ….
   Rect();
   Rect(double width, double height); //(2)
   ….
};


위의 예에서 두 번째 생성자 (2)는 배열을 생성하면서 동시에 호출할 수 없다. 따라서 다음과 같이 분리해 놓고


class Rect {
public :
   ….
   Rect();
   void set(double width, double height);
   ….
};


객체 배열 생성 후 반복문으로 순차적으로 set()함수를 호출하는 방법을 사용해야 한다.


Rect *pRect = new Rect[100];
for (int k=0; k<100; k++)
   pRect[k].set(11,22);




Posted by 살레시오

C/C++ 의 if 문 예제 1

C,C++ 2015.06.25 17:11

4.1.1 조건문을 사용한 예제 1     [doc]    [smts]

여기에서는 if 문을 사용한 예제를 풀어보도록 하겠다.


1. 세 개의 float 형 숫자를 사용자에게 입력 받아서 가장 큰 수를 출력하는 프로그램을 작성하라.


세 float형 변수를  f1, f2, f3 라고 하고 가장 큰 수를 저장하는 변수를 fMax 라고 하 하자. 간단한 알고리듬을 다음과 같이 생각해 볼 수 있다.


(a) f1과 f2 중 큰 것을 fMax에 저장한다.

(b) f3이 fMax보다 크다면 fMax 값을 f3 값으로 갱신한다.

(c) fMax를 화면에 출력한다.


이것을 그대로 프로그램으로 구현하면 다음과 같다.


ex04-06.c
#include  <stdio.h>
int main() {
float f1, f2, f3;
printf("Input three numbers : ");
scanf("%f,%f,%f",&f1, &f2, &f3);
float fMax = (f1>f2)? f1:f2; //(a)를 구현
if (f3 > fMax) //(b)를 구현
fMax = f3;
printf("The maximum value is %f.", fMax);//(c)를 구현
}

실행 예는 다음과 같다.


Input three numbers : 11,22.5,-10
The maximum value is 22.500000.

이 프로그램을 조금만 바꾸면 최소값을 구하는 프로그램으로 변경할 수 있다.


Posted by 살레시오


티스토리 툴바