Information Security

PART2 C프로그래밍 기초 다지기 038-050 본문

C언어 300제/PART 1, PART 2

PART2 C프로그래밍 기초 다지기 038-050

leeeeye321 2017. 3. 16. 02:30

038 문자열 이해하기

.

.

.

-문자열은 문자들의 모임이다.

-컴퓨터에서 사용하는 문자를 아스키(ASCII) 문자라고 한다. ASCII는 "American Standard Code for Information Interchange"의 약자이다. 숫자 '0' ~ '9', 영문 'A' ~ 'Z', 'a' ~ 'z', 그리고 기타 문자 코드들로 이루어져 있다. 한글을 표현할 때는 아스키코드 128 ~ 255 범위의 값을 2바이트씩 조합하여 사용한다.

 

039 배열 이해하기

-배열은 같은 데이터 형의 변수를 동시에 여러 개 정의할 때 사용한다. 배열의 각 요소들은 메모리에 연속적으로 할당된다. 만약 위의 코드에서 배열을 사용하지 않았다면 변수 10개를 정의해야 하기 때문에 프로그래밍이 복잡해질 것이다. 배열은 같은 속성을 가지는 집단 변수를 정의할 때 사용하면 편리하다.

 

040 메모리 이해하기

-우리가 지금까지 정의하고 사용해왔던 모든 변수들은 그 변수가 저장되는 메모리 번지를 가지고 있고, 모든 프로그램이 실행될 때는 내부적으로 변수의 이름이 아닌 메모리의 번지에 의해 변수가 구분되고 값이 저장된다.

 

5, 6: 운영체제는 i, j라는 정수형 변수가 저장될 4바이트 공간을 확보하고, 그 번지를 i, j라는 변수의 이름 대신 사용한다.

i=0은 메모리 주소 003EFA08~003EFA0B에 0을 넣는 것을 의미한다.

j=0은 메모리 주소 003EF9FC~003EF9FF에 1을 넣는 것을 의미한다.

 

8, 9: printf 함수의 형식 문자열로 '%p'를 사용하면 변수가 실제로 저장되어 있는 메모리 번지를 16진수로 출력한다.

또 변수도 자신의 메모리 번지를 넘겨주기 위하여 번지 지정 연산자(&)를 사용한다. &i는 "i의 주소"라는 의미이다.

 

041 포인터 이해하기   

-C언어는 컴퓨터의 하드웨어적인 부분과 밀접한 관련되어 프로그래밍하기 때문에 저 수준 언어라고 한다. 저 수준 언어의 특징은 메모리를 직접 접근하여 사용한다는 것이다. C언어로 메모리에 직접 접근하기 위하여 사용하는 것이 포인터이다.

-분신[ , Double, Doppelgänger ]은 하나의 몸이 여럿으로 분리되는 것을 말한다.

인간 세상에서는 분신을 만들 수 없지만, C언어에서는 분신을 만들 수 있다. 나 대신 일을 해줄 수 있는 수많은 분신을 만들 수 있다. 그 비법이 바로 포인터이다. 포인터는 분신을 만들기 위해 사용한다고 생각하면 쉽게 이해할 수 있다.

 

7, 8: 포인터 변수 정의 방법: 데이터형 *변수명; 

정수형 포인터 변수 pointer, psaram을 정의한다. 일반 변수와 달리 '*'를 사용하여 정의된 변수는 다른 변수의 분신이 되기 위한 변수이다. 

 

10: 포인터 변수 pointer를 saram_A의 분신으로 지정한다. 분신을 만들기 위해서는 '&'를 사용한다.

11: saram_A의 분신으로 지정하고, 사용하려면 '*'를 붙여야 한다. *pointer= 1은 saram_A= 1과 같게 된다.

거꾸로 saram_A= 1은 *pointer= 1과 같다.

 

14: 포인터 변수 psaram도 saram_A의 분신으로 지정한다. 이렇게 saram_A의 분신은 두 개가 된다.

15: *psaram= 2는 saram_A= 2, *pointer= 2와 같게 된다.

 

18: 포인터 변수 pointer를 saram_B의 분신으로 지정한다. 하나의 변수에 대해서만 분신이 될 수 있기 때문에 pointer는 이제 더 이상 saram_A의 분신이 아닌 saram_B의 분신이 되었다. saram_A의 분신은 psaram만 남게 된다.

19: *pointer= 3은 saram_B= 3과 같다.

 

22: 포인터 변수 psaram도 saram_B의 분신으로 지정한다. 이렇게 saram_B의 분신은 두 개가 된다. saram_A의 분신은 하나도 남지 않게 된다. 

23: *psaram= 4는 saram_B= 4, *pointer= 4와 같게 된다.

 

