관리 메뉴

나구리의 개발공부기록

Chapter 02 - C언어(포인터) 본문

2024년도 수제비 실기책(6판) 내용 정리


13. 포인터

1) 포인터(pointer)개념

  • 변수의 주소값을 저장하는 공간

 

2) 포인터 선언

자료형* 포인터_변수명 = &변수명;
// 주소에 해당하는 값을 가리키는 * 연산과,
// 주솟값을 나타내는 & 연산은 반대 기능
//*(&)처럼 두 연산을 같이쓰면 서로 상쇄됨
  • 자료형 뒤에 *를 붙이면 주소를 저장하는 포인터 변수라는 의미이고, 일반 변수명에 &를 붙이면 해당 변수명의 주솟값임
  • 각 타입의 변수를 가리키는 포인터 변수 선언시 int*, char*, float* 처럼선언해서 사용
  • 주소에 해당하는 값을 가리킬때는 *를 사용
#include <stdio.h>
int main() {
  int a = 10;    // int 타입 변수 a에 10을 저장
  int* b = &a;   // 변수 a의 주소를 포인터 변수 b에 저장
  
  // a의 값 10 출력
  // 포인터 변수 b가 가르키는 주소값에 저장된 값(a의 주소값) 10 출력
  // a의 주소값이 가르키는 값 == a의 값 이므로 10 출력
  printf("%d %d %d", a, *b, *(&a));
  return 0;
}

3) 배열과 포인터

  • 배열이 '자료형 배열명[요소];'의 형태일 때
구분 코드
배열의 i번지 주소 배열+i == &배열[i];
배열의 i번지의 값 *(배열+i) == 배열[i];

 

(1) 1차원 배열과 1차원 포인터

 

  • 1차원 배열에서 배열명만 단독으로 사용할 경우 1차원 포인터와 동일함
  • 1차원 배열일 때는 배열명[요소] 형태, *(배열명+요소)일 경우 값을 가르킴
  • 1차원 포인터일 때는 포인터[요소] 형태, *(포인터+요소)일 경우 값을 가르킴

 

 

 

#include <stdio.h>
int main() {

  // 길이가 3인 int타입 배열 변수a에 1, 2를 저장, 남은 자리는 0이 저장됨
  // a의 0번째에는 1, 1번째에는 2, 2번째에는 0이 저장됨
  // &a[0] == a(a의 주소값), &a[1] == (a+1)(a+1의 주소값)
  // a[0] == *a(a의 값), a[1] == *(a+1)(a+1의 값)
  // *a가 a[0]을 가르키게 되는 이유는 *a는 *(&a[0])가 되어서 서로가 상쇄됨
  int a[3] = {1, 2};
  
  int *p = a;  // 포인터 변수 p에 a의 주소값을 저장
  
  // 각 배열a의 0,1,2번째 위치의 값을 출력 -> 1, 2, 0
  printf("%d %d %d\n", a[0], a[1], a[2]);
  
  // *은 값을 뜻하므로 a의 주소가 가르키는 값, a+1의 주소가 가르키는값, a+2의 주소가 가르키는 값을 출력
  printf("%d %d %d\n", *a, *(a+1), *(a+2)); // 1, 2, 0
  
  // 포인터 변수p의 값은 a의 주소이므로, 위와 동일한 값이 꺼내짐
  printf("%d %d %d\n", *p, *(p+1), *(p+2)); // 1, 2, 0
  
  // 위와 동일한 값을 뜻함
  printf("%d %d %d\n", p[0], p[1], p[2]);   // 1, 2, 0
  
  return 0;
}

 

(2) 2차원 배열과 1차원 포인터

 

  • 2차원 배열에서 배열명만 단독으로 사용할 경우 2차원 포인터와 동일함
  • 배열명[요소]형태, *(배열명+요소)는 1차원 포인터와 동일하고, 1차원 포인터에 대해 *과 [ ]을 이용해야 값을 가르킬 수 있음

