공부했던 자료 정리하는 용도입니다.

재배포, 수정하지 마세요.

 

 


 포인터 

  1. 포인터
    • 포인터 변수
    • 포인터 변수의 선언
      * 포인터 변수의 크기
    • 포인터 형(type)
    • 포인터 변수와 관련된 연산자
      1. & 연산자
      2. * 연산자
    • null 포인터
    • 포인터 대상의 const 선언
      * 상수를 가리키는 포인터인 경우
      * 포인터 자체가 상수인 경우
      * 포인터가 상수이면서 상수를 가리키는 경우
  2. 포인터와 배열
    • 포인터 연산
    • 문자열의 선언방식
    • 포인터 배열
      * 문자열 배열
  3. 포인터와 함수
    • 함수의 호출 방식
      * Call-by-value
      * Call-by-reference
    • 함수의 인자로 배열 전달
      * 인자로 배열 전달 시 배열의 크기
  4. 이중 포인터(더블 포인터)
    • 이중 포인터 선언과 사용
    • 포인터 변수 대상의 Call-by-reference
    • 포인터 배열과 포인터 배열의 포인터 type
    • 다중 포인터 변수
  5. 다차원 배열과 포인터
    • 2차원 배열의 이름과, 2차원 배열의 첫 번째 행
    • 2차원 배열의 증감 연산
    • 2차원 배열의 포인터
    • 함수 인자로 2차원 배열 전달
    • 다차원 배열에서 arr[i]와 *(arr + i)
    • 배열 포인터와 포인터 배열
    • main 함수의 인자
      * 인자의 형성 과정
      * char * argv[ ]
  6. 함수 포인터
    • 함수 포인터 변수 선언
  7. void 포인터

 


 