★문자열 포인터 예

-pstr을 문자열 "Korea"의 분신으로 지정한다. 위의 예제에서 사용했던 '&'를 문자열에서는 사용하지 않는다. 문자열 자체가 이미 분신이기 때문이다.

 

 

★배열 포인터 예

-pstr을 배열 변수 string에 대한 분신으로 지정한다. 여기에서도 '&'를 사용하지 않았다. string이 배열을 대표하는 분신이기 때문이다. 

 

 

※ 번지 지정 연산자(&)

1. 번지 연산자를 사용해야 하는 경우

-정수형, 실수형, 문자형 등

 

2. 번지 연산자를 사용하지 않는 경우

-문자열형, 배열형 등

 

3. 배열의 요소에 따라 사용/ 사용하지 않는 경우

 

042 널(NULL) 문자 이해하기

7: 함수 length 호출 시, 문자열 "abcde"를 문자형 포인터 변수 pstr이 문자열 전체가 아닌 첫 번째 문자의 번지만을 전달받는다.

1000      1001       1002      1003      1004       1005

'a' 

 'b'

'c' 

'd' 

'e' 

'\0' 

pstr+0    pstr+1    pstr+2    pstr+3    pstr+4    pstr+5

-문자열 "abcde"가 메모리에 저장된 모습이다. 더블 쿼테이션(")으로 둘러싸인 모든 문자열의 끝은 항상 널(NULL)로 종료된다.

 

※ 포인터 변수에 대한 증감 연산자(++, --)는 포인터가 가리키는 데이터형의 크기만큼 증감된다. char*은 1, int*는 4, 구조체는 구조체의 크기만큼 증감된다.

 

043 구조체 이해하기

-구조체는 배열과 비슷한 구조를 가진다. 둘의 차이점은 배열은 같은 데이터형의 변수들을 하나로 묶어서 사용하는 기능이고 구조체는 서로 다른 데이터형의 변수들을 하나로 묶어서 사용하는 기능이라는 것이다.

 

3: 구조체를 정의한다. 구조체는 struct 키워드와 구조체 이름(tagSungJuk)으로 시작된다. 구조체를 정의한다고 해서 실제 변수가 정의되는 것은 아니며, 멤버들을 가진 하나의 틀을 만드는 것에 불과하다.  

struct 태그명 {

데이터형 멤버명;

데이터형 멤버명;

};

 

12: 구조체 변수를 선언한다. 구조체 변수를 선언하면 멤버들이 메모리에 할당되어 접근할 수 있게 된다.

struct 태그명 변수명1, 변수명2, ...;

 

14, 15, 16: 구조체의 각각의 변수에게 접근한다. '.'을 사용하여 접근을 하고, 접근한 후에는 일반 변수처럼 사용한다.
구조체 변수명 . 구조체 멤버명

 

044 공용체 이해하기

-공용체는 구조체와 비슷한 구조를 가진다. 둘의 차이점은 공용체는 멤버들이 같은 주소에 할당되어 메모리를 공유해서 사용하고, 구조체는 멤버들이 메모리에 순차적으로 할당된다는 것이다.

-여러 멤버들이 메모리를 공유하기 때문에 한 멤버의 값을 변경하면 다른 멤버들의 값이 함께 변경된다.

 

3: 공용체를 정의한다. 공용체는 union 키워드와 공용체 이름(tagVariant)으로 시작된다. 

union 태그명{

데이터형 멤버명;

데이터형 멤버명;

}; 

 

1000번지    1001    1002    1003

  i 변수

 d 변수

-이 그림은 공용체 변수 i와 d의 실제 메모리 사용 모습이다. 변수 i와 d가 메모리를 공유하여 사용하고 있기 때문에 13번째 줄에서 i를 0으로 초기화했음에도 불구하고, 14번째 줄에서 d에 5.5를 대입하면서 i의 값이 중첩되어 1085276160이 출력된다.

 

045 열거체 이해하기

-열거형이 없었다면 #define Sun 0, #define Mon1....처럼 일일이 모두 선언해야 해서 불편했을 것이다. 열거형은 상수를 나열하는 것과 같은 효과가 있다.

 

3: 열거형 상수를 정의한다. 초기값을 설정하지 않을 경우 Sun의 값은 자동으로 0이 되며, 그다음 상수들은 각각 하나씩 증가되는 값이 설정된다.  

 

-초기값을 설정할 경우, 그 값부터 하나씩 증가되며 각각 상수들에게 설정된다.

 

-모든 상수는 값을 설정할 수 있고, 설정하지 않는 경우에는 이전 상수의 값에 1이 더해진 값이 설정된다.

 

046 데이터형 정의하기

-typedef 문은 새로운 데이터형을 만드는 경우에 사용한다. 

-구조체, 공용체, 열거체 등에서 자주 사용된다.

 

6: int형 변수를 가지고 새로운 데이터형 bool을 선언한다. C 언어에는 bool형이 존재하지 않기 때문에 C++ 언어에서 사용되는 bool형을 typedef 문을 사용하여 선언하고 사용할 수 있다.

 

47 함수와 인수 이해하기

3: print() 함수를 선언한다. 함수를 정의하기 전에 main() 함수에서 함수를 호출했기 때문에 선언을 해주어야 한다. 

 

6: 함수를 호출한다. 문자열 "This is a function!"을 문자열 포인터 변수 string에 전달한다.

 

9: print() 함수를 정의한다.

 

11: 전달받은 문자열의 길이를 저장할 변수를 정의한다.

14: string이 가리키는 번지에 저장된 값이 NULL 인지 비교한다. 문자열은 항상 NULL로 종료된다.

15: string이 가리키는 번지에 저장된 값을 출력한다.

16: string이 가리키는 번지의 값을 1 증가시킨다.

17: 변수 len의 값을 1 증가시킨다. 문자열의 길이를 카운트한다.

19: 문자열의 길이를 반환한다.

 

048 변수의 범위 이해하기

-전역 변수(global variable)는 어떤 함수의 에서 정의되는 변수이다. 전역 변수를 초기화하지 않으면 컴파일러에 의해 0으로 초기화된다.

-지역 변수(local variable)는 함수 에서 정의되는 변수이다. 지역 변수의 범위는 변수가 정의된 함수로 제한된다. 지역 변수는 컴파일러에 의해 자동으로 0으로 초기화되지 않는다. 지역 변수를 정의할 때 초기화하지 않으면 쓰레기 값(garbage)으로 초기화되므로 초기화를 꼭 해주는 것이 좋은 습관이다.

 

5: 전역 변수 x를 정의하고 20으로 초기화한다. 일반적으로 main() 함수 전에 정의한다.

9: 함수 안에서는 지역변수가 우선순위가 높으므로 "x=5"가 출력된다. main() 함수 안에서 지역 변수 x를 정의했기 때문에 전역 변수 x는 사용할 수 없다.

15: 전달받은 x를 출력한다.  

22: 전역 변수 x를 출력한다. 함수 안에서 사용된 지역 변수가 없기 때문에 5번째 줄에서 정의한 전역 변수 x가 사용되는 것이다. 

 

049 #include 문 이해하기

-C 언어를 사용하기 위해서는 구현하고자 하는 기능의 함수가 어느 파일에 선언되어 있는지 알아야 한다.

-위의 코드에서 사용된 getch() 함수는 conio.h 파일에 선언되어 있다. conio.h 파일에 선언되어 있는 getch() 함수의 원형은 int getch(void); 이다. 2번째 줄에서 conio.h 파일을 include 하지 않는다면 getch() 함수의 원형을 별도로 선언해 주어야 한다. 

 

1: printf() 함수가 선언되어 있는 stdio.h(standard input/output) 파일을 include(포함) 한다.

2: getch() 함수가 선언되어 있는 conio.h(console input/output) 파일을 include 한다.

 

9: 키보드로부터 한 문자를 입력받는다.

 

-#include 문은 다음의 두 가지 형태로 사용된다.

1. #include <파일명>: C의 표준 라이브러리를 사용하고자 하는 경우에 사용

2. #include "파일명": 사용자가 만든 헤더 파일을 사용하고자 하는 경우에 사용

 

050 매크로 이해하기

-매크로는 함수 대신 사용하는 것이다. 매크로를 정의할 때는 #define 문을 사용한다.

 

3: 최댓값을 구하는 매크로 MAX를 작성한다. 주어진 a, b는 함수에서 처럼 인수의 역할을 하며,

9번째 줄에서 사용된 MAX(i, j)를 "i>j ? i:j"로 치환해 준다.

4: 최솟값을 구하는 매크로 MIN을 작성한다. 주어진 a, b는 함수에서 처럼 인수의 역할을 하며,

10번째 줄에서 사용된 MIN(i, j)를 "i<j ? i:j"로 치환해 준다.

 

9: 사용된 MAX(i, j)가 매크로이므로 이 문장은 컴파일러에 의해서 "printf("최댓값은 %d 입니다.\n", i>j ? i:j"로 변경된다.

매크로는 함수와 다르다. 매크로를 사용한 모든 곳은 해당 매크로가 선언된 문장으로 컴파일시 자동으로 변경된다.

10: 사용된 MIN(i, j)가 매크로이므로 이 문장은 컴파일러에 의해서 "printf("최솟값은 %d 입니다.\n", i<j ? i:j"로 변경된다.