본문 바로가기
Language & Framework & GIT/C

[씨앤씨뿔] C/ 포인터 - 2

by veganwithbacon 2023. 4. 8.
반응형

  포인터의 활용

  배열과 포인터의 관계

(1)포인터의 연산

연산자 의미
p + N 주소에 포인터가 기리키는 데이터형의 크기xN만큼 더한다
p - N 주소에서 포인터가 가리키는 데이터형의 크기xN만큼 뺀다
p1 - p2 포인터가 가리키는 데이터형의 개수로 주소의 차를 구한다
++p, p++ 주소를 포인터가 가리키는 데이터형 크기만큼 증가시킨다
--p, p++ 주소를 포인터가 가리키는 데이터형의 크기만큼 감소시킨다
p1 = p2 같은 형의 포인터끼리 대입
p1 == p2 주소가 같은지 비교한다
p1 != p2 주소가 다른지 비교한다
p[N] 포인터를 배열 이름인 것처럼 사용해 N번째 원소에 접근한다
*p p가 가리키는 변수에 접근한다
&p p의 주소를 구한다
p->m p가 가리키는 구조체의 멤버 m에 접근한다

 

p+N 연산의 결과는 p가 가리키는 데이터형 N개 크기만큼 더한 주소이다.

마찬가지로 p - N은 p가 가리키는 데이터형 N개 크기만큼 뺀 주소이다.

즉 p에 대한 N의 크기만큼이 조정되는 것이다.

 

배열과 포인터의 관계를 처음 배울 때, 제일 인상 깊었던 말이 있다.

배열의 이름과 포인터 변수와의 관계, 서로 상대방인척하는 관계라는 것이다.

 

'포인터 + 정수' 연산은 포인터가 가리키는 주소에 마치 배열이 있는 것처럼 메모리에 접근한다.

예로 int 배열 arr의 0번 원소인 arr[0]은 int형 변수이므로 arr[0]의 주소를 포인터 p에 저장가능하다.

 

포인터의 증감 연산도 포인터가 배열의 0번 원소를 가리킬 때 의미있게 사용된다.

p+1과 비교하면, p+1은 p의 값을 변경하지 않으나 p++이나 ++p는 p의 값을 직접 변경한다.

 

++ 연산자가 역참조 연산자보다 우선순위가 높으므로 *p++은 *(p++)의 의미가 된다.

이처럼 p를 증가시키려고 하면 *p++로 사용해도 되지만, 포인터가 아닌 포인터가 역참조하는 변수를 증가시키려면 (*p)++로 써주면 된다.


(2) 배열처럼 사용되는 포인터

포인터는 변수 또는 배열의 원소를 가리킬 수도 있다.

type형의 포인터는 항상 type형의 변수 또는 type형 배열의 원소를 가리킬 수 있다.

배열 원소를 가리키는 포인터다.

 

배열의 시작 주소에는 항상 0이 있기 때문에 arr과 &arr[0]은 항상 같은 값이다.

 

배열 원소를 가리키는 포인터는 배열 이름처럼 사용가능하다.

p[i]와 *(p+i)가 같은 의미로 p[i]를 사용하는 것이 좋다.

*(p+i)보다 p[i]를 사용하는 것이 알아보기 쉽다.

 

포인터는 배열의 어떤 원소든지 가리킬 수 있다.

해당 배열의 0번으로 접근한다.


(3) 포인터처럼 사용되는 배열

배열의 이름은 배열의 시작 주소를 의미한다.

배열 명을 포인터인 것처럼 사용할 수 있다. arr[i]대신 *(arr+i)라고 써도 된다.

C컴파일러는 항상 arr[i]와 *(arr+i)를 동일하게 처리한다.

 

배열 명을 포인터처럼 사용하는 경우

더보기

arr[i] == *(arr + i)

 

//arr은 배열명, *(arr+i)는 int형 변수

 

&arr[i] == arr + i

 

//arr+i는 주소를 의미한다.

 

arr -> p[i]

*p -> *arr

=> *(p+i) => *(arr+i)


(4) 배열과 포인터의 비교

배열과 포인터는 사용법은 유사하나 배열과 포인터는 본질적으로 확연히 다르다.

배열 이름은 포인터처럼 사용 가능하나, 의미로 보면 특정 변수 전용 포인터에 해당한다.

 

배열은 메모리에 할당되고 나면 배열의 시작 주소를 변경할 수 없다.

배열 이름으로 대입 연산이나 증감 연산을 하면 컴파일 에러가 발생한다.

 

포인터는 값을 변경할 수 있으므로 포인터에 보관된 주소는 변경할 수 있다.

 

간단하게 이해하면, 배열은 집주소가 픽스인 반면 포인터는 주소를 원하는 대로 변경 가능하다.


  여러 가지 포인터의 선언

(1)포인터 배열

: 주소를 저장하는 배열

 

선언 시, 데이터형, 포인터 수식어(*), 배열 이름을 순서대로 쓰고 []안에 배열의 크기를 지정

