설계 도면을 그릴때 실사이즈를 조정하면 안된다. 그래서 이런 함수를 사용해 실제 스케일을 건들지않고 확대 도면을 용지에 맞춰 출력할 수 있도록 작업한다.

 

위 두 함수를 잘 사용할 경우 도면을 그리는 시간을 줄일 수 있다. 이 작업은 아래 순서에 맞춰 진행하면 큰 도움이 된다.

1. 건축도면을 확실히 정리한다. 레이어를 설정하고, 작업해야하는 도면을 그리면서 레이어를 정확히 나눈다.(이 작업이 제대로 진행된 경우 시간을 단축시킬 수 있다.)

2. (출력용지 폼의) 스케일 조정한다.

3. 상세 도면 작업을 할때 기준점을 잡아 추가 상세 도면을 그릴때 문제를 막는다.(기존 도면에 출력되지 않는 레이어를 설명해 기준점을 지정해준다.)

4. 우리가 작업할 내용을 위의 방법을 따라 똑같이 진행해 덮어준다.

 

XClip

1.출력용지 폼에 맞춰 pl(ine)을 그려준다.

2.우선 제공받은 출력용지 폼을 원하는 위치에 co(py)한다.

3.sc(ale) 로 원하는 비율로 조정한다.

4.상세 도면이 필요한 부분을 비율조정한 폼에 맞춘다.

5. xc(lip) 입력 후 폼에 맞춘 도면을 클릭한다.

6. n(ew) 클릭 후 (poline)s(elct) 입력 엔터해 준다.

 

해당 작업은 블럭처리를 하는 것이지 불필요한 부분을 삭제하는게 아니다. 그래서 절대로 폭파(explode)하면 안된다. 폭파할 경우 XClip으로 잘라둔 부분이 다시 나타나면서 모든 부분이 나타난다.

 

wipeout

폴리선을 이용해 출력하고싶지 않은 부분을 가려주는 기능을 한다.

1. 가리고 싶은 구간에 맞춰 닫힌 폴리선을 그린다. (도형, pl 모두 가능)

2. wipeout 엔터

3. (f 엔터 / wipeout의 테두리 존재 여부 선택 y,n)

4. p 엔터 닫힌 폴리선 선택

5. 폴리선 삭제 여부 선택 y,n

또는 폴리선을 만들어두지 않고 바로 wipeout 명령어를 불러와 실행할 수도 있다.

 

 

구조체는 프로그램 개발에 있어서 매우 중요한 요소입니다. 구조체란 하나 이상의 변수(포인터 변수와 배열 포함)를 묶어서 새로운 자료형을 정의하는 도구입니다. 구조체 정의는 다음과 같이 합니다.


struct point        // point라는 이름의 구조체 정의

{

int xpos;       // point 구조체를 구성하는 멤버 xpos

int ypos;       // point 구조체를 구성하는 멤버 ypos

}


이때 point라는 이름이 int나 double과 같은 자료형의 이름이 되는 것입니다. 그러면 이렇게 선언된 구조체를 대상으로 변수 선언을 할 수 있는데 다음과 같이 진행하면 됩니다.


struct type_name val_name;


위 문장에서 보이듯이 구조체 변수를 선언할 때 맨 앞에 struct 선언을 추가한 이후 구조체의 이름과 변수의 이름을 선언하면 됩니다.


struct point pos;    // pos란 구조체 변수에 point 구조체 멤버 xpos와 ypos가 만들어졌다.


이렇게 만들어진 구조체 변수의 접근은 구제초변수의 이름.구조체 멤버의 이름으로 부를 수 있습니다.


pos.xpos = 20;    // 구조체 변수 pos의 멤버 xpos에 20 저장


코드를 작성하다보면 하나하나 데이터를 입력하는게 귀찮을 때가 있습니다. 그럴때 다음과 같은 방법으로 한 번에 입력도 가능합니다.


struct point pos = {10, 20}    // 문자 데이터는 ""를 사용하면 된다.


지금까지 구조체 변수에 대해 알아봤는데 구조체 배열도 동일한 방법으로 진행할 수 있습니다.구조체 배열의 초기화때 데이터 입력은 배열의 길이만큼 중괄호를 이용하면 됩니다.

구조체 포인터도 동일한 방법으로 진행됩니다. 다만, 구조체의 포인터 변수를 표현할때 *연산과 .연산을 하나의 ->연산으로 대신 할 수 있음을 기억하길 바랍니다. 의미는 100% 동일합니다.


pptr->xpos == (*pptr).xpos


구조체는 포인터 변수를 구조체 멤버로 선언도 가능합니다. 즉 다음과 같이 선언이 가능합니다.


struct point

{

int xpos;

int ypos;

struct point *ptr;        // 구조체 point의 포인터 변수 선언

}


구조체 변수를 선언할 때는 무조건 struct 선언을 해야합니다. 하지만 이게 상당히 귀찮습니다. 그렇다면 구조체를 정의한 후에 typedef 선언을 추가하면 됩니다. typedef 선언은 기존에 존재하는 자료형의 이름에 새 일므을 부여하는 것을 목적으로 하는 선언입니다. 예를 들어 다음과 같습니다.


typedef int INT;        //int의 또 다른 이름 INT를 부여. INT num; == int num;


typedef 선언에 있어 새로운 이름의 부여는 가장 마지막에 등장하는 단어를 중심으로 이뤄집니다.


typedef name name1 name2;


