본문 바로가기

학습공간/C, 프로그래밍기초

[6주차] 1차원 배열, 포인터의 이해

반응형

10장

배열이란 무엇인가?

- 많은 양의 데이터를 처리하는 경우 유용하게 사용할 수 있는 것이 배열이다.

예를 들어서 10층짜리 아파트의 각층별 사람 수를 구하는 프로그램을 짠다고 해보자.

그렇다면 변수를 선언할 때는 이러한 귀찮음이 따른다.

만약에 아파트의 층수가 10층이 아니라 20층, 또는 30층이라면 어떻게 하겠는가?

배열의 사용은 우리들이 저러한 끔찍한 일을 겪지 않도록 편의를 제공해 준다. 배열이라는 문법적 요소는 여러 개의 변수를 동시에 생성할 수 있는 특징을 지니기 때문이다.

 

1차원 배열의 선언 및 초기화

배열은 선언되는 형식에 따라서 1차원 구조로 선언할 수도 있고, 2차원 구조로 선언할 수도 있다. 지금은 1차원 배열의 경우를 살펴보겠다.

배열 요소 자료형(int) : 배열을 구성하는 각각의 요소(Index)는 int 형 변수라는 의미이다.

배열 이름(array) : 배열의 요소에 접근할 때 array라는 이름을 사용하겠다는 의미이다.

배열 길이([10]) : 변수 10개를 모아서 배열을 구성한다는 뜻이다. 10개의 각각 변수를 가리켜 ‘배열 요소’ (ARRAY INDEX) 라 한다. 단, size를 선언할 때는 반드시 상수를 사용.

오른쪽의 소스를 보면 int 형

array 라는 size 10개짜리의

배열을 선언 하고 각각의 배열

요소에 접근을 하고 있다.

여기에서 우리의 일반적인 생각과는

달리 첫 번째 요소가 1부터 시작하지

않고 0부터 시작한다.

즉,

첫 번째 배열 요소의 인덱스는 0이다.

그리고 배열의 메모리 공간은 int 형 4byte X size 10 = 40byte 이다.

int array[10] 선언한 경우 메모리 공간에 할당되는 모양

배열을 선언한 순간 array 라는 이름의 정수형 (4byte) 방이 0번부터 9번까지 만들어 진다.

 

다음은 1차원 배열의 예이다.

첫 번째 arr1라는 배열은 우리가 가장 흔히 쓰는 방법이다.

int arr1[5]; 를 선언함과 동시에 값을 1, 2, 3, 4, 5로 초기화 하였다.

5개의 방을 만들어서 5개의 값을 초기화 시킨 것이다.

배열에서 나는 에러의 대부분은 메모리가 할당되지 않은 곳을 참조해서 일어나는 경우가 많으므로 조심하기 바란다. 첫 번째 경우에서는 0~4번지에 1, 2, 3, 4, 5를 대입한 것이다.

 

두 번째 arr2라는 배열도 마찬가지로 선언과 동시에 초기화 하는 방법 중 하나인데, 배열 길이가 정해져 있지 않다. 이 경우는 컴파일러가 오른쪽 배열 요소를 계산해 알아서 크기를 잡아준다. 이때의 배열 arr2 의 길이는 5이다.

 

세 번째 arr3라는 배열의 경우에는 미리 배열 길이를 5로 잡아준 뒤, 값을 두 개밖에 대입 하지 않았다. 이 경우는 차례대로 초기화 된 후 남은 부분은 자동으로 0으로 초기화 된다.

 

배열을 이용한 문자열 변수의 표현

-여태껏 우리가 접했던 모든 문자열은 상수였다. 문자열도 변수로 존재할 수 있다면 여러모로 유용하지 않을까? 배열을 이용하면 문자열도 변수로 선언하는 것이 가능해진다.

다음은 문자열 변수를 선언하고 출력하는 방법이다.

문자열 배열을 선언하는 방법을 보니 char형을 이용하여 오른쪽에 선언한 문자열을 str1

이라는 배열에 저장하라는 의미이다. 이때 문자열을 담은 이 str1 이나 str2를 문자열을 담은 변수라 하여 문자열 변수라고 말한다. 그리고 이상한 점은 Good 문자는 4개인데 배열 사이즈가 5로 잡혀 있다. 이것은 잠시 후에 설명하겠다.

 

문자열 변수의 특징과 널(NULL) 문자에 대한 이해

 

scanf 함수를 이용해서 문자열 입력받기

위 예문을 보면 scanf 함수를 이용하여 문자열을 입력 받을 수도 있다.