#include <stdio.h>
int main() {
  
  // int타입 배열 변수 a에 행길이가 3, 열길이가 2인 2차원 배열을 아래 처럼 값을 넣고 초기화
  // a[0][0] == 1, a[0][1] == 2,
  // a[1][0] == 3, a[1][1] == 4,
  // a[2][0] == 5, a[2][1] == 6,
  // a[0] == &a[0][0], a[0][1] == &a[0][1]
  // a[1] == &a[1][0], a[1][1] == *a[1][1]
  // a[2] == &a[2][0], a[2][1] == *a[2][1]
  int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
  
  // 포인터 변수 p에 a[1](&[1][0])의 주소값을 저장
  // p = a[1]이므로 p대신에 a[1]을 넣어도 결과가 같음
  int *p = a[1];    
  
  // a[0] == &a[0][0], *a[0] == *(&a[0][0]) 이지만 &과 *가 상쇄되어 a[0][0]값을 출력
  // 결과적으로 *a[0][0] == a[0], *a[1] == a[1][0], *a[2] == a[2][0]의 값을 출력
  printf("%d %d %d\n", *a[0], *a[1], *a[2]);    // 1, 3, 5
  
  // **a == *(&a[0][0]) 이므로 서로 상돼되어 a[0][0]값이 출력됨
  // **(a+1) == *(&a[1][0]) a[1][0]값이 출력
  // **(a+2) == *(&a[2][0]) a[2][0]값이 출력
  printf("%d %d %d\n", **a, **(a+1), **(a+2));  // 1, 3, 5
  
  // 포인터 변수 p의값은 a[1]의 주소값을 가지고 있으므로 a[1][0]의 값을 출력
  // *(p+1) == *(a[1]+1)이 되므로 a[1][1]의 값을 출력
  printf("%d %d\n", *p, *(p+1));   //3, 4
  
  // p == p[0] == a[1]이므로 a[1][0]의 값을 출력
  // p[1] == a[1][1], 해당 값을 출력
  printf("%d %d\n", p[0], p[1]);   //3, 4
  
  return 0;
}

 

(3) 2차원 배열과 포인터 배열

#include <stdio.h>
int main() {
  
  // 2차원 배열 선언
  int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
  
  // 길이가 3인 int타입 포인터배열 p에 각각 a[2], a[0], a[1]의 주소값을 저장함
  int *p[3] = {a[2], a[0], a[1]};
  
  printf("%d %d %d\n", a[0][0], a[1][0], a[2][0]);  // 1, 3, 5
  
  // *(&a[0][0]) == a[0][0]의 값을 출력, 이하 동일
  printf("%d %d %d\n", *a[0], *a[1], *a[2]);        // 1, 3, 5
  
  // p[1][0] == (a[0])[0] == a[0][0]의 값을 출력
  // p[2][0] == (a[1])[0] == a[1][0]의 값을 출력
  // p[0][0] == (a[2])[0] == a[2][0]의 값을 출력
  printf("%d %d %d\n", p[1][0], p[2][0], ;p[0][0]); // 1, 3, 5
  
  // *p[1] == *(&p[1][0]) == p[1][0] == a[0][0]의 값을 출력, 이하 동일
  // *과&이 상쇄되는 것에 주의
  printf("%d %d %d\n", *p[1], *p[2], *p[0]);        // 1, 3, 5
  
  return 0;
}

 

(4) 2차원 배열과 2차원 포인터

 

  • 배열명[요소][요소], *배열명[요소], **(배열명+요소)일 경우 값을 가르킴
  • 2차원 포인터는 int **p, **q; 형태로 선언할 수 있으나 2차원 배열에서 한덩어리의 크기를 알 수 없기 때문에 열의 개수를 명시하는 형태로 포인터 변수를 선언함
  • 포인터 변수 선언 시 괄호가 없으면 2차원 배열에 대한 포인터가 아닌 포인터 배열이 됨 ex) 자료형 (*변수명)[크기];

