• 검색 결과가 없습니다.

컴퓨터프로그래밍 2014 2학기 - [이론] 09_ch14_포인터

N/A
N/A
Protected

Academic year: 2021

Share "컴퓨터프로그래밍 2014 2학기 - [이론] 09_ch14_포인터"

Copied!
65
0
0

로드 중.... (전체 텍스트 보기)

전체 글

(1)
(2)

데이터와 메모리 주소

메모리 주소를 저장하는 포인터 변수

포인터와 배열

포인터에 대한 연산

포인터와 문자열

함수와 포인터

문자열처리와 관련된 라이브러리 함수

키워드 const

(3)

포인터(pointer)는 에스파냐 원산의 꿩 사냥개 이 사냥개는 후각이 예민하여 사냥감을 발견하면 오른쪽 앞발을 쳐들어 올리고 주시(注視)하는 특성이 있다고 합니다. 이러한 현상을 포인트(point)라고 하며, 품종의 이름은 이러한 특성으로 유래되었다고 한다. 이 개의 특징은 사냥감을 직접 잡는 것이 아니라 포수에게 사냥감의 정확한 위치만을 알려줌으로써 사냥을 쉽게 할 수 있도록 도와주는 역할을 한다.

(4)

레이저 포인터(laser pointer)

레이저 포인터라는 장비의 역할은 설명하려는 부분을 가리키는 것이다. 

C 언어에서의 포인터라는 개념도 이와 동일하다.

C 언어에서의 포인터 역시 무엇인가를 가리키는데 그 대상은

(5)

메모리(memory) : 데이터를 보관하는 장치(주 기억장치, 메인 메모리) 데이터를 효율적으로 보관하기 위해 저장 위치를 나타내는 일련번호가 붙는데 이를 메모리 주소(address)라 한다. 

이 주소는 호텔의 객실을 관리하기 위해 붙인 객실 번호 또는 아파트 단지 내에서 동과 호수로 표현하는 주소와 같은 개념이다.

(6)

포인터(pointer)는 메모리상의 어떤 주소를 가리키고, 메모리상의 주소에는 데이터가 저장되므로 포인터는 메모리에 저장된 데이터의 위치를 가리킨다. 포인터의 예 엘리베이터의 층별 버튼을 누르면 그 버튼이 가리키는 해당 층으로 이동 휴대전화의 단축번호 편리하고 빠르기 때문에 이러한 방법을 사용하는 것이다. 

(7)

변수에 저장된 데이터는 메모리상의 어디엔가 위치하는데 메모리는

기본적으로 1 byte 단위로 구분되어 있으므로 1 byte 마다 구별되는 주소가 있다. 

그런데 변수는 그 곳에 저장할 데이터의 형에 따라 그 크기가 달라진다. 

변수의 데이터 형이 char 형이라면 그 변수는 1 byte 크기의 방 1개를 차지하고,  double 형의 경우는 8 byte 크기이므로 1 byte 크기의 방 8개를 차지하고 있는 것이다. 

따라서 포인터가 가리키게 될 데이터가 저장된 위치는 데이터 형의 크기와 상관없이 해당 데이터가 저장되어 있는 첫 번째 byte의 주소를 가리킨다.

(8)

실행결과

printf에서 변수 앞에 붙은 & 연산자는 해당 변수의 주소를 알려주는 주소 연산자이며, 변수의 주소를 정수형으로 출력할 때는 %d 또는 %u(부호 없는 정수형)를, 16진수로 출력할 때는 %p를 사용 char 형 변수 : 1 byte int 형 변수 : 4 byte

(9)

[예제 14-1]의 line 05를 double k=8;로 수정하여 결과를 확인하고, 앞의 [그림](변수에 대한 메모리상의 위치)과 같이 나타내시오.

실습문제 정답

(10)

포인터는 주소를 가리키며, 포인터 변수는 주소를 저장하는 변수

