211026, C언어 입문 - day 11

Min Hyeok·2021년 10월 26일
0

C언어 개념 익히기

목록 보기
13/19

ㅎㅇ. 오랫만이다.

최근엔 어느정도 백준, C언어 이론공부 모두 끄적끄적했다.

15일에 올린 근황을 보면 백준은 55문제를 맞췄는데, 현재는 64문제까지 왔다. 기본 수학 1의 한문제를 넘기고 기본 수학 2를 풀고 있는 중이다.

그리고 C언어 이론공부도 현재 18장까지 대충 마치고 오늘 19장을 들어왔는데, 14장까진 포인터를 제외하곤 어느정도 배운 기억이 있는 친구들이였다면, 15장부터는 좀 익숙하지 않은? 배운 기억이 잘 없는? 내용들이라 좀 한번에 몰아서 복습해보고 싶어서 여기에 복습을 하지 않았다.

그래서 오늘부터 다시 달릴 예정. 이론 공부 1회차가 (이제와서) 어느정도 끝이 보여서, 빨리 1회독 끝내고 C언어 2회독 + 자구, 파이썬 1회독을 해야 할거같다. 맘같아선 프로그래밍 말고 컴활도 해야할 거 같고..

여튼, 시작해보자

15장, 배열과 포인터

이번 장에서는

1. 배열과 포인터의 표기상 특징 이해
2. 두 문법을 섞어서 사용했을 때 장점알아보기 및 이해하기

를 해볼 예정이다.

15.1

배열은 "해당 배열이 사용하는 메모리 그룹의 시작 주소를 기준으로 삼는다". 포인터는 "포인터 변수가 가리키는 메모리의 시작 주소를 기준으로 삼는다."

? 큰 차이가 없는거같은데..

메모리의 시작 주소를 기준으로 삼는다.

그렇다. 큰 차이가 없다. 그래서 표기만 다르지, 핵심적인 문법 구조가 비슷하다. 그래서 두문법의 표기법을 바꿔서 쓸 수도 있다.

// code 1

char data[5];
data[1] = 5;
*(data + 1) = 5; // 바로 윗줄과 똑같은 표현임

// code 2

char data;
char *p = &data;
*p = 3;
p[0] = 3; // 바로 윗줄과 똑같은 표현임

요렇게.

엥, 근데 생각해보면 그냥 배열은 배열표기법 쓰고, 포인터는 포인터 표기법쓰면되지, 왜 굳이 섞어쓰냐는 생각이 든다?

이런 나같은 아무것도 모르는 noob들을 위해서, 배열 표기법의 한계 pt가 있다.

만약, data라는 1차원 배열이

int data[2] = {0x12345678, 0x12345678};

라고 초기화되어 있다고 가정하겠다.

위처럼 선언한 배열을 그림으로 표기하면

이렇게 되어있다. 보면 data[0]은 int형인 4바이트에 맞게 나뉘어있다.

근데 만약, "어 나 data[0]의 첫번쨰 바이트 부분 0x55로 넣고싶은데 ㅋㅋ"라고 생각해서 data[0]=0x22; 로 바꾼다면?

이건 딱 보기만 해도 각이 나온다. 한 바이트가 아닌, data[0] 네 바이트가 전부 다 바뀌겠지. 0x00000022로. 그래서 배열 표기법은 의도한대로 표현하려면 답이 읎다.

근데, 포인터 표기법을 써준다면??? (with Casting)

*(char *)(data + 0) = 0x22; // 일시적으로 int *형을 (char *)형으로 변환시킴

내가 의도한대로 바뀐다!! 포인터 표기법을 배열에다 스까서 써준다면, 항목 크기 상관없이, 자유롭게 값을 수정할 수 있게 된다.

좀 더 세심하게 배열을 건들 수 있게 된다고 생각하면 되겠다.

배열 표기법을 포인터에 끼워넣는 방법은, "문장, 수식이 단순해진다" 는 장점이 있다. 예로 들면,

