[C코드 최적화] 포인터에 대한 오해와 진실

2 minute read

개요

해당 내용은 ‘임베디드 프로그래밍 C코드 최적화’ 책의 story 6에 내용을 정리하였습니다. 포인터가 주소값을 저장하는 변수라는 것은 알고 있지만, 포인터 변수에 대한 자세한 동작을 모른다면 한번 읽어보시는 것도 좋아보입니다.

메모리에 접근하려면 반드시 포인터가 필요한가?

정답은 X이다. 주소값만 알고있다면 포인터 없이 접근이 가능하다. 다만 주소값은 실행할 때 마다 바뀌기 때문에 포인터 없이 원하는 메모리에 접근하기는 쉽지 않다. 본 책에서는 임베디드 환경에서 개발시 메모리 레이아웃을 이해하고 개발한다면 포인터 없이 바로 주소값에 접근하는것이 문제되지 않는다고 한다.

포인터와 포인터 연산은 +, - 만 가능한가?

  • 포인터로 연산이 가능한 부분
    • 정수와의 + , - 연산 (p+1, p-1)
    • 단항 연산 (p++, p–)
    • 포인터끼리의 - 연산 (p - q)
    • 포인터끼리의 대입 (p = q)
    • 포인터끼리의 비교 (if(p>q) {})
  • 포인터로 연산이 불가능한 부분
    • 실수와 +, - 연산 및 정수와 * , / 연산
    • 포인터끼리의 +, *, /

배열의 이름은 포인터인가?

배열의 이름은 배열의 시작 주소를 나타낸다. 그렇다면 배열 이름은 포인터인가?

int a[] = {1, 2, 3};
int c = 100;
int *b = &c;
a = b;

위의 코드를 컴파일하면 에러가 발생한다

pointer_array.c:7:5: error: assignment to expression with array type
   a = b;
     ^
make: *** [<내장>: test] 오류 1

a[] 배열의 a와 &a 는 같은 의미인가?

a[3] (1, 2, 3);

위와 같이 배열을 선언하였고, 배열은 메모리 100번지에 저장되었을 때 주소 및 값의 표현은 다음과 같다.

실주소 값의 표현
0x100 a[0] == *a
0x104 a[1] == *(a+1)
0x108 a[2] == *(a+2)

이때

1) a, &a의 값은 각각 얼마인가? 2) a+1, &a+1 의 값은 각각 얼마인가?

이에 대한 답은 아래와 같다.

1) a = 0x100, &a = 0x100 2) a+1 = 0x104, &a+1 = 0x10c

(1) 의 값은 많은 분들이 예상하였을 것이다. 배열의 이름은 주소값이라고 알고 있기 때문이다.

하지만 (2)의 결과는 모르시는 분들이 꽤 될것으로 생각된다.

a+1 은 a 의 주소값의 +4이다. a+1 을 하게 되면 a의 자료형인 int 의 크기만큼 주소값이 증가한다

하지만 &a+1은 a 주소값의 +12 이다. &a+1 을 하게되면 a 의 크기만큼 주소값이 증가하기 때문이다

이해를 돕기 위해 테스트 코드를 작성하였다. 테스트 코드 및 결과는 아래와 같다.

#include<stdio.h>

int main() {
  int i;
  int a[3] = {1, 2, 3};
  int p, q;

  for(i=0; i<3; i++)  printf("a[%d] address : %p\n", i, &a[i]);

  printf("\n1) a = %p, &a : %p\n", a, &a);
  printf("2) a+1 = %p, &a+1 = %p\n\n", a+1, &a+1);

  p = &a+1;
  q = &a;
  printf("(&a+1) - a = %d\n", p-q);
  printf("sizeof(a) : %d\n", sizeof(a));


  return 0;
}
$ ./array_address
a[0] address : 0xbea0a478
a[1] address : 0xbea0a47c
a[2] address : 0xbea0a480

1) a = 0xbea0a478, &a : 0xbea0a478
2) a+1 = 0xbea0a47c, &a+1 = 0xbea0a484

(&a+1) - a = 12
sizeof(a) : 12

&a+1 와 a의 차이는 12이다. sizeof(a)도 12이다. 따라서 &a+1 은 a 의 크기만큼 증가했다는 것을 알 수 있다.

정리

  1. 메모리는 포인터 없이 직접 접근할 수 있다.
  2. 포인터끼리의 연산은 만 가능하고 +는 사용할 수 없다.
  3. 배열의 이름은 상수이다.
  4. 배열 이름은 배열 첫 요소에 대한 주소이고, &배열 이름은 배열의 시작 주소다.

테스트에 사용한 샘플 코드는 github에 업로드하였다.

  • https://github.com/Sunghwan7330/my_study/tree/master/book_study/c_code_optimization/story06_pointer

Leave a comment