포인터 변수에는 오직 주소만을 저장할 수 있다. 포인터 변수도 값을 기억하는 변수이므로 사용하기 전에 선언해야 하며, 변수와 같이 저장된 값을 다른

주소로 변경시킬 수 있다. 

(11)

포인터 변수 p의 선언

변수 k의 주소를 포인터 변수 k에 저장

실행결과

포인터 변수 p가 가리키는 곳에 저장된 데이터

(12)

p=&k; 의 결과

p=&i; 의 결과

(13)

p=&k; 의 결과

*p=i; 의 결과

(14)
(15)

포인터 변수는 실제 메모리 주소 값만을 저장하는데 왜 데이터 형이 필요할까? 포인터 변수의 실제 크기는 어떻게 될까?

포인터 변수의 데이터 형은 포인터 변수가 가리키게 될 주소에 저장된 데이터의 형을 말한다. 

Visual C++와 같은 32 bit 컴파일러에서 메모리 주소는 4 byte로 표현하기 때문에 포인터 변수의 크기도 4 byte가 된다. 

따라서 포인터 변수를 다음과 같이 여러 데이터 형으로 선언했다 하더라도 포인터 변수 자체의 크기는 4 byte의 크기를 갖는다.

(16)

배열 요소들은 메모리 상에 연속된 기억 공간에 저장된다.  배열의 이름은 그 배열의 첫 번째 요소가 저장되어 있는 주소를 가리키므로 주소를 가리킨다는 점에서는 배열과 포인터는 유사하다.  그러나 포인터 변수는 다른 주소를 저장할 수 있는 반면에 배열의 이름은 첫 번째 요소가 저장되어 있는 주소를 가리키는 상수이다.  변수는 대입 연산자에 오른쪽과 왼쪽 모두 사용할 수 있지만, 상수는 값을 저장하는 공간이 아니므로 대입 연산자의 왼쪽에 사용할 수 없다.  배열과 포인터는 공통점도 있고, 차이점도 있으나 배열을 사용하는 프로그램에서는 포인터 변수와 함께 사용되는 경우가 많다. 

(17)
(18)

실행결과

포인터 변수에는 주소가 저장되고 주소는 4 byte(Visual C++)로 표현된 숫자이므로 포인터 변수나 주소에 대한 연산이 가능. 

(19)

*(pt+2)과 *pt+2의 차이 *(pt+2)는 (pt+2)가 가리키는 곳의 값 a[2]를 의미.  *pt+2 에서 pt에는 배열의 시작 주소가 저장되어 있으므로 *pt+2의 의미는 a[0]+2와 같다.

요약

(20)

포인터 변수에 대한 증가 또는 감소 연산자(++, ‐‐)의 사용은 포인터 변수에 저장된 주소를 변경시키게 되므로 포인터가 가리키는 대상이 달라진다.

pt=a; 의 결과

pt++; 의 결과

(21)

char *pt;라 함은 포인터 변수 pt의 크기를 1 byte 크기로 하라는 것이 아니라,  포인터 pt가 가리키게 될 주소의 데이터 형이 char 형이 됨을 의미한다.  따라서 char 형으로 선언된 변수의 주소를 포인터 하려면 char 형의 포인터 변수를 사용해야 하고, double 형으로 선언된 변수의 주소를 포인터 하려면 double 형의 포인터 변수가 필요하다.

컴파일 오류

(22)

포인터 변수는 데이터 형에 관계 없이 4 byte의 크기를 갖는다. 앞에서 오류가 발생되는 이유를 쉽게 설명하면 char 형 포인터와 int 형 포인터가 각각 가리키게 될 메모리의 크기가 서로 다르기 때문이다.  즉, char 형 포인터는 포인터가 가리키게 될 주소가 1 byte의 크기이지만 int 형 포인터는 포인터가 가리키는 주소가 4 byte의 크기를 갖는 첫 번째 주소가 되기 때문이다.  따라서 포인터 변수에 대입할 변수 역시 같은 데이터 형을 사용해야 한다

(23)

