10 배열과 포인터

15793 단어 CC

🔗 혼자 공부하는 C언어


1. 배열과 포인터의 관계

  • 배열은 자료형이 같은 변수를 메모리에 연속으로 할당하므로, 첫 번째 요소의 주소를 알면 나머지 요소의 주소도 알 수 있다.
  • 주소도 연산이 가능한데, 그 형식은 다음과 같다.
주소 + 정수 -> 주소 + (정수*주소를 구한 변수의 크기)
  • 만약 크기가 4 바이트인 int형 변수 a의 주소가 100번지라고 하면, 여기에 1을 더한 결과는 101이 아니라 104가 된다.
*(ary + 1) == ary[1]
  • 이러한 연산 규칙은 배열에서 유용하며, 주소 연산한 결과를 간접 참조하는 것은 대괄호([])를 사용하여 첨자로 접근하는 것과 같다.
  • 특별한 경우가 아니면 대괄호를 사용하는 것이 편리하며, &arr[2]와 같은 경우 arr + 2로 써서 연산 과정을 줄일 수 있다.
&arr[2]
== &(*(arr + 2))
== arr + 2

2. 배열명 역할을 하는 포인터

  • 배열명은 주소이므로 포인터에 저장할 수 있다.
  • 포인터에 배열명을 저장하면 포인터 연산을 통해 배열 요소를 사용할 수 있으므로, 포인터를 배열처럼 사용할 수 있다.
int ary[3];
int *pa = ary;  // 포인터에 배열명 저장

pa[0] = 10;  // 포인터를 배열처럼 사용
*(pa + 1) = 20;

3. 배열명과 포인터의 차이

  • sizeof 연산 결과가 다르다.
    • 배열명에 사용하면 배열의 전체 크기, 포인터에 사용하면 포인터의 크기를 구한다.
    • 즉 배열명을 포인터에 저장한 후 포인터로 배열 전체의 크기를 확인할 수는 없다.
  • 변수와 상수의 차이가 있다.
    • 포인터는 그 값을 바꿀 수 있지만, 배열명은 상수이므로 바꿀 수 없다.
    • 즉, 포인터 pa에 1을 더하여 다시 pa에 저장할 수 있지만, 배열 arr에 1을 더하여 다시 arr에 저장할 수는 없다.
// 가능
pa = pa +1;
pa++;

// 불가능
ary = ary +1;
ary++;
  • 포인터로 배열 요소를 출력할 때 다음과 같이 증가 연산자와 간접 참조 연산자를 함께 사용할 수도 있다.
for (i = 0; i < 3; i++)
{
    printf("%d ", *(pa++));
}
  • 그러나 반복문이 끝나면 pa의 값은 배열의 범위를 벗어나므로 이후에 사용하려면 다시 초기화를 해야 한다.
  • 만약 여기서 전위 표현(*(++pa))를 사용하면 두 번째 요소부터 참조하는데, 마지막에는 쓰레기값을 출력하게 되므로 주의해야 한다.

4. 포인터의 뺄셈과 관계 연산

  • 가리키는 자료형이 같은 경우 뺄셈 연산이 가능하다.
포인터 - 포인터 -> 값의 차 / 가리키는 자료형의 크기
  • 관계 연산자로 대소 관계를 확인할 수도 있다.
    • 배열은 메모리에 순서대로 할당되므로, 관계 연산으로 배열 요소의 순서를 확인할 수 있다.

5. 배열의 값을 출력하는 함수

  • 배열의 값을 수시로 출력해야 하는 경우, 매개변수가 포인터인 함수에 배열명을 전달하여 포인터를 배열명처럼 사용할 수 있다.
  • 이러한 방식은 배열의 데이터를 복사하지 않고 배열의 데이터에 접근할 수 있으므로 효율적이다.
  • 그러나 배열의 주소를 통해 해당 위치의 값을 변경할 수 있기 때문에 주의해서 사용해야 한다.
#include <stdio.h>

void print_ary(int* pa);  // 매개변수를 포인터형으로 선언

int main(void)
{
	int ary[5] = { 10, 20, 30, 40, 50 };

	print_ary(ary);   // 배열명을 인자로 전달

	return 0;
}

void print_ary(int* pa){
    for(int i = 0 ; i < 5 ; i++){
        printf("%d ", *pa++);
    }
}
// 실행 결과
10 20 30 40 50 

💡 매개변수를 배열(int arr[])로 선언하고 함수 안에서 값을 변경하면 함수 밖의 배열이 수정되는 이유

  • C 언어에서 함수의 매개변수로 배열을 전달하면 포인터로 취급된다.
  • 즉 함수 안에서 매개변수로 전달된 arr는 포인터처럼 사용 가능하다. (arr++ 이런 것도 가능)
  • sizeof 연산자로 크기를 확인해보면 배열의 크기가 아닌 포인터의 크기와 같다.
  • 이는 컴파일러가 강제로 배열을 포인터처럼 사용하도록 하는 것이므로, 컴파일러의 부담을 덜기 위해 자료형을 포인터형으로 선언하는 것이 좋다.
  • 배열의 데이터 변경이 일어나지 않도록 하려면 새로 배열을 만들어 데이터를 복사한 후에 작업해야 한다.


    🔗 참고
  • C 언어 코딩 도장 - 64.1 배열 매개변수 사용하기
  • 배열을 함수의 매개 변수로 사용할 때 주의할 점

6. 배열 요소의 개수가 다른배열도 출력하는 함수

  • 배열의 요소 개수가 달라지면 출력문의 반복 횟수도 달라지므로, 배열 요소의 개수를 함수에 같이 전달해야 한다.
#include <stdio.h>

void print_ary(int* pa, int size);  // 매개변수로 배열의 크기 전달

int main(void)
{
	int ary1[5] = { 10, 20, 30, 40, 50 };  
	int ary2[7] = { 10, 20, 30, 40, 50, 60, 70 };  

	print_ary(ary1, 5);   
	printf("\n");
	print_ary(ary2, 7);     

	return 0;
}

void print_ary(int *pa, int size){
    for(int i = 0 ; i < size ; i++){
        printf("%d ", *pa++);
    }
}
// 실행 결과
10 20 30 40 50 
10 20 30 40 50 60 70 

7. 배열에 값을 입력하는 함수

  • 배열에 값을 입력할 때는 주소값이 필요하므로, 간접 참조 연산자 없이 포인터명을 그대로 사용한다.
scanf("%lf", pa + i);

좋은 웹페이지 즐겨찾기