예로 int *arr[5] = {NULL}; // int*형의 변수 5개를 저장하는 배열

 

포인터 배열의 각 원소를 int변수의 주소로 초기화가 가능하다.

arr 배열이 각 a,b,c,d,e의 주소로 초기화하려면 arr[i]가 가리키는 변수에 *arr[i]를 통해 접근한다.

int a,b,c,d,e;
int *arr[5] = { &a, &b, &c, &d, &e};

for(i=0;i<5;i++)
	arr[i]=i;

 

  해당 배열 원소에 대한 포인터

int *p;

p +1 ; //배열 원소 크기(int)

 

  배열 전체에 대한 포인터

int (*p)[5];

p + 1; //배열 원소 크기 (int[5])

 


(2) 배열에 대한 포인터

배열에 대한 포인터= 배열 전체를 가리키는 포인터

ex)  int (*p)[5] = NULL;

위처럼 포인터를 초기화해두면 p는 크기가 5인 int 배열의 주소를 저장하는 포인터가 되는 것이다.

 

배열 원소에 대한 포인터

int *p;

p + 1

  배열 원소 크기(int )

 

배열 전체에 대한 포인터

int (*p)[5];

p + 1;

  배열 전체 크기(int [5])

 

#include<stdio.h>

int main(void)
{
	int data[3][5] = {
    	{1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 13, 14, 15}
    };
    int (*p)[5] = &data[0];
    int i, j;
    
    for(i=0;i<3;i++)
    {
       for(j=0;j<5;j++)
       	  printf("%2d ", p[i][j]);
       printf("\n");
    }
    
    return 0;
    
}

(3) 이중 포인터

이중 포인터(double pointer)는 포인터의 주소를 저장하는 포인터이다.

 

포인터를 선언할 때는 포인터형을 쓰고, 포인터 수식어(*), 포인터 변수명을 써준다.

int *가 포인터형, pp가 포인터 변수명이 된다.

 

 

int **pp = &p;  (int형 변수의 주소를 저장하는 포인터)
pp는 포인터 변수, pp가 가리키는 변수의 데이터형 int

int* 주소  ==>  int 주소 ==> 10

pp(이중포인터)    p                      x


함수의 포인터

                                 인자

함수를 호출하는 쪽 <------> 함수의 정의

                                 리턴값

 

값에 의한 전달(passing by value) 

: 함수 호출 시 넘겨주는 인자를 매개변수로 복사해 전달하는 방식

 

인자의 값이 매개변수로 '복사' 되므로 이런 인자 전달 방법을 복사에 의한 전달(passing by copy)라고 부르기도 한다.

#include<stdio.h>
void swap(int x,int y)
{
	int temp=x;
    x=y;
    y=temp;
}

int main(Void)
{
	int a = 3, b = 7;
    printf("a = %d, b =%d\n", a, b);
    swap(a,b);
    printf("a = %d, b = %d\n", a, b);
 }

위 예제에서는 swap함수는 값에 의한 전달로는 구현할 수 없는데, 이런 경우에 포인터에 의한 전달이 필요한 것이다.

 

  포인터에 의한 전달

포인터에 의한 전달(passing by pointer)은 변수의 값을 전달하는 대신 변수의 주소를 전달하는 방식이다.

 

void swap(int *x, int *y); //매개변수는 포인터형으로 선언한다

함수를 호출할 떄는 인자를 직접 전달하는 대신 인자의 주소를 전달한다

 

swap(&a,&b); //포인터에 의한 전달

 

ex)

void swap(int *x, int *y0
{
  int temp = *x;
  *x = *y;
  *y = temp;
 }
 
 int main(void)
 { 
    int a = 3, b = 7;
    printf(" a = %d, b = %d\n", a, b");
    
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b");
}

포인터에 의한 전달 방법으로 인자를 전달하면 함수 호출 시 넘겨준 주소가 매개변수인 포인터에 저장된다.

 

  함수의 처리 결과를 매개변수로 전달하는 과정

1.  입력 매개변수(in parameter) : 값으로 전달한다

2.  출력 매개변수(out parameter) : 포인터로 전달한다.

3. 입출력 매개변수(in-out parameter) : 입출력 모두에 사용되므로 함수 안에서 그 값이 사용도 되고 변경된다. 입출력 매개변수도 값을 변경해야 하므로 포인터로 전달한다.

 

c에서는 배열을 함수의 인자로 전달할 때 항상 포인터로 전달한다.

배열을 입력 매개변수로 사용하려면 const 포인터형의 매개변수를 선언한다.

const int arr[];는 const int *arr;와 같은 의미이다.

 

 

 

반응형

'Language & Framework & GIT > C' 카테고리의 다른 글

[씨앤씨뿔] C/ 문자열 -1  (0) 2023.04.11
[씨앤씨뿔] C/ 포인터 - 1  (0) 2023.04.06
[씨앤씨뿔] C/ 배열  (0) 2023.04.04
[씨앤씨뿔] C/ 함수  (0) 2023.04.04
[씨앤씨뿔] C/ 제어문  (0) 2023.03.28

댓글