한 개의 문자열은 1차원 문자형 배열에 저장하고,  두 개 이상의 문자열은 2차원 문자형 배열에 저장하여 사용 포인터 변수에 문자열 초기화 방법 포인터 변수에 저장할 경우에는 크기를 지정할 필요가 없다.  포인터 변수는 문자열 자체를 보관하는 것이 아니라 그 문자열이 저장된 첫 번째 주소를 보관하기 때문이다.

(24)
(25)

포인터 배열이란 여러 개의 포인터 변수를 배열로 사용하는 것

포인터 배열은 문자형 포인터 배열을 사용할 때 유용하게 사용된다.

문자형 2차원 배열과 포인터 배열의 차이

(26)

실행결과

2차원 문자형 배열 na와 포인터 배열 ct의 첨자에 대한 사용방법은 동일하다. 따라서 line 10과 12의 na[2]와 ct[2]는 각각 "Germany"가 저장된 첫 번째 주소에서부터 '\0'(NULL)이 나타날 때까지 출력

(27)

이배열 교수는 정확한 인원수가 결정되지 않았지만 여행을 가기 전에 숙소를 미리 예약을 해야 한다며 최대 인원인 40명에 대해 메모리 호텔의 객실 41개를 다음과 같이 예약.  "9월 20일부터 23일까지 인솔자를 포함하여 객실 41개를 예약하는데, 41개의 방은 서로 떨어지지 않게 인접한 방으로 잡아주시고, 인솔자의 방을 첫 번째 방으로 해 주십시오.“ 김포인터 교수는 정확한 인원수가 결정되지 않아 자신이 묵을 객실 한 개만을 미리 예약하였습니다. 출발 당일 여행을 갈 학생들과 같이 메모리 호텔에 가서 인원수만큼 연이어서 객실을 달라고 하였고, 김포인터 교수는 학생들이 이용할 객실의 첫 번째 방 번호만 확인.  호텔의 규정상 예약을 먼저 할 경우, 취소와 객실 변경이 불가능하며 예약한 일정과 객실 수에 대해 숙박비를 받는다고 한다. 누구의 방법이 더 효율적인가?

(28)
(29)

값에 의한 호출(call by value)과 참조에 의한 호출(call by reference)  값에 의한 호출은 인자를 전달할 때 항상 그 값(value)만을 전달한다. C 나 PASCAL 언어에서 함수를 호출할 때, 일반적으로 사용하는 방법 참조에 의한 호출은 함수에 전달된 인수의 값이 함수 안에서 변경된 경우, 함수를 호출한 쪽에도 영향을 미치는 것을 의미한다. 이러한 방법은 FORTRAN 언어에서 주로 사용하는 함수 호출 방법. 앞에서 함수 swap2에서 i와 j의 변경된 값이 함수를 호출한 main에도 영향을 주었으므로 참조에 의한 호출로 볼 수도 있겠지만, 함수의 인자로 포인터(주소)를 이용함으로써 변수의 값이 변경(side  effect)된 것이므로 엄밀한 의미에서 참조에 의한 호출과는 다르다고 할 수 있다.  결국 C 언어는 참조에 의한 호출을 하기 위해서는 포인터(주소)를 함수의 인자로 사용하는 간접적인 방법에 의해 처리할 수 있다.

(30)

포인터는 어떤 변수의 주소를 가리키지만 함수의 주소에 대해서도 포인트 할 수 있다. C 언어에서 문자열 상수, 배열의 이름 그리고 함수의 이름은 모두 포인터 상수이며, 함수 포인터를 사용하여 함수를 인자로 전달할 수 있다.  함수 포인터는 다음과 같은 방법으로 선언한다. 포인터 pt는 함수의 시작 주소를 저장할 수 있는 함수 포인터로서, 사용자 정의 함수가 int plus(int x, int y);라고 정의되어 있다면 함수 plus()의 시작 주소를 함수 포인터에 저장할 수 있으며, 

(31)