그런데 주의할 점은 scanf 함수에서는 서식문자로 주소를 받기 때문에 &를 붙여야 한다고 알고 있다. 하지만 배열에서는 &를 붙이지 않았다. 이것은 나중에 다시 설명하겠지만 일반 변수의 값에 &를 붙이면 주소를 참조하게 된다. 배열에서는 배열의 이름 자체가 0번지의 주소를 나타내기 때문에 그 자체가 주소여서 &를 붙일 필요가 없는 것이다.

 

그리고 두 번째 문자열을 입력하는데 배열에 모든 문자열이 저장되지 않았다.

그 이유는 앞에서도 말했지만 scanf 함수는 스페이스바, 엔터, 텝 등으로 데이터의 수를 구분하기 때문이다. 그러므로 배열은 1개이지만 데이터가 3개 이므로 앞의 1개만 저장하게 되는 것이다.

 

문제 1] 길이가 9인 int형 정수 배열을 선언하고 나서 사용자로부터 숫자를 하나 입력받는다. 그리고 입력받은 숫자의 구구단 계산 결과를 미리 선언해 놓은 배열에 저장한다. 마지막으로 배열의 요소중에서 짝수 번째 위치에 존재하는 요소들을 출력하는 프로그램을 작성하자.                                               ex) 9 입력 -> 18 36 54 72 출력

 

문제 2] 사용자로부터 하나의 문자열을 입력받아서 입력받은 문자열의 길이를 출력하는 프로그램을 작성하자. 널(null)문자는 문자열의 길이에서 제외시키는 것으로 하자.

                             ex) Hello 는 null문자까지 포함하여 길이가 6이지만 5로 봄

                                 Hello 입력 -> 5 출력

 

 

 

 

11장

포인터란 무엇인가?

- 포인터란 메모리의 주소 값을 저장하기 위한 변수이다.

즉, 포인터란 기본 자료형 변수와 달리 메모리 공간의 주소 값을 저장하는데 사용되는 변수를 말하는 것이다. 그래서 흔히 포인터가 변수라는 것을 강조하기 위해서 ‘포인터변수’라는 표현을 쓰는 것이다.

 

포인터 선언하기

int *a: int형 변수의 주소 값을 지닐 수 있는 int형 포인터

char *b: char형 변수의 주소 값을 지닐 수 있는 char형 포인터

double *c: double형 변수의 주소 값을 지닐 수 있는 double형 포인터

 

주소와 관련된 연산자: &연산자와 *연산자

- 포인터를 선언한은 방법도 이해했으니, 이제는 포인터에 저장할 주소 값을 구하는 방법에 대해서 살펴볼 차례이다.

1) 주소 값을 참조할 때 사용하는 연산자: &연산자

- & 연산자는 어떤 변수의 주소를 반환 시킬 때 쓰는 연산자이다.

2) 포인터가 가리키는 메모리 참조: *연산자

- * 연산자는 어떤 포인터 변수의 값을 참조하기 위해 쓰는 연산자이다.

                                                     (선언에 쓰이는 *는 의미가 다름.)

여기에서 포인터 변수 pA는 a의 주소를 가리킨다 라고 말함.

a 변수를 통해 메모리 접근: 직접 접근  // pA 포인터 변수를 통해 메모리 접근: 간접 접근

위의 소스를 메모리상에서 보면 다음과 같다.

변수 a는 4byte의 메모리를 차지하며 주소는 Ox1000 이고 값은 2005 이다.

포인터 변수 pA는 4byte의 메모리를 차지하며 주소는 Ox1006이고 Ox1000 (a의 주소)를 가리키고 있다.(*을 붙임으로써 가리키는 주소가 갖는 값을 참조할 수 있음. 변경도 가능.)

 

포인터의 타입

- 포인터의 타입은 메모리를 참조하는 방법을 알려주는 역할을 한다.

타입에 따라 메모리를 읽어 들이는 정도가 다르다.

 

잘못된 포인터의 사용

잘못된 포인터의 사용

첫 번째 경우는 초기화를 하지도 않은 채 포인터가 가리키는 주소의 값을 변경하려 하고 있기 때문에 메모리 참조 에러가 발생한 것이다. 포인터가 어디를 가리키고 있는지 모르기 때문이다.

 

두 번째 경우는 초기화를 했지만 주소를 담아야 하는데 100이라는 상수를 담았다. 컴퓨터는 100을 받아들이면서 100이 무슨 주소인지를 모르기 때문에 역시 에러가 발생한다.

 

문제 3] int형 변수a와 b를 선언과 동시에 각각 10, 20으로 초기화시킨다. 그리고 포인터 p1과 p2를 선언한 다음 각각 변수a와 b를 가리키게 한다. 이러한 상태에서 간접 접근 방식에 의해서 값을 하나 감소시킨다. 그 다음 포인터 p1과 p2가 가리키는 대상을 서로 바꿔준다.

 

반응형