int tips = 0x12345678, sum;
char *p;
p = (char *)&tips;
sum += *(p + 0);
// 여기서  *(p+0)은 1.p+0, 2.* 로 두번의 연산을 해야하지만,
 만약 이걸 p[0]으로 바꾼다면, 수식이 깔끔해진다.

요로켕.

15.2

포인터는 "일반 변수의 주소"만 가질 수 있는게 아니라, "그룹으로 묶인 메모리의 주소"도 가질 수 있다.

,, 이정도만 기억해두자. 여기선.

15.3

여기서는 배열에 포인터를 활용 or 포인터에 배열을 활용하는 방법을 알려준다.

char data[5] = {1,2,3,4,5};
int i, sum=0, select = 2;
for(i=0 ; i<10 ; i++) sum += data[select];

이렇게 적는다면, i가 0부터 10까지 가면서 data+select 연산을 해주어야한다.
근데 만약, 배열의 특정 요소 주소를 포인터 변수에 저장하고 쓴다면?

char data[5] = {1,2,3,4,5};
int i, sum=0, select = 2;
char *p = data + select;
for(i=0 ; i<10 ; i++) sum += *p;

이렇게 써주면, 코드를 조금이라도 더 효율적으로 짜줄 수 있다.

15.4

배열과 포인터가 합-체를 한다! 근데, 여기서 "배열을 기준"으로 포인터와 합체할지, "포인터를 기준"으로 배열과 합체할지가 각각 의미가 달라지기에, 그것부터 알아보자.

  1. "배열 기준" 포인터와 합체
char *p[5];

위와 같이 선언을 해주는 것인데, 이렇게 되면 p 배열의 크기는 포인터 변수 4byte * 5 = 20byte가 된다.

이런 느낌으로 구성되어있단 얘기다.

char *p[5]

가 알고보니

char *(p[5])

라는 얘기지. 포인터가 5개 선언되었다는 얘기다.

  1. "포인터 기준" 배열과 합체
char (*p)[5];

이런 형태를 "배열포인터"라고 한다.

이렇게 선언하면 (*p)가 먼저 처리되므로, p변수는 "배열"이 아니라, "포인터"라는 뜻이다. 고로, p변수 크기는 4바이트다. 그리고 그 다음으로 char [5]가 읽혀지는데, 그러면 이게 무슨 뜻이 될까?

위처럼, 포인터 변수 *p가 가리키는 대상의 크기가 char [5], 즉 5바이트라는 뜻이 된다.

만약 5개의 공간 (5바이트 메모리 공간)중 두번째 공간에 7이라는 숫자를 넣고싶으면

(*p)[1] = 7;

위와 같이 사용해주면 된다.

그리고, 포인터 변수 p 에 주소 연산을 해주면, 한번 p++를 할 때마다 저장된 주소가 5씩 증가한다. p는 "대상을 가리키는 포인터"이기 때문에, 한번 가리킬때마다 5바이트의 메모리 공간을 가리킨다. 이걸 "다음 공간"을 가리킨다고 해석해주면 p가 100번지일 때 p++을 할 시 105번지로 이동하게 된다.

.. 이건 어따 써먹나? 그냥 1차원 배열을 가리키려면 그냥 포인터 *p만 써주면 되는데.

이 배열포인터는 *라는 1차원 포인터, []라는 1차원 배열을 더해줬기 때문에 2차원의 개념을 가지므로, 2차원 배열을 가리키는 용도로 써준다.

만약 char data[3][5];라는 주소가 있다고 가정하고, 이걸 char(*p)[5]라는 배열포인터에 저장을 한다고 치자.

char data[3][5];
char (*p)[5];
p = data; // 포인터 변수 p는 data 배열의 시작 주소를 저장한다.
(*p)[1] = 3; // p[0][1] = 3
(*(p+2))[2] = 5; // p[2][2] = 5
(*(p+1))[4] = 7; // p[1][4] = 7

이렇게. 쓸 수 있단 얘기다.

조금 더 상세한 내용을 복습해야 할 땐 여기를 참고하자.

이상, 15장 끗.

0개의 댓글