pt=plus;는 함수 plus의 시작 주소를 함수 포인터에 저장하며,  pt(a, b)는 마치 plus(a, b)를 호출한 것과 같은 결과 함수 포인터는 프로그램에서 자주 사용하게 될 몇 개의 함수들을 배열에 저장하여 사용하거나 하나의 함수 포인터에 대해 조건에 따라 여러 함수들 중에서 하나의 함수를 포인터로 이용하고자 할 때 사용한다.

실행 결과

(32)

함수의 결과값으로 배열을 반환하려면 포인터를 이용한다. 앞에서 간단하게 배열의 이름은 포인터 상수라고만 설명하였으나 배열과 포인터는 컴퓨터 메모리의 주소를 나타낸다는 점에서 서로 유사하다. 배열과 포인터 그리고 메모리 주소라는 것에 대해서는 다음 절에서 자세히 설명하기로 하고, 여기에서는 단지 배열을 함수의 결과값으로 반환할 수 있다는 것에 대해서만 설명함.

(33)

지금까지 사용한 모든 변수나 배열 등은 사용된 데이터 형에 의해 컴파일 과정 에서 기억공간에 대한 크기가 결정 되는데 이를 정적 할당(static allocation) 동적 할당(dynamic allocation)은 컴파일 과정이 아니라 프로그램의 실행 중에 기억공간을 확보 n개의 크기를 갖는 배열을 동적으로 할당하기 위해 함수 malloc을 사용

실행 결과

(34)

함수를 선언할 때 함수의 앞부분에는 그 함수의 결과 값이 어떤 데이터 형인가를 명시해 주어야 하는데 함수의 결과 값으로 포인터를 사용하는 경우도 있다 위의 함수들의 경우에 함수의 결과 값으로 포인터를 반환하므로 함수들이 정상적으로 처리될 경우 에는 특정 주소를 반환하고, 오류가 발생되면 대부분 NULL을 결과 값을 반환한다.  이렇게 함으로써 함수를 호출한 곳에서 결과를 알 수 있도록 한다.

(35)

반복문 while에 의해서 *src 즉, 복사할 내용의 문자열을 차례로 읽어 가면서 *p에 저장해 나가되 NULL(거짓)이 될 때 까지 계속 순환. .  문자의 복사가 끝나면 제일 마지막에 NULL을 추가하고 문자열의 포인터를 반환 함수 strcpy는 두 개의 인자를 필요로 하는데 두 번째 인자로 키워드 const를 사용한다. 키워드 const가 사용된 변수는 그 값을 상수로 간주하므로 프로그램에서 값을 변경할 경우 오류가 발생된다.  따라서 함수 strcpy를 호출할 때 함수로 하여금 인자의 값을 변경할 수 없도록 하는 것이다.  함수 strcpy를 호출한 결과, 문자형 배열 str1에는 문자열 상수인 "compile"이 저장된다.

strcpy의 함수 정의

(36)

실행 결과

포인터 변수 pt에는 str의 주소가 저장되므로 동일한 주소를 가리키며 출력된 문자열도 동일하게 나타난다. 

(37)

실행 결과

char 형 포인터 변수에 문자열을 입력 받는 프로그램. 이 예제에서 포인터 변수 str의 크기는 20이므로 최대 19자까지 입력 받을 수 있다. 동적 할당(dynamic allocation)은 컴파일 과정이 아니라 프로그램의 실행 중에 기억공간을 확보. 20개의 크기를 갖는 공간을 동적으로 할당하기 위해 함수 malloc을 사용

(38)

함수 itoa는 inter to ascii, 함수 ltoa는 long to ascii의 의미로서

정수형 숫자를 2진수, 8진수, 10진수 또는 16진수의 문자열로 변환한다. 이 함수들은 헤더 파일 <stdlib.h>를 필요로 한다. 

(39)
(40)
(41)