#include <stdio.h>
int main() {

  int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};  // 2차원 배열 선언
  int (*p)[2] = a;    // 2차원 포인터 배열 변수 p에 a의 주소값을 저장, (a[0][0]의 값을 가르킴)
  int (*q)[2] = a+1;  // 2차원 포인터 배열 변수 q에 a+1의 주소값을 저장 (a[1][0]의 값을 가르킴)
  
  printf("%d %d %d\n, a[0][0], a[0][1], a[1][0]");  // 1, 2, 3
  
  // p[0][0] == a[0][0]의 값을 출력, 이하 동일함
  printf("%d %d %d\n, p[0][0], p[0][1], p[1][0]"); // 1, 2, 3
  
  // q[0][0] == (a+1)[0] == a[1][0]의 값을 출력
  // q[0][1] == (a+1)[1] == a[1][1]의 값을 출력
  // q[1][0] == (a+2)[0] == a[2][0]의 값을 출력
  printf("%d %d %d\n, q[0][0], q[0][1], q[1][0]"); // 3, 4, 5

  return 0;
}

4) 구조체와 포인터

(1) 구조체 변수와 구조체 포인터

 

  • 일반 구조체 변수로 접근할 때는 .으로 접근하고, 구조체 포인터로 접근할 때는 ->로 접근

#include <stdio.h>

struct Student {  // 구조체 틀 생성
  char gender;    // Student 구조체에 char 타입 변수 gender 생성
  int age;        // int 타입 변수 age 생성
};

int main() {
  struct Student s = {'F', 21};  // 구조체타입 변수 s에 구조체의 순서에맞게 'F'와 21을 저장
                                 // gender에 'F'가 age에 21이 저장됨
  
  struct Student *p = &s;        // 포인터 변수 p에 변수s의 주소가 저장됨
  
  // 변수 s에 포함된 gender, age의 값을 출력
  printf("%c %d\n", s.gender, s.age);           // F 21
  
  // s의 주소값에 저장된 gender, age의 값을 출력
  printf("%c %d\n", (&s)->gender, (&s)->age);   // F 21
  
  // 포인터 변수 p가 가르키는 주소의 gender, age의 값을 출력
  printf("%c %d\n", p->gender, p->age);         // F 21
  
  // p의값(s의 주소값)에 포함된 gender, age의 값을 출력
  printf("%c %d\n", (*p).gender, (*p).age);     // F 21
  
  // 1차원 포인터이므로 p[0]으로도 접근이가능
  printf("%c %d\n", p[0].gender, p[0].age);     // F 21
  
  return 0;
}

 

(2) 1차원 구조체 배열과 1차원 구조체 포인터

 

  • 1차원 구조체 배열에서 배열명만 단독으로 사용할 경우 1차원 구조체 포인터와 동일함
  • 1차원 구조체 배열일 때 배열명[요소].변수명 형태, (*(배열명+요소)).변수명, 배열명 -> 변수명 형태, (배열명+요소) -> 변수형 형태로 값을 가르킴
  • 1차원 포인터일 때 포인터[요소].변수형 형태, (*(포인터+요소)).변수명, 포인터->변수명 형태, (포인터+요소)->변수명 형태로 값을 가르킴

#include <stdio.h>
struct Student {
  char gender;
  int age;
};