이때 마지막에 등장한 name2이 'name name1'에 부여된 새 이름이 됩니다. 그런데 이렇게 일일이 구조체를 또 선언해주는 것도 귀찮아 다음과 같은 방법을 가장 많이 사용합니다.


typedef struct point

{

int xpos;

int ypos;

} Point;


이렇게 선언하면 다음줄에 struct point가 Point라고 따로 선언을 해주지 않아도 바로 적용이 됩니다. 그리고 이렇게 사용하다보니 구조체 이름을 생략하기도 합니다. 즉, struct point라고 선언한 point를 생략하는 것입니다.

지금까지 구조체 사용법을 확인해봤습니다. 사용법을 통해 느꼈겠지만, 구조체는 상당히 자주 사용됩니다. 그러면 구조체를 정의하는 이유가 궁금할 것입니다. 그 이유는 구조체를 통해서 연관 있는 데이터를 하나로 묶을 수 있는 자료형을 정의하면, 데이터의 표현 및 관리가 용이해지고, 그만큼 합리적인 코드를 작성할 수 있기 때문입니다.

'배울 것들 > 언어' 카테고리의 다른 글

C프로그래밍 11/? 포인터&함수  (0) 2018.11.22
C프로그래밍 10/? 포인터  (0) 2018.11.21
C프로그래밍 9/? 배열  (0) 2018.11.20
C프로그래밍 8/? 함수  (0) 2018.11.20
C프로그래밍 7/? 조건문  (0) 2018.11.19

함수는 인자를 전달받도록 정의할 수 있습니다. 사실 함수라는 이름이 붙은 이유도 인자의 전달과 값의 반환이 가능하기 때문입니다.


인자전달의 기본방식은 값의 복사입니다. 즉, 복사가 되는 것 뿐이기 때문에 함수가 호출되고 나면, 전달되는 인자와 매개변수는 별개가 됩니다.


int SimpleFunc(int num) {....}

int main(void)

{

int age = 17;

SimpleFunc(age);        //age에 저장된 값이 매개변수 num에 복사된다

}


위 코드의 SImpleFunc 함수의 호출을 통해서 인자로 age를 전달하고 있습니다. 그러나 실제로 전달되는 것은 age가 아닌, age에 저장된 값일뿐입니다. 그리고 배열을 통째로 넘겨받으려면 매개변수로 배열을 선언할 수 있어야하지만, 허용되지 않습니다. 대신에 함수 내에서 배열에 접근할 수 있도록 배열의 주소값을 전달하는 것은 가능합니다. 이해를 돕기위해 예를 들어보겠습니다. 아파트를 보고 싶어 하는 사람 앞에 아파트를 통째로 복사해다 놓을 수 없다면, 아파트의 주소를 가르쳐줘서 직접 찾아가게 하면 되는 경우를 생각해보시면 이해가 될 것입니다. 예를 통해 보겠습니다.


#include <stdio.h>


void ShowArayElem(int *param, int len)