포인터

  • 포인터를 이용하면 메모리에 직접 접근이 가능하다. 그래서 C언어가 Low 레벨 언어의 특성을 지닌다고 말한다. 
  • 시스템이 몇 비트인지에 따라 메모리 주소의 범위도 달라진다. ``sizeof(sizeof(포인터), sizeof(자료형 *))``로 포인터의 크기를 구해보면 32 bit에서는 4 byte, 64 bit에서는 8 byte가 나온다.

    32 bit : 16진수 8자리

    0x00000000 ~ 0xFFFFFFFF

    64 bit : 16진수 16자리

    8자리씩 끊어서 백틱을 붙이기도 한다. ex) 0x00000000`00000000
    0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF

 

 

  ■ 리눅스, OS X에서 %p

  리눅스나 OS X에서 서식 지정자 ``%p``를 사용하면 메모리 주소 앞에 0x가 붙고 A~F는 소문자로 출력된다. 또한 높은 자리수의 0은 생략된다.
  ex) 008AF7FC → 0x8af7fc

 

 

포인터 변수

  • 포인터 변수는 메모리의 주소 값을 저장하기 위한 변수이다.
    C언어는 시작 번지만을 가지고 위치를 표현한다. (자료형에 따라 끝 위치가 어딘지 계산이 가능하기 때문)
  • 포인터는 '변수 형태의 포인터'와 '상수 형태의 포인터'를 어우르는 표현이다. (대부분의 경우 포인터 변수를 의미)

 

 

 

포인터 변수 선언

int * pnum1;		// int형 변수를 가리키는 pnum1 선언
unsigned int * pnum2;	// unsigned int형 변수를 가리키는 pnum2 선언

// *의 위치는 상관 X (아래 3문장은 모두 동일한 의미)
double * ptr;
double* ptr;
double *ptr;

(주소 값은 동일한 시스템에서 그 크기가 동일하며 모두 정수의 형태를 띄지만) 가리키는 변수의 자료형에 따라서 선언하는 방법이 달라진다.

 

 

  ■ 포인터 변수의 크기

포인터와 변수의 참조관계

  포인터 변수의 크기는 운영체제마다 다르다. ``32bit`` 시스템에서는 주소 값을 ``32bit``로 표현하기 때문에 포인터 변수의 크기가 ``4byte``인 반면, ``64bit`` 시스템에서는 주소 값을 ``64bit``로 표현하기 때문에 포인터 변수의 크기가 ``8byte``이다.(주소 값의 크기와 포인터 변수의 크기는 같다.) 그러나 ##64bit## 시스템에서 동작하는 컴파일러의 경우 ##64bit##로 컴파일을 하지만 ##32bit##로도 컴파일을 할 수 있다. 따라서 포인터 변수 대상의 ##sizeof## 연산의 결과로 ##8##을 확인하려면 ##64bit## 시스템상에서 ##64bit##로 컴파일해야 한다. 

 

 

 

포인터 형(type)

  포인터 변수의 선언 및 구분에 사용되는 ``int *``, ``char *``, ``double *`` 등을 가리켜 포인터 형(type)이라고 한다. 포인터 변수도 값을 저장하는 변수이기 때문에 포인터 형도 자료형의 범주에 포함시키기도 한다. 포인터형은 메모리 공간을 참조하는 기준이 되기 때문에 포인터 형에 맞지 않는 자료형의 변수를 참조(``int *`` 형 포인터 변수가 ``double``형 변수를 참조한다던지)하면 안 된다(경고 메시지는 출력되지만 컴파일 에러가 발생하지는 X)

 

 

 

 

포인터 변수와 관련된 연산자

  ■ & 연산자

int num = 6;
int * pnum = #	// num의 주소값을 반환해서 포인터 변수 pnum을 초기화

  ``&``연산자는 오른쪽에 등장하는 피연산자의 주소 값을 반환하는 연산자이다. 피연산자는 변수여야 하며, 상수는 피연산자가 될 수 없다. 또한 변수의 자료형에 맞지 않는 포인터 변수의 선언은 문제가 될 수 있다.(컴파일 에러는 발생하지 않지만 경고 메시지는 발생하며 포인터 관련 ##*##연산 시 문제가 발생한다.)

 

 

 

  ■ * 연산자(역참조 연산자)

int num = 10;
int * pnum = #	// 포인터 변수 pnum이 변수 num을 가리키게 하는 문장
*pnum = 20;		// pnum이 가리키는 변수에 20을 저장
printf("%d", *pnum);	// pnum이 가리키는 변수를 출력

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

위의 경우 사실상 ``*pnum``은 포인터 변수 ``pnum``이 가리키는 변수 ``num``을 의미한다.

 

  • 포인터의 자료형과 역참조
char *cPtr;	// char형 포인터
*cPtra = 'a';	// 1 byte(char의 크기)만큼 역참조하여 값을 저장

short *sPtr;	// short형 포인터
*sPtr1 = 100;	// 2 byte(short의 크기)만큼 역참조하여 값을 저장

int *iPtr;	// int형 포인터
*nPtr = 100;	// 4 byte(int의 크기)만큼 역참조하여 값을 저장

long long *lPtr;    // long long형 포인터 선언
*lPtr = 100;	    // 8 byte(long long의 크기)만큼 역참조하여 값을 저장

포인터는 포인터를 선언한 자료형의 크기만큼 역참조 하는 크기가 결정된다.

 

 

null 포인터

// 아래 두 문장은 같은의미이다.
int * ptr1 = 0;
int * ptr2 = NULL:	// NULL 은 0을 의미
  • 값 ``0``을 널 포인터라고 하는데, ``0``번지가 아닌 아무 데도 가리키지 않는다는 의미이다. 
  • 키워드 ``NULL``도 마찬가지로 널 포인터를 의미하며, 실제로 이는 상수 ``0``으로 정의되어 있다.
  • 아무것도 가리키지 않는 상태이기 때문에 역참조는 할 수 없다.

  포인터 변수는 선언만 하고 초기화하지 않으면 쓰레기 값으로 초기화된다. 우선 선언만 해놓고 이후에 주소를 가리키게 하려면 위와 같이 초기화하면 된다. null 포인터를 이용한 ``*`` 연산은 메모리 공간에 어떠한 영향도 미치지 않으며, 프로그램이 멈추는 현상은 동일하게 일어나지만, 잘못된 메모리 접근에 대해 보호장치가 없는 운영체제에서도 시스템에 치명적인 영향을 주지 않는다.

 

 

 

 

포인터 대상의 const 선언

//1. 상수를 가리키는 포인터(pointer to constant)인 경우 : 메모리 주소에 저장된 값을 변경할 수 없다는 의미
const int num = 10;
const int *numPtr;	// int형 상수를 가리키는 포인터. int const *numPtr도 같음

numPtr = &num1;
*numPtr = 20;		// error. num1이 상수이므로 역참조로 값 변경 불가


//2. 포인터 자체가 상수(constant pointer)인 경우: 메모리 주소를 변경할 수 없다는 의미
int num1 = 10;
int num2 = 20;
int * const numPtr = &num1;	// int형 포인터 상수

numPtr = &num2;		// error. 포인터(메모리 주소) 변경 불가


//3. 포인터가 상수이면서 상수를 가리키는 상황(constant pointer to constant) : 메모리 주소에 저장된 값과 메모리 주소 모두 변경할 수 없다는 의미
const int num1 = 10;
const int num2 = 20;
const int * const numPtr = &num1;	// int형 상수를 가리키는 포인터 상수. int const * const numPtr도 같음

*numPtr = 30;		// error. num1이 상수이므로 역참조로 값 변경 불가
numPtr = &num2;		// error. 포인터(메모리 주소) 변경 불가

포인터에도 ``const``를 붙일 수 있는데, 위치에 따라 특성이 달라진다.

  1. 상수를 가리키는 포인터(pointer to constant)인 경우 : 메모리 주소에 저장된 을 변경할 수 없다는 의미
  2. 포인터 자체가 상수인 경우(constant pointer)인 경우 : 메모리 주소를 변경할 수 없다는 의미
  3. 포인터가 상수이면서 상수를 가리키는 상황(constant pointer to constant) : 메모리 주소에 저장된 과 메모리 주소 둘 다 변경할 수 없다는 의미

 

 

  ■ 상수를 가리키는 포인터(pointer to constant)인 경우

int num = 20;
const int * ptr = #
*ptr = 30	// error
num = 40;	// 가능

  ``const``를 맨 앞에 선언하면 '포인터 변수를 이용해 포인터 변수가 가리키는 값의 변경을 허용하지 않겠다'라는 의미이다. (값의 변경 방법 제한) 위의 예시의 경우 ``ptr``을 이용해 ``ptr``이 가리키는 변수의 값을 변경할 수 없다. 그러나 포인터 변수 @@ptr@@이 가리키는 변수가 상수화 되는 것은 아니다. 위의 예시처럼 포인터 변수를 이용하지 않는 ``num = 40;`` 문장은 허용된다.

 

 

  ■ 포인터 자체가 상수(constant pointer)인 경우

int num1 = 20;
int num2 = 30;
int * const ptr = &num1;
ptr = &num2;	// error
*ptr = 40;	// 가능

  포인터 변수 앞에 ``const``가 오면 포인터 변수가 상수가 된다. (한번 주소 값이 저장되면 변경 불가) 포인터 변수가 상수가 되는 것이라 포인터가 가리키는 대상에 저장된 값을 변경하는 것은 가능하다.

 

 

  ■ 포인터가 상수이면서 상수를 가리키는 상황(constant pointer to constant)인 경우

const int * const ptr = #

  앞서 말한 두 가지 형태의 ``const``를 동시에 선언하는 것도 가능하다. 동시에 선언하면 해당 포인터 변수로 포인터 변수가 참조하는 변수의 값도 변경할 수 없고, 포인터 변수를 변경하는 것도 불가능하다. 

 

 

 

'기타 > C' 카테고리의 다른 글

[C]이중 포인터(더블 포인터)  (0) 2020.05.12
[C] 포인터와 배열, 함수  (0) 2020.05.07
[C] 배열 (1차원 배열, 다차원 배열, 가변 길이 배열)  (1) 2020.04.28
[C] 함수  (0) 2020.04.27
[C] 조건문, 반복문  (0) 2020.04.16

+ Recent posts