문자열을 변환하는 함수들의 이름 대부분은 str로 시작하며 이는 string의 약어로서 문자열을 의미한다.  다음의 함수들은 문자열을 숫자로 변환하는 함수이나 특정 진법으로 표현된 문자열을 정수로 변환한다. 즉, 앞에서의 itoa와 반대의 기능을 하는 함수 이 함수들은 헤더 파일 <stdlib.h>을 필요로 한다. 문자열을 숫자로 변환하는 함수

(42)
(43)

다음의 함수들은 두 개의 문자열을 연결하는 함수이며 헤더파일 <string.h>을 필요로 한다. 

(44)
(45)
(46)

실행 결과

문자열 s1과 s2는 첫 12개

문자열("Republic of ")은 같지만 이후의 문자열에서 차이가

(47)

다음은 문자열을 복사하거나 문자열의 길이를 계산하는 함수 이 함수들은 헤더파일 <string.h>를 필요로 한다.

(48)
(49)
(50)

키워드 const는 constant의 줄임말로 상수를 의미한다.

상수로 정의된 변수는 그 값을 프로그램 상에서 변경할 수 없고, 만약 프로그램 상에서 변수로 사용한다면 컴파일 오류가 발생한다. 

#define에 의해서도 매크로 상수를 정의할 수 있는데 #define과 const의 차이는 다음과 같다. 또 다른 차이는 #define은 전처리기 중의 하나로서 이 부분은 컴파일 하기 이전에 전처리기에 의해 처리되며, const에 의한 상수는 컴파일러에 의해 변수로써 만들어지지만 컴파일러가 그 변수를 상수로만 처리할 수 있도록 한다.  const는 일반 변수를 상수로 정의하는데 사용되며, 배열이나 포인터에 대해 그 값을 또는 주소가 가리키는 곳의 값을 사용할 수는 있지만 변경할 수는 없도록 하는데 사용한다. 

(51)

배열 str1은 상수가 아닌 일반 배열이므로 line 07과 같이 다른 값('a')을 대입할 수 있지만, 배열 str2는 상수이므로 line 10과 같이 대입 연산자를 이용하여 값을 변경할 수 없다. 그러나 포인터 변수 ptr은 상수로 정의되어 있지만 line 08과 11과 같이 하나의 변수로서 주소를 대입할 수는 있지만 line 09와 12 같이 포인터가 가리키는 곳의 값은 변경할 수 없다.

(52)

포인터란?

포인터는 메모리상의 어떤 주소를 가리키고, 메모리상의 주소에는 데이터가 저장되므로 결국 포인터는 메모리에 저장된 데이터의 위치를 가리킨다.

포인터 변수는?

주소를 저장하는 변수이므로 데이터 형이 있으며 사용하기 전에 선언해야 한다. 일반 변수와는 달리 포인터 변수에는 오직 주소만을 저장할 수 있다. 

(53)

포인터와 관련된 연산자

(54)

포인터 변수의 크기와 데이터 형

포인터 변수의 데이터 형은 포인터 변수가 가리키게 될 주소에 저장된 데이터의 형을 말한다.  메모리 주소는 4 byte의 크기로 나태내므로

포인터 변수를 다음과 같이 여러 데이터 형으로 선언했다 하더라도 포인터 변수 자체의 크기는 4 byte의 크기를 가진다. 

(55)

포인터와 배열

배열의 이름은 그 배열의 첫 번째 요소가 저장되어 있는 주소를 가리키므로 주소를 가리킨다는 점에서는 배열과 포인터는 유사하다. 

그러나 포인터 변수는 다른 주소를 저장할 수 있는 반면에 배열의 이름은 첫 번째 요소가 저장되어 있는 주소를 가리키는 상수다.

(56)

포인터에 대한 연산

포인터 변수에는 주소가 저장되고 주소는 4 byte로 표현된 숫자이므로 포인터 변수나 주소에 대한 연산이 가능하다. 그러나 주소는 양의 정수로 표현하므로 실수 연산은 사용할 수 없다.

(57)

포인터와 변수의 데이터형이 일치해야 하는 이유는?