int main() {
  
  // 길이가 3인 구조체 배열변수 s에 값들을 저장
  struct Student s[3] = {'F', 21, 'M', 20, 'M', 24};
  struct Student *p = s;  // 포인터 변수 p에 변수s의 주소를 저장
  
  // 구조체 변수s의 [0]에 저장된 gender, age의 값을 출력
  printf("%c %d\n", s[0].gender, s[0].age);      // F 21
  
  // *s(*s의 값은 배열s가 가르키는 첫번째의 값) == s[0]이므로 위와 같음
  printf("%c %d\n", (*s).gender, (*s).age);      // F 21
  
  // 구조체 배열 변수명을 단독으로 사용하면 1차원 구조체 포인터와 동일
  // s -> == p ->, p는 s[0]의 값들을 가르키므로 위와 동일한 값이 출력됨
  printf("%c %d\n", s->gender, s->age);          // F 21
  
  // (s+1) == s[1], s[1]이 가르키는 변수의 값을 출력
  printf("%c %d\n", (s+1).gender, (s+1).age);    // M 20
  
  // 1차원 포인터 p에 첫번째에 저장된 값(s의 주소값)의 값들을 출력
  printf("%c %d\n", p[0].gender, p[0].age);      // F 21
  
  // 포인터 변수 p에 포함된 값(s의 주소값)의 값들을 출력
  printf("%c %d\n", (*p).gender, (*p).age);      // F 21
  
  // 포인터 변수 p가 가르키는 값(s의 주소값)의 값들을 출력
  printf("%c %d\n", p->gender, p->age);          // F 21
  
  // (p+1) == p[1], 포인터 변수 p의 두번째 값이 가르키는 값(s+1 == s[1])의 값들을 출력
  printf("%c %d\n", (p+1).gender, (p+1).age);    // M 20

  return 0;
}

5) 함수 포인터

 

  • 함수의 주소를 저장하고 해당 주소의 함수를 호출하는데 사용하는 포인터
  • 리턴타입 (*함수_포인터)(함수 파라미터);
#include <stdio.h>
void fn1() {                  // 반환 타입이 없는 fn1함수 정의
  printf("fn1 함수\n");       // fn1함수 호출시 출력
}

int fn2(int a) {               // 반환타입이 int이고 파라미터 타입이 int인 fn2 함수 정의
  printf("fn2 함수: %d\n", a); // fn2함수 호출시 파라미터의 값 a의 값을 출력
  return 0;
}

int main() {
  void (*pf1)();    // 리턴 타입이 없는 함수형 포인터 *pf1() 선언
  int (*pf2)(int);  // 리탄 타입이 int인 함수형 포인터 *pf2에 파라미터 타입을 int로 선언
  
  fn1();         // fn1 함수 호출
  // fn1 함수라는 문구 출력
  
  fn2(5);        // fn2 함수에 아규먼트 값으로 5를 지정하여 호출
  // fn2 함수: 5 문구 출력
  
  pf1 = fn1;     // 함수 fn1의 주소를 포인터 변수 pf1에 저장
  pf2 = fn2;     // 함수 fn2의 주소를 포인터 변수 pf2에 저장
  
  pf1();         // 포인터 변수 pf1(); 호출
  // pf1의 값은 함수 fn1의 주소이므로 fn1함수가 호출되어 실행됨
  // fn1 함수라는 문구 출력
  
  pf2(2);        // 포인터 변수 pf2에 아규먼트 값을 2를 넣어서 호출
  // pf2의 값을 함수 fn2의 주소이므로 fn2함수가 호출됨
  // 아규먼트의 값을 2가 되었으므로 fn2함수 호출시 전달인자는 2가 되어 연산이 됨
  // fn2 함수: 2 문구 출력
  
  return 0;
}

6) 사용자 정의 함수 포인터 반환

  • 사용자 정의 함수의 반환 값으로 포인터를 전달 받을 수 있음
#include <stdio.h>
#include <string.h>

char n[6];             // 길이가 6인 문자타입 변수 n선언
char *soojebi() {      // soojebi 함수가 char타입 포인터를 반환, n의 주소를 반환
  strcpy(n, "Hello");  // Hello의 문자열을 문자 배열n에 복사 후 n을 반환
  return n;
}

int main() {
  char *p = soojebi(); // soojebi 함수의 연산결과를 포인터 변수 p에 저장
  // 반환 결과가 n의 주소 == &n[0] 이므로 해당 주소를 포인터 변수에 저장
  
  printf("%s\n", p);  // 포인터 변수 p의값 == n의 주소 == &n[0] 의값을 문자열로 출력, Hello
  return 0;
}