Let's kick it
[혼공C]ch.10_배열과 포인터 본문
- 아티스트
- IVE (아이브)
- 앨범
- IVE EMPATHY
- 발매일
- 2025.02.03
10-1 배열과 포인터의 관계
배열명으로 배열 요소 사용하기
- 포인터에 배열명을 저장하면 배열명처럼 사용가능함.
- 배열명은 첫 번째 배열 요소의 주소 값임.
- 배열명은 첫 번째 배열 요소를 가리킴.
- 간접 참조 연산(*)을 사용하면 모든 배열 요소에 접근 가능함.
포인터의 연산
- 포인터의 정수 덧셈
계산식 : 주소 + (정수 * sizeof(자료형의 크기) )
ex) int 배열의 포인터 p가 있을 때, p+1은 p에서 4바이트(int 크기) 뒤의 주소를 가리킴
- 포인터끼리의 뺄셈
계산식 : (두 포인터의 주소 값의 차) / (가리키는 자료형의 크기)
결과 : 배열 요소 간 간격 차이
뺄셈 계산 이후 관계 연산자(>, < 등)로 대소관계 확인 가능.
배열명은 주소이므로 포인터에 저장해 포인터로 연산식이나 대괄호를 써서 배열 요소 사용.
배열 요소 표현식과 포인터 연산식의 동등성
- 배열 요소 표현식 ary[1]과 포인터 연산식 *(ary+1)은 동일함
- 대괄호를 사용한 배열 요소 표현식은 내부적으로 포인터 연산식으로 변환됨
ex) pa[2] = pa[0] + pa[1]
-> *(pa+2) = *(pa+0) + *(pa+1) // 포인터 연산식으로 바뀜
-> *(ary+2) = *(ary+0) + *(ary+1) //포인터 pa에 저장된 값은 ary, ary 배열명으로 바뀜
-> ary [2] = ary[0] + ary[1] // 배열 요소 표현식으로 바뀜.
배열명과 포인터의 차이
- 값 변경 가능성
- 배열명: 주소 상수로, 대입은 가능하지만 값 변경 불가
- 포인터: 변수로서 값 변경 가능
- sizeof 연산 결과
- 배열명: 배열 전체 크기 반환
- 포인터: 포인터 자체의 크기만 반환 (32비트 시스템에서 4바이트, 64비트 시스템에서 8바이트)
- 배열명을 포인터에 저장해도 포인터의 특성을 가지므로 배열 전체 크기 확인 불가
Visual Studio 2022에서 실행하는데 원래의 코드로 돌리면 오류가 너무 많아 빌드조차 되지 않았음.
그래서 원하는 결과가 나오도록 위의 코드를 조금 수정했다.
포인터끼리의 뺄셈은 값을 빼는 게 아니라 주소값의 차이기 때문에 %u가 아닌 포인터 주소를 출력할 때 사용하는 서식 지정자인 %p로 변경해 주었고, print함수에서 %p를 사용하기 위해 형 변환을 사용했다.
(void *)의 형 변환 목적은
- 일반적인 포인터로 사용: void*는 어떤 자료형의 포인터든 담을 수 있는 일반적인 포인터. 특정 자료형에 종속되지 않고 포인터의 주소값만 사용할 때 유용.
- printf 함수와의 호환성: printf 함수에서 %p 형식 지정자는 void* 포인터를 인자로 받음. 따라서 다른 자료형의 포인터를 출력하려면 void*로 형 변환해야 함.
포인터 뺄셈 연산 결과를 %u를 출력하는 것도 문제였다. 포인터 뺄셈은 주소 차이가 아닌 요소 개수를 반환하기 때문에 ptrdiff_t타입으로 처리해야 함. 그래서 ptrdiff_t를 위한 헤더인 stddef.h를 추가해 주고 %td를 사용해 줌.
뺄셈의 결과의 값을 단순히 출력만 할 때는 자료형 선언 없이 직접 사용해도 되는데, 이때 출력 형식 지정자로 %td가 사용되어야 한다.
- 클로드에게 ptrdiff_t에 대해 자세히 설명해 달라고 부탁했다.
ptrdiff_t는 C 언어에서 두 포인터의 차이(difference)를 나타내기 위해 특별히 설계된 자료형으로, 중요한 특징으로는
- 목적: 같은 배열 내의 두 포인터를 뺄 때 사용되며, 두 포인터 사이의 요소 개수를 나타냄.
- 정의 위치: <stddef.h> 헤더 파일에 정의되어 있음.
- 크기: 시스템에 따라 크기가 달라질 수 있습니다. 보통 size_t와 같은 크기를 가지며, 대개 시스템의 주소 지정 능력에 맞게 설계됨.
- 부호 있음: ptrdiff_t는 부호 있는(signed) 정수형입니다. 이는 포인터를 뺄 때 음수 결과가 나올 수 있기 때문.
- 사용 예시:
int array[10];int *p1 = &array[1];int *p2 = &array[7];ptrdiff_t diff = p2 - p1; // 결과는 6 (요소 개수 차이)
- 출력 형식: printf에서 ptrdiff_t 값을 출력할 때는 % td 서식 지정자를 사용.
- 중요성: 단순히 주소 값의 차이가 아니라 배열 요소 간의 논리적 거리를 나타냄. 예를 들어, int 배열에서 요소 크기가 4바이트라면, 메모리 주소 값의 차이가 12일 때 ptrdiff_t 값은 3.
책에서는 포인터 뺄셈 결과를 % u로 출력했는데, 이는 단순히 주소 값의 차이를 출력하려는 의도였을 수 있습니다. 하지만 C 표준에 따르면 포인터 뺄셈은 요소 개수를 반환하므로 ptrdiff_t를 사용하는 것이 올바릅니다.
내가 가장 헷갈려했던 포인트! 포인터의 주소값과 포인터가 가리키는 값에 대한 개념
10-2 배열을 처리하는 함수
함수에서 배열 처리 방법
- 함수로 배열을 처리하려면 포인터가 필요함
- 함수 호출 시 배열명을 인수로 넘기면 배열의 첫 번째 요소 주소가 전달됨
- 호출된 함수의 매개변수에 포인터를 사용해 배열 요소에 접근 가능
입출력 함수 예시
- 입력 함수(scanf):
- 데이터를 저장할 배열 위치(주소)가 필요함
- 예: scanf("%d", pa + i);에서 pa + i는 i번째 배열 요소의 주소
- 출력 함수(printf):
- 저장된 값을 읽어 출력함
- 예: printf("%d", pa[i]);로 i번째 배열 요소의 값 출력
함수에서 배열 크기 문제
- 호출받은 함수 내에서 sizeof 연산자로 배열 크기를 알 수 없음
- 배열을 가리키는 포인터에 sizeof를 사용하면 포인터 자체의 크기만 반환됨
- 배열 요소 개수를 구하는 방법 sizeof(배열명) / sizeof(배열 요소)는 함수 내에서 사용 불가능
- 따라서 배열 크기는 별도의 매개변수로 전달하는 것이 좋음
아래의 코드를 돌리는데 계속 출력된 값은 보이질 않아 뭐가 문제인 건지 확인하기 위해 제미나이에게 물어봤을 때 공백이 문제인데 공백을 찾지 못했고 안 그래도 비주얼 스튜디오를 돌릴 때 상태가 이상해서 이 문제이겠거니 하고 초기화하고 복구까지 했는데도 여전히 같아서 마지막 방법인 재설치 전에 클로드에게 물어봤더니 scanf_s함수에서 공백이 있어서 이 공백이 입력 처리 방해했을 가능성이 높다고 하자마자 내 눈에 보이지 않던, 나조차 모르게 입력되어 있던 공백을 찾아 지우니 원하던 결과가 나왔다.
이 코드가 어디가 문제인 건지 확인하기 위해 각 단계별로 값을 출력하도록 코드를 수정했던 버전이다.
함수 내 변수와 배열의 사용 범위
- 함수 내에 선언된 변수나 배열의 이름 사용 범위는 중괄호 블록 {}으로 제한됨
- 함수 내에서만 사용 가능하므로 2개 이상의 함수에서 같은 이름의 변수나 배열을 독립적으로 사용 가능
- 각 함수 안에서 사용한 변수나 배열의 이름은 사용 범위가 달라 서로 충돌하지 않음
'c언어' 카테고리의 다른 글
혼공학습단 13기_혼공C 회고 (2) | 2025.02.24 |
---|---|
[혼공C]ch.09_포인터 (0) | 2025.02.08 |
[혼공C]ch.08_도전 실전 예제 문제 (0) | 2025.02.03 |
[혼공C]ch.08 (0) | 2025.02.02 |
[혼공C]4주차_ch.07 (0) | 2025.02.01 |