char 형은 메모리 상에 1 byte 크기의 공간을 갖는데 char *pt;라 함은 포인터 변수 pt의 크기를 1 byte 크기로 하라는 것이 아니라, 포인터 pt가 가리키게 될 주소의 데이터 형이 char 형이 됨을 의미한다. 

따라서 char 형으로 선언된 변수의 주소를 포인터 하려면 char 형의 포인터 변수를 사용해야 하고, double 형으로 선언된 변수의 주소를 포인터 하려면 double 형의 포인터 변수가 필요하다.

(58)

포인터와 문자열

C 언어에는 문자를 저장하는 char 형은 있지만 문자열을 저장하는 데이터 형은 따로 없으므로 다음과 같이 배열 또는 포인터를 이용하여 문자열을 초기화 할 수 있다.  [방법 1]의 배열을 이용하는 방법은 배열의 초기화에서만 사용이 가능하고,  프로그램 내에서는 배열 이름인 str1이 문자열 "copy"의 시작 주소를 가리키는 포인터 상수이므로 str1="test";와 같이 다른 문자열을 대입할 수 없다.  반면 [방법 2]의 포인터 변수 str2는 변수이므로 str2="test";와 같이 사용할 수 있고, 다른 주소를 할당할 수 있으며 연산도 가능하다. 

(59)

포인터 배열이란?

여러 개의 포인터 변수를 배열로 사용하는 것이다. 포인터 배열은 문자형 포인터 배열을 사용할 때 유용하게 사용한다.

(60)

함수의 인자로 포인터를 사용하는 방법

함수를 호출할 때 인자의 전달 방식

(61)

함수 포인터란?

C 언어에서 함수의 이름은 그 함수가 시작되는 주소를 나타내는 포인터 상수이므로 함수 포인터를 사용하여 함수를 인자로 전달할 수 있다.

(62)

함수의 결과로 배열을 반환하는 방법

함수의 결과값으로 배열을 반환하려면 포인터를 이용한다.  결과값으로 반환하는 배열은 컴파일과정에서 그 값이 정해진 것이 아니라 실행 중에 함수 호출에 의해서 새롭게 만들어지는 것이므로 n개의 크기를 갖는 배열을 동적으로 할당하기 위해 함수 malloc을 사용한다.

(63)

함수의 결과 값으로 포인터를 반환하는 함수

위의 함수들의 경우에 함수의 결과 값으로 포인터를 반환하므로 함수들이 정상적으로 처리될 경우 에는 특정 주소를 반환하고, 오류가 발생되면 대부분 NULL을 결과 값을 반환한다. 

(64)
(65)

참조

관련 문서

 CPU 내에 데이터가 담겨 있는 메모리 주소를 임시 저장하는 장소.  CPU 내에 데이터가 담겨 있는 메모리 주소를

- 함수의 인자로 배열이 전달되면 배열의 기본 주소가 (배열의 내용이 아님) call-by-value로 전달됨...

 메모리는 바이트 단위로 주소가 있고, 주소를 사용하여 데 이터를 메모리에 쓰거나 읽을 수 있다..  알고리즘은 문제를 해결하는 절차나 방법을

두 가지 표현을 비교해 보면 퍼센트로 표현 한 첫 번째 방법은 실업률이 상당히 증가했다는 인상을 주는 반면, 퍼센트 포인트로 표현한 두 번째 방법은 그 렇지 않은 인상을

 IP 주소를 할당하는 특정 서버가 전송하는 정보 그대로 컴퓨터에서 자동으로 설정하는 방식..  DHCP(Dynamic Host

▪ 문법과 배열의 결속, 문장의 체계적인 배열, 문장의 긴밀한 결속 등 을 글을 마무리 짓는 순간까지 계속해서 점검해야 함..

 컴퓨터공학과의 김이화 학생과 환경공학과의 홍이화 학생의 정보를 저장 하고자 한다... (예제2) 학과 주소를

첫 번째 worker 함수 호출 10번 결과를 출력. 두 번째 worker 함수 호출