{

int i;

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

printf("%d ", param[i]);

printf(("\n");

}

int main(void)

{

int arr1[3] = {1, 2, 3];

int arr2[5] = {4, 5, 6, 7, 8];

ShowArayElem(arr1, sizeof(arr1) / sizeof(int));

ShowArayElem(arr2, sizeof(arr2) / sizeof(int));

return 0;

}


결과

1 2 3

4 5 6 7 8


그러면 배열과 동일한 선언을 확인했다. 즉 포인터입니다. 위 코드에서 ShowArayElem함수의 int형 배열의 주소 값을 인자로 전달받을 수 있도록 int형 포인터 변수가 선언되었는데 이를 배열로 선언해도 동일합니다.


int *param >> int param[]


하지만 이 둘이 같은 선언으로 간주되는 경우는 매개변수의 선언을 제한됩니다.


함수호출

Call-by-value와 Call-by-reference는 구분하는 기준은 함수의 인자로 전달되는 대상에 있습니다. 사실, 이를 구분하는데 큰 의미는 없습니다. 하지만 이를 구분하는 이유는 실수를 막기 위함에 있습니다. 아래 설명을 통해 둘의 차이를 확인해보겠습니다.

Call-by-value

함수를 호출할 때 단순히 값을 전달하는 형태의 함수호출입니다. 


void Swap(int n1, int n2)

{

int temp = n1;

n1 = n2;

n2 = temp;

}


Call-by-reference

메모리의 접근에 사용되는 주소 값을 전달하는 형태의 함수호출입니다.


void Swap(int *ptr1, int *ptr2)

{

int temp = *ptr1;

*ptr1 = *ptr2;

*ptr2 = temp;

}


이러한 형태는 scanf 함수호출시에도 확인했을 것입니다.


scanf("%d", &num);


이처럼 scanf 함수호출이 완료되면 변수 num에는 값이 채워집니다. 이때 변수 num에 그 값을 채우는 일을 scanf 함수가 하는데 이때 변수 num의 주소값을 알아야합니다.  이렇듯 scanf 함수의 호출도 Call-by-reference형태의 함수호출에 해당합니다.


const 선언

const 선언은 위치에 따라 그 의미가 변할 수 있습니다. 두 예를 모두 보겠습니다.

이는 포인터 변수가 참조하는 대상의 변경을 허용하지 않습니다. 


int num = 20;

const int *ptr = &num;

*ptr = 30;    // 컴파일 에러 ptr이 가리키는 변수에 저장된 값을 변경하는건 불가능하다는 의미

num = 40;   // 컴파일 성공 ptr이 가리키는 변수 자체는 상수가 아니라 변경 가능


이는 포인터 변수의 상수화를 진행시키는 방법입니다.


int num1 = 20;

int num2 = 30;

int *const ptr = &num1;

ptr = &num2;    // 컴파일 에러 ptr은 상수라서 변경 불가

*ptr = 40;         // 컴파일 성공 ptr이 가리키는 대상에 저장된 값은 변경 가능


const는 코드를 작성하다보면 쉽게 지나칠 수 있는 부분입니다. 그러면 이는 왜 생겼는지 의문이 생길 것입니다. 그 이유는 실수로 변수에 잘못된 값으로 설정했을 때, 컴파일 시 발견되지않습니다. 그래서 이처럼 중요한 변수의 값을 실수로 변경하는 일을 방지하기 위해 사용할 방법을 찾아보다 이용하게 된게 아닐까 합니다.



'배울 것들 > 언어' 카테고리의 다른 글

C프로그래밍 /? 구조체  (0) 2018.11.27
C프로그래밍 10/? 포인터  (0) 2018.11.21
C프로그래밍 9/? 배열  (0) 2018.11.20
C프로그래밍 8/? 함수  (0) 2018.11.20
C프로그래밍 7/? 조건문  (0) 2018.11.19

포인터를 이용하면 메모리에 직접 접근이 가능합니다. 아래의 글을 코드로 작성해 포인터의 선언방법과 변수의 주소 값을 얻을 때 사용하는 &연산자에 대해 보겠습니다.

'정수 7이 저장된 int형 변수 num을 선언하고 이 변수의 주소 값 저장을 위한 포인터 변수 pnum을 선언하자. 그리고 나서 pnum에 변수 num의 주소 값을 저장하자'


int main(void)

{

int num = 7;

int *pnum;           // 포인터 변수 pnum의 선언

pnum = &num;    // num의 주소값을 포인터 변수 pnum에 저장

}


위 코드를 보면 &연산자는 '오른쪽에 등장하는 피연산자의 주소 값을 반환하는 연산자'입니다. 따라서 위 문장에서는 &연산자의 결과로 변수 num의 주소값이 반환되며, 이를 포인터 변수 pnum에 저장하게 됩니다. 포인터를 선언할 때는 가리키고자 하는 변수의 자료형에 맞춰 선언해야합니다. 위 코드는 int형 변수를 가리키기 때문에 따라서 int형 포인터를 선언했습니다.

지금부턴 포인터의 가장 중요한 &연산자와 *연산자를 살펴보겠습니다.


&연산자

&연산자는 피연산자의 주소 값을 반환하는 연산자입니다. &연산자의 피연산자는 변수이어야하며, 상수는 피연산자가 될 수 없습니다. 그리고 다음과 같이 변수의 자료형이 맞지 않는 포인터 변수의 선언은 문제가 될 수 있습니다.


int main(void)

{

int num1 = 5;

double *pnum1 = &num1;


double num2 = 5;

int *pnum2 = &num2;

}


*연산자

*연산자는 포인터가 가리키는 메모리 공간에 접근할 때 사용하는 연산자입니다. 


int mian(void)

{

int num = 10;

int *pnum = &num;    // 포인터 변수 pnum이 변수 num을 가리키게 하는 문장

*pnum = 20;             // 포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 20을 저장해라

printf("%d", *pnum);   // 포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 정장된 값을 출력해라

}

이렇듯 사실상 *pnum은 포인터 변수 pnum이 가리키는 변수 num을 의미하는 것입니다.


포인터 선언시 주의할 점

포인터 변수를 선언할 때 초기화를 동시에 진행하는 습관을 들여야합니다. 그렇지않으면, 포인터 변수는 쓰레기 값으로 초기화 됩니다. 아래의 코드를 보고 흔히 하는 실수를 확인해보겠습니다.


int main(void)

{

int *ptr;        //포인터 변수 ptr은 쓰레기 값으로 초기화 된다.

*ptr = 200;

}

이 경우 포인터 변수를 선언만하고 초기화하지않아 쓰레기 값으로 초기화 됐습니다. 이때, ptr이 가리키는 메모리 공간이 매우 중요한 위치였다면, 시스템 전체에 심각한 문제를 일이클 수도 있는 상황입니다.


int main(void)

{

int *ptr = 125;

*ptr = 10;

}


이 경우도 위의 실수와 동일한 문제가 발생합니다. 125번지에 저장하는건 결국 쓰레기값에 저장한 경우와 다르지 않습니다. 그러면 포인터를 선언만 하고 이후에 유효한 주소 값을 채워 넣는 방법을 생각해보겠습니다.


int *ptr1 = 0;         // 여기서 '0'은 0번지가 아닌 널포인트를 의미한다.

int *ptr2 = NULL;    // NULL은 사실상 0을 의미한다.


물론 이렇게 진행하면 프로그램이 멈추는 현상이 동일하게 일어나지만, 잘못된 메모리의 접근에 대해 보호장치가 없는 운영체제에서도 시스템에 치명적인 영향을 주지 않습니다.



포인터와 배열의 관계

배열의 이름도 포인터입니다.단, 그 값을 바꿀 수 없는 '상수 형태의 포인터'입니다. 

비교조건 \ 비교대상 

포인터 변수 

배열의 이름 

이름이 존재하는가? 

존재한다 

존재한다 

무엇을 나타내거나 저장하는가? 

메모리의 주소 값 

메모리의 주소 값 

주소 값의 변경이 가능한가? 

가능하다 

불가능하다(=상수 형태란 의미) 


포인터와 배열의 관계를 간단하게 정리해봤습니다. 거의 동일함을 알 수 있습니다. 다만, 주소값의 변경 유무에 차이가 있습니다. 이렇든 배열도 포인터이므로 배열의 이름을 피연산자로 하는 *연산도 가능합니다.


int arr1[3] = {1, 2, 3};

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

*arr1 += 100;

printf("%d \n", *arr1[0]);

return 0;


이와 같이 코드를 입력하면 arr1의 값은 1과 101로 출력됩니다. 1차원 배열이름의 포인터 형은 배열의 이름이 가리키는 대상을 기준으로 결정하면 됩니다. 그리고 포인터를 배열의 이름처럼 사용할 수도 있습니다. 이는 아래 코드를 통해 설명하겠습니다.


int arr[3] = {15, 25, 35};

int *ptr = &arr[0];        // int *ptr = arr; 과 동일한 의미


printf("%d %d \n, ptr[0], arr[0]);        // 결과적으로 15 15

printf("%d %d \n, ptr[1], arr[1]);        // 결과적으로 25 25

printf("%d %d \n, ptr[2], arr[2]);        // 결과적으로 35 35

printf("%d %d \n", *ptr, *arr);           // 결과적으로 15 15

return 0;


이렇듯 배열과 포인트 둘 다 포인트이기 때문에 나온 결과입니다. 이처럼 사용할 일은 거의 없지만, 이러한 일이 가능하다는 사실은 알고 있어야합니다.


포인터 연산

포인터는 *연산 이외에 증가 및 감소연산도 가능합니다. 결과는 포인터의 특성에 맞춰 진행합니다. 이전에 설명했듯이 TYPE형 포인터를 대상으로 n의 크기만큼 값을 증가 및 감소 시, n x sizeof(TYPE)의 크기만큼 주소 값이 증가 및 감소합니다. 즉, int형 포인터면 1증가시 4가 증가하고 double형은 8이 증가합니다.

*(++ptr) = 20;    // ptr에 저장된 값 자체를 변경

*(ptr+1) = 20;    // ptr에 저장된 값은 변경되지 않음


위 문장의 차이를 잘 이해해야 합니다. ++연산의 결과로 인해 포인터 변수 ptr에 저장된 값이 4만큼 증가합니다. 하지만 +연산의 결과로 인해 포인터 변수 ptr에 저장된 값이 증가하지 않습니다. 다만 증가된 값을 연산의 결과로 얻어서 *연산을 진행할 뿐입니다. 즉 arr[i] == *(arr+i)란 의미를 알 수 있습니다.


두 가지 형태의 문자열 표현

char str1[ ] = "My STring";    // 변수 형태의 문자열

char *str2 = "Your String";    // 상수 형태의 문자열


첫 번째 경우 배열을 기반으로 하는 '변수 형태의 문자열' 선언입니다. 반면 두 번째 경우 포인터를 기반으로 문자열을 선언하는 방법입니다. 이렇게 선언을 하면 메모리 공간에 문자열 "Your String"이 저장되고, 문자열의 첫 번째 문자 Y의 주소값이 반환됩니다. 그리고 그 반환 값이 포인터 변수 str2에 저장됩니다. 그래서 str2를 char형 포인터로 선언한 것입니다. 그렇다면 위 두 문자열 선언의 차이점은 무엇인지 보겠습니다.

 

\0 

<--------------배열 str1-------------->

 ㅁ------------->

 Your String \0

↑포인터 str2 

자동 할당된 문자열 


str1은 그 자체로 문자열 전체를 저장하는 배열이고, str2는 메모리상에 자동으로 저장된 문자열 "Your String"의 첫 번째 문자를 단순히 가리키고만 있는 포인터 변수입니다. str1, str2 모두 문자열의 시작 주소 값을 담고 있다는 측면에서 동일하나, '배열이름 str1은 계속해서 문자 M이 저장된 위치를 가리키는 상태이어야 하지만 포인터 변수 str2는 다른 위치를 가리킬 수 있다는 차이'가 있습니다.

이전에 말한대로 str1은 변수 형태의 문자열로 변경이 가능하고, str2은 상수 형태의 문자열로 변경이 불가능합니다. 


포인터배열

포인터 변수로 이뤄진, 그래서 주소 값이 저장이 가능한 배열을 포인터 배열이라 합니다. 이러한 배열의 선언방식은 기본 자료형 배열의 선언방식과 동일합니다.


int *arr1[2]            // 길이가 20인 int형 포인터 배열 arr1

double *arr2[30]    // 길이가 30인 double형 포인터 배열 arr2


그리고 포인터 배열또한 문자열 배열이 가능합니다. 이전에 설명한것처럼 큰따움표를 기준으로 문자열의 주소 값을 저장할 수 있습니다.


char *strArr[3] = {"Simple", "String", "Array"};

printf("%s \n", strArr[0]);        //출력값 Simple

printf("%s \n", strArr[1]);        //출력값 String

printf("%s \n", strArr[2]);        //출력값 Array


'배울 것들 > 언어' 카테고리의 다른 글

C프로그래밍 /? 구조체  (0) 2018.11.27
C프로그래밍 11/? 포인터&함수  (0) 2018.11.22
C프로그래밍 9/? 배열  (0) 2018.11.20
C프로그래밍 8/? 함수  (0) 2018.11.20
C프로그래밍 7/? 조건문  (0) 2018.11.19

1차원 배열

단순하게 배열은 '둘 이상의 변수를 모아 놓은 것'으로 설명할 수 있습니다.


int oneDimArr[4]


int               배열을 이루는 요소(변수)의 자료형

oneDimArr    배열의 이름

[4]               배열의 길이

즉, int형 변수 4개로 이뤄진 배열을 선언하되, 그 배열의 이름은 oneDimArr로 한다는 의미입니다. 선언된 배열은 [ ] 연산자 사이에 배열의 위치정보를 명시해 특정 위치에 원하는 값을 저장할 수 있습니다. 이때 들어가는 숫자를 '인덱스'라 합니다. 이는 1부터 시작이 아닌 0부터 시작합니다. 그러면 배열의 선언과 초기화 방법을 확인해보겠습니다.


int arr1[5] = {1, 2, 3, 4, 5};          // 수나적으로 1, 2, ,3, 4, 5로 초기화 한다.

int arr2[ ] = {1, 2, 3, 4, 5, 6, 7};    // 컴파일러에 의해서 자동으로 인덱스에 7이 삽입된다.

int arr3[5] = {1, 2};                   // 3, 4, 5번째 배열요소는 0으로 채워진다.


이러한 방법으로 선언과 동시에 초기화를 진행합니다.

배열을 이용해 문자열 변수의 표현도 가능합니다.


char str[ ] = "Good morning!";    // 배열의 길이는 14로 결정된다.


자세히보면 공백을 포함해도 배열의 길이는 13인데 14로 저장됩니다. 이는 문자열 끝에 '\0'이라는 특수문자가 자동으로 삽입이 되어 실제 문자열의 길이는 14가 되는 겁니다. 따라서 문자열의 저장을 목적으로 char형 배열을 선언할 경우 '\0'이 저장될 공간까지 고려해 배열의 길이를 결정해야 합니다.

이렇듯 문자열의 끝에 자동으로 삽입되는 문자 '\0'을 가리켜 '널(null)'문자라 합니다. 이 널 문자는 C언에서 표현하는 모든 문자열의 끝에 자동으로 삽입됩니다. 따라서, 널문자의 유무로 문자 배열인지 문자열인지를 구분할 수 있습니다.

그러면 널 문자는 왜 필요한지 생각해보겠습니다. 메모리상에서 문자열은 이진 데이터로 저장되기 때문에 문자열의 시작과 끝이 표시되어 있지 않아 구분이 불가능합니다. 그래서 널 문자를 이용해서 문자열의 끝을 표시하는 것입니다.


다차원 배열

다차원 배열은 2차원 이상의 배열을 의미합니다. 즉 2차원, 3차원 배열을 총칭해서 다차원 배열이라 합니다.


int arrOneDim[10];            // 길이가 10인 1차원 int형 배열

int arrTwoDim[5][5];          // 가로, 세로의 길이가 각각 5인 2차원 int형 배열

int arrThreeDim[3][3][3];     // 가로, 세로, 높이의 길이가 각각 3인 3차원 int형 배열


이중 가장 많이 사용하는 2차원 배열을 중심으로 진행하겠습니다. 2차원 배열이 선언되면 다음의 형태로 배열이 생성(할당)됩니다.

 

1열 

2열 

3열 

4열 

5열 

1행 

[0][0] 

[0][1] 

[0][2] 

[0][3] 

[0][4] 

2행 

[1][0] 

[1][1] 

[1][2] 

[1][3] 

[1][4] 

3행 

[2][0] 

[2][1] 

[2][2] 

[2][3] 

[2][4] 

4행 

[3][0] 

[3][1] 

[3][2] 

[3][3] 

[3][4] 

5행 

[4][0] 

[4][1] 

[4][2] 

[4][3] 

[4][4] 


그렇다면 2차원 배열의 선언과 초기화를 보겠습니다.


int arr1[3][3] = {

{1},            // 1 0 0

{4, 5},         // 4 5 0

{7, 8, 9}      // 7 8 9

  };

이와같이 중괄호를 넣어주면 일부 빠진 요소에 대해서는 초기화를 생략할 수 있습니다.


int arr2[3][3] = {

1, 2, 3,

4, 5, 6,

7

};

int arr3[3][3] = {1, 2, 3, 4, 5, 7};

이처럼 진행하면 마지막에 입력한 배열 뒤에 값은 자동으로 초기화가 진행됩니다.


int arr4[ ][4] = {1, 2, 3, 4, 5, 6, 7, 8};

배열의 크기를 알려주지 않을 경우 1차원과 달리 가로또는 세로의 길이는 지정해야합니다. 그렇지 않다면 arr4의 크기가 1x8인지 2x4인지 알수가 없어 에러가 발생합니다.


3차원 배열의 경우 잘 사용하지않고, 2차원가 큰 차이가 없어 생략하겠습니다. 3차원 배열은 큐빅퍼즐이라 생각하면 쉽습니다.

'배울 것들 > 언어' 카테고리의 다른 글

C프로그래밍 11/? 포인터&함수  (0) 2018.11.22
C프로그래밍 10/? 포인터  (0) 2018.11.21
C프로그래밍 8/? 함수  (0) 2018.11.20
C프로그래밍 7/? 조건문  (0) 2018.11.19
C프로그래밍 6/? 반복문  (0) 2018.11.19

함수

함수는 코드를 작성하는데 있어 문제를 하나씩 해결하려는 이유와 이후 예상하지 못한 문제가 발생할 시 빠르고 쉽게 해결하기위해 만든다고 볼 수 있습니다. 함수는 입력에 따른 출력이 존재하는 것이라 했으나, 입력(전달인자)가 없거나 출력(반환 값)이 없는 경우도 있습니다.

유형에 따라 함수를 구분하면 전달인자와 반환 값의 존재 유무로 나눌 수 있습니다.

  • 전달인자와 반환 값 모두 있는 경우

가장 일반적인 형태의 함수입니다. 이 경우 전달인자와 반환 값 모두 같은 자료형의 변수를 선언해야합니다. 예를 들면 다음과 같습니다.


it Add(int num1, int num2)              //int형 함수 Add를 선언하고 int형 전달인자(매개변수)를 받는다.

{

int result = num1 + num2;        // 반환 값의 변수도 동일한 int형이다.

return result;                          // 반환 값


// return num1+num2; 로 작성해도 동일한 결과를 얻을 수 있다.

}


main함수에서 변수에 Add함수를 호출하면 함수의 호출문이 반환 값으로 대체됩니다. 즉, Add함수가 종료되면서 반환된 값이 대입연산을 통해 변수에 저장됩니다.

  • 전달인지나 반환 값이 존재하지 않는 경우 

void ShowAddResult(int num)    // 전달인자만 존재.

{

printf("덧셈결과 출력 %d \n", num);

}


함수 정의에서 반환형이 void로 선언됐습니다. 여기서 void는 '반환하지 않는다'라는 의미입니다. 그래서 함수 몸체에 return문이 존재하지 않습니다.


int ReadNum(void)    // 반환 값만 존재.

{

int num;

scanf("%d", &num);

return num;

}


매개변수의 선언 위치에 void 선언이 등장했습니다. 여기서 void는 '인자를 전달하지 않는다'라는 의미입니다. 이처럼 void를 이용해 전달인자와 반환 값을 받을지 말지를 선언해 함수를 만들 수 있습니다.


지금까지 함수를 만드는 방법에 대해 확인했습니다. 그러면 이렇게 만든 함수의 위치를 결정할 때 주의를 기울여야 합니다.


int main(void)

{

int num = 2;

num = Incement(num);

retunr 0;

}

int Increment(int n)

{

return n++;

}


이와 같이 코드를 작성하면 main함수에서 Incement함수를 본적이 없다면서 에러가 발생합니다. C언어는 절차지향 프로그래밍 언어로 위에서부터 순차적으로 코드를 읽어나갑니다. 그렇기때문에 main문 앞에 함수의 선언을 넣어줘야합니다.


int Increment(int n);    //함수의 선언


int mian(void)

{

......

}

int Increment(int n)    //함수의 정의

{

.....

}


main문 앞에서 함수의 선언이 이루어지고 이후에 선언된 함수의 정의가 나옵니다. 함수의 정의를 먼저 한 이후 main문을 작성해도 되지만, 코드는 혼자만 볼 것이 아니라 여러 사람이 보는 것으로 일반적인 경우를 따르는 것이 좋습니다.


변수

변수는 선언되는 위치에 따라 '전역변수'와 '지역변수'로 나뉩니다. 이 둘의 차이는 메모리상에 존재하는 기간, 변수에 접근할 수 있는 범위에 있습니다.

  • 지역변수

여기서 지역은 중괄호에 의해 형성되는 영역을 뜻합니다. 


int SimpleFuncOne(void)    // void에 매개변수가 들어가면 이 또한 지역변수이다

{

int num = 10;    // 지역변수 num

.....

}


이렇게 중괄호 안에서 선언된 변수를 지역변수라 부릅니다. 따라서 num은 중괄호를 벗어난 구간에서 호출하면 에러가 발생합니다. 지역변수는 해당 선언문이 실행될 때 메모리 공간에 할당됐다가, 선언문이 존재하는 함수가 반환을 하면(종료) 메모리 공간에서 소멸됩니다. 즉, 이전에 함수를 호출해서 생긴 값은 반환과 동시에 모두 사라집니다. 그래서 지역변수가 선언된 지역을 제외한 곳에서 또 다른 지역변수를 같은 변수로 선언해도 무방합니다.

  • 전역변수

전연벽수는 어디서든 접근이 가능한 변수로써 지역변수와 달리 중괄호 내에 선언되지 않습니다. 따라서 전역변수는 프로그램의 시작과 동시에 메모리 공간에 할당되어 종료 시까지 존재하며, 별도의 값으로 초기화하지 않으면 0으로 초기화되며, 프로그램 전체 영역 어디서든 접근이 가능합니다.


#include <stdio.h>

void Add(int val);

int num;        // 전역변수는 기본 0으로 초기화된다.

....


전역변수의 선언은 이렇게 진행합니다. 만약 전역변수와 동일한 이름의 지역변수가 선언되면 해당 지역 내에서 전역변수가 가려지고, 지역변수로의 접근이 이루어집니다. 하지만 이런 상황 자체는 만들지 않는 것이 좋습니다. 에러 발생 시 문제를 찾기도 쉽지않아 코드가 엉키면 해결하는데 오랜 시간이 걸립니다.

전역변수는 지역변수에 비해 상당히 편리한듯하지만, 사용을 최대한 제한하는 것이 좋습니다. 전역변수는 프로그램의 구조를 복잡하게 만드는 주범이기 때문입니다. 앞서 설명했듯 전역변수는 프로그램의 시작과 동시에 생성돼 종료될 때까지 존재한다 했습니다. 이는 마치 지금 하지않는 작업을 책상에 펼쳐둔거와 같습니다.

  • 추가적인 변수

static 변수

지역 변수의 휘발되는 특징은 static 선언을 통해 막을 수 있습니다. 지역변수 선언 시 static를 붙이면 지역변수도 전역변수처럼 프로그램 종료 시까지 메모리에 저장됩니다. static (지역)변수는 전역변수로 쉽게 대체가 가능합니다. 하지만 static (지역)변수는 전역변수보다 안정적이므로, 가능하다면 전역변수를 static (지역)변수로 대체하면 프로그램의 안전성을 높일 수 있습니다.


register 변수

지역변수의 또 다른 추가 선언입니다. static함수와 같이 선언을 할 수 있습니다. 이는 변수가 CPU 내에 존재하는 '레지스터'라는 메모리 공간에 저장될 확률이 높아집니다. 레지스터는 CPU내에 존재하는 매우 작은 메모리입니다. 하지만 CPU내에 존재하기 때문에 이 메모리에 저장된 데이터를 대상으로 하는 연산은 매우 빠릅니다. 하지만 레지스터의 최종 결정은 컴파일러가 내립니다. 또한 이는 지역변수만 가능합니다.


재귀함수

재귀함수란 같은 함수 내에서 자기 자신을 다시 호출하는 함수를 의미합니다.


void Recursive(void)

{

printf("Recursuve call! \n");

Recursuve();    // 자기 자신을 재 호출

}


마치 에러가 날듯하지만 이는 가능하다. 쉽게 이해를 돕기위해 설명하면 Recursive함수 내에서 Recursuve의 복사본을 호출하는 것이다. 

'배울 것들 > 언어' 카테고리의 다른 글

C프로그래밍 10/? 포인터  (0) 2018.11.21
C프로그래밍 9/? 배열  (0) 2018.11.20
C프로그래밍 7/? 조건문  (0) 2018.11.19
C프로그래밍 6/? 반복문  (0) 2018.11.19
C프로그래밍 5/? 자료형  (0) 2018.11.13

선택적 실행이 가능한 코드를 구현할 때 조건문을 사용합니다.


if, if~else, if~if else~else

if

if(num1 > num2)

printf("num1이 num2보다 크다 \n");


if문은 위와 같이 조건을 소괄호로 표시합니다. 이때 해당하는 경우에만 if문이 실행되어 그에 대한 진행이 이루어 집니다.


else

키워드 else는 if와 달리 독립적으로 사용할 수 없습니다. if의 조건이 참이 아닐 경우에만 실행됩니다.

if(조건1)

{

// 조건 1 만족 시 실행

}

else

{

//조건 1을 제외한 모든 경우

}


else if

if만을 사용할 경우 매번 조건을 확인하며 넘어가야합니다. 하지만 else if를 사용하는 경우 여러 조건 중 한 가지를 만족하면 나머지를 모두 건너뛴다는 장점이 있습니다.

if(조건1)

{

//조건1 만족한 경우

]

else if(조건2)

{

//조건 2 만족한 경우

}

.........................

else

{

//앞의 모든 조건을 불만족한 경우

}

이를 정리하면 다음의 특징을 알 수 있습니다.

  • 조건의 만족여부 검사는 위에서 아래로 진행됩니다.
  • 조건이 만족될 경우 해당 블록을 실행하고 나면 마지막 else까지 건너뜁니다.


조건 연산자: 피 연산자가 세 개인 '삼 항 연산자'

if~else문을 일부 대체할 수 있는 조건 연산자입니다. 피연사자의 수가 세 개이기 때문에 삼 항 연산자라 부르기도 합니다. 


조건 ? data1 : data2;


이와 같이 사용합니다. 즉, 조건 연산자는 기호 ? 와 기호 : 으로 이뤄집니다. 이렇듯 두 개의 기호가 서로 떨어져서 하나의 연산자를 구성하기 때문에 피 연산자를 3개까지 둘 수 있습니다. 위의 예를 해석해보면 조건이 참이면 data1이 반환되고, 조건이 거짓이면 data2가 반환됩니다. 


int num3 = num1>num2 ? num1 : num2;


이렇게도 사용이 가능합니다. 이 경우, 대입 연산자보다 조건 연산자의 우선순위가 높아 조건 연산자가 먼저 진행됩니다.


continue&break

break

break는 가장 가까이서 감싸고 있는 반복문 하나를 빠져 나오게 됩니다. 하지만 break문이 if문과 함께 썼을때 빠져 나오는 동작을 진행하는 것은 아닙니다.


continue

이 경우는 break와 반대로 계속적인 진행을 하도록 만들어줍니다. continue가 입력된 조건을 만족하는 경우 다시금 조건 검사로 이동을 시킵니다.


int main(void)

{

.......

while(1)

{

if(x > 20)

break;        // 조건문을 종료합니다.

if(x/2 == 1)

continue;    // 조건문을 계속 진행합니다.


switch

if...else if...else문과 유사한 측면이 있습니다. 하지만 if문에 비해 제한적입니다.


switch(num)

{

case 1:

printf("hello \n");

break;    //해당 조건에서 빠져나온다.

case 2:

printf("World \n");

break;

default:

printf("Bye \n");

}


보면 알 수 있듯 num은 switch문으로 전달되는 인자 정보입니다. 이는 정수형 변수이어야 하는데, 대표적으로 int형 변수가 위치하게 됩니다. 그리고 저장된 값에 따라서 실행할 영역이 결정됩니다.

위의 예를 보면 알 수 있듯이 해당되는 인자의 case가 실행이 되며, 입력된 num의 값이 해당 case값과 일치하는게 없을 때 if문의 else와 같이 실행이 됩니다. 그리고 중간중간에 보이는 break문은 해당 조건에서 빠져나오는 역할을 진행합니다. 만약 break문이 없다면 계속해서 case를 확인하고 마지막에 default문을 실행하게 됩니다.


goto

그 이름이 의미하듯이 프로그램의 흐름을 원하는 위치로 이동시킬 때 사용하는 키워드입니다. 하지만 goto문에 대해선 부정적으로 봅니다. 이는 프로그램의 자연스러운 흐름을 방해한다는 것 때문입니다. C언어와 같은 절차지향 프로그래밍 언어에서 프로그램의 흐름을 방해하거나 복잡하게 하는 것은 아주 큰 단점이 됩니다. 그리고 goto를 굳이 써야하는 경우가 딱히 존재하지도 않습니다. 


int main(void)

{

....

rabbit;                 // 위치지정에 사용된 rabbit이라는 레이블

...

goto rabbit;    //레이블 rabbit의 위치로 이동

}



'배울 것들 > 언어' 카테고리의 다른 글

C프로그래밍 9/? 배열  (0) 2018.11.20
C프로그래밍 8/? 함수  (0) 2018.11.20
C프로그래밍 6/? 반복문  (0) 2018.11.19
C프로그래밍 5/? 자료형  (0) 2018.11.13
C프로그래밍 4/? 자료형  (0) 2018.11.11

반복문은 말그대로 어떠한 행위를 반복시키는 방법입니다. 이러한 방법은 총 3가지가 있습니다. while, do~while, for문 순으로 알아보겠습니다.


while문

while문은 특정조건을 주고 그 조건을 만족하는 동안, 특정영역을 계속해서 반복하는 구조입니다. 


while(num<3)    //반복의 조건은 num<3

{

printf(""Hello World %d \n", num);

num++;                            // num이 3보다 작아 중괄호에 들어왔으니 1증가 시킨다.

}


이것이 while문입니다. 보듯이 소괄호 안에 반복의 조건을 명시하고, 이 조건이 만족되는 동안 중괄호 안에 존재하는 코드가 반복 실행되는 구조입니다. 만약 num의 초기값이 0이라면 문장은 3번의 반복을 진행하고 while문을 벗어나, 그 다음 행을 실행하게 됩니다. 이 문장에서 가장 중요한 부분은 num++입니다. 이는 반복의 조건을 무너뜨리기 위한 최소한의 연산입니다. 만약 이 문장이 없다면 무한루프가 발생하게 됩니다.

무한루프를 구성하고자 하면 while의 소괄호에 참을 의미하는 숫자 1를 넣어주면 됩니다. 이 경우에 무한루프를 무너뜨리는 요소는 break문입니다.


do~while문

while문과의 차이점은 반복의 조건을 검사하는 시점에 있습니다. do~while문은 반복조건을 while문과 달리 뒷부분에서 검사합니다. 따라서 반복 조건을 만족하지않더라도 최소한 한번은 실행되는 구조입니다.


do

{

printf("Hello world \n");
num++;

} while(num < 3);


이와 같이 사용합니다. 코드를 해석해보면 while문과 크게 다르지않습니다. 그래서 두 방법은 서로 변형이 가능합니다. 허나, while문의 경우 반복의 조건이 앞 부분에 위치해 코드를 작성하고 해석하는데 더 용이하여 while문을 일반적으로 사용합니다.


for문

이전의 반복문과 달리 for문은 반복을 위한 변수의 선언과 반복조건을 거짓으로 만들기 위한 값의 증가 및 감소연산 등을 한데 묶을 수 있도록 만들어진 반복문입니다.

for(초기식; 조건식; 증감식)

{

//반복의 대상이 되는 문장들

}

  • 초기식    반복을 위한 변수의 선언 및 초기화에 사용
  • 조건식    반복의 조건을 검사하는 목적으로 선언
  • 증감식    반복의 조건을 '거짓'으로 만드는 증가 및 감소연산

따라서 앞의 예문을 for문으로 표현하면 아래와 같습니다.


for(int num = 0; num < 3; num++)

printf("Hi~");    //반복되는 문장이 하나이면 중괄호를 생략할 수 있다


세 가지 방법을 확인해본 결과 for문의 가장 편리한 반복문이라 생각할 수 있습니다. 다만, 이는 반복의 횟수가 미리 정해진 경우에만 해당됩니다. 그렇다고 for문에서 무한루프를 만들 수 없는 것은 아닙니다. 조건식을 비워둔다면 무조건 참으로 인식되어 무한루프를 형성할 수 있습니다.


'배울 것들 > 언어' 카테고리의 다른 글

C프로그래밍 8/? 함수  (0) 2018.11.20
C프로그래밍 7/? 조건문  (0) 2018.11.19
C프로그래밍 5/? 자료형  (0) 2018.11.13
C프로그래밍 4/? 자료형  (0) 2018.11.11
C프로그래밍 3/? 데이터  (0) 2018.11.10

+ Recent posts