[C++] 포인터 & 포인터 배열

꿈별·2023년 3월 13일
0

C++

목록 보기
10/27

포인터

포인터 변수(=포인터)

  • 메모리의 주소를 저장하는 변수
  • 주소의 단위는 바이트, 주소는 정수 타입으로 표현한다.
  • 선언 시 초기화되지 않는다. 초기화되지 않은 값은 쓰레기

[선언]
자료형* 포인터변수명

ex)
int* pInt = nullptr;
short* pShort;
//nullptr은 아무것도 가리키지 않는 걸 의미함

  • 포인터 변수의 자료형은 가리키는 변수의 자료형과 같아야 한다.
int a = 10;
float b = 1.23;

(O)
int* pInt1 = &a;
float* pfloat2 = &b;

(X)
int* pInt2 = &b; //Error
pInt1 = &b;		 //Error

  • C++에서는 포인터 변수가 리터럴 메모리 주소를 할당할 수 없다.
int *pInt = 3; //Error

  • 포인터 변수를 동시에 여러 개 선언할 경우, * 연산자가 각 변수에 포함돼야 한다.
int* pInt1, *pInt2;
int* pInt3, pInt4;
-> 포인터 변수 O : pInt1, pInt2, Pint3
   포인터 변수 X : pInt4 (int형 변수)

  • 선언된 변수or값에 참조 연산자(&)를 붙이면 해당 변수의 주솟값을 나타낸다.
//정수 i의 주솟값을 포인터 변수 pInt에 저장
int i = 100;
int* pInt = &i;

  • 포인터 변수를 선언한 뒤, 변수명 앞에 다시 역참조 연산자(*) 를 붙이면 해당 변수에 저장된 주솟값을 참조하겠다(접근하겠다)는 의미
//위 코드에서 이어짐
(*pInt) = 100; //i를 참조하여 i에 100을 대입
cout << *pInt; // pInt가 가리키는 주소의 값을 출력

  • 포인터 변수의 자료형은 해당 포인터가 전달받는 주소를 해석하는 단위이다.
    가리키는 주소에 들어있는 값이 그대로여도, 보는 관점(자료형)에 따라 다르게 표현된다.
    포인터 변수 입장에서는 실제 메모리 공간에 들어있는 데이터 값은 중요하지 않다.

  • 포인터 변수의 증감 단위는 가리키는 자료형의 크기에 따라 달라진다.

    포인터 변수의 주솟값을 1 증가시키면
    sizeof(자료형)과 같이 자료형의 크기 단위로 증가한다.
    ex)
    int형 포인터 pInt의 주소가 100번지일 때,
    pInt+=1; 을 하면 주솟값은 101번지가 아닌 104번지로 바뀐다. (int형의 크기는 4btye)


포인터 활용 예시

  • 포인터 변수를 사용하여, 두 변수의 값을 서로 변환하는 함수를 만들어 보자.
#include <stdio.h>
using namespace std;

void swap(int* x, int* y)
{
	int temp;
	temp = *x;
	*x = *y;
	*y = temp;
}
int main()
{
	int x = 1;
 	int y = 2;
	swap(&x, &y);
	printf("x = %d\ny = %d\n", x, y);
	return 0;
}
[출력]
x = 2
y = 1

-> 포인터를 사용하지 않으면, 매개변수의 값들이 함수 내에서만 처리되고, 함수가 종료되면서 사라지기 때문에 두 변수의 값이 바뀌지 않게 된다.

//포인터 변수를 사용하지 않은 경우

#include <stdio.h>
using namespace std;

void swap(int x, int y)
{
	int temp;
	temp = x;
	x = y;
	y = temp;
}
int main()
{
	int x = 1;
 	int y = 2;
	swap(x, y);
	printf("x = %d\ny = %d\n", x, y);
	return 0;
}
[출력] //두 변수의 값이 바뀌지 않았다.
x = 1
y = 2

✔포인터 배열

  • 배열의 이름은 배열의 시작 주소(배열 첫 번째 요소의 주소)를 의미한다.
  • 배열은 포인터와 유사하지만 같지 않다.
배열 포인터 : 배열을 가리키는 "포인터"
포인터 배열 : 배열 요소로 포인터를 가지는 "배열"

[선언]
int(*ptr)[4]; //배열 포인터, 
int* ptr[4]; //포인터 배열
  • 예시 1
int a = 10;
int b = 20;
int c = 30;
int d = 40;

int* ptr[4];

ptr[0] = &a;
ptr[1] = &b;
ptr[2] = &c;
ptr[3] = &d;

printf("%d %d %d %d\n", *ptr[0], *ptr[1], *ptr[2], *ptr[3]);
[출력]
10 20 30 40
  • 참고
char str[] = "Hello";
printf("%s", str);
//printf("%s", &str[0]); 과 같음
[출력]
Hello

-> 배열의 이름 str이 배열의 시작 주소를 가리킨다. 이 때 %s를 사용했으므로 시작 주소부터 널 문자(\0)가 나올 때까지 문자들을 출력해 준다,

  • 예시 2 (다차원 배열과 문자열)
char strings[3][10] = { "Hello", "World", "Doodle"};

for (int i = 0; i < 3; i++) {
	printf("%s\n", strings[i]);
    //strings[i] 는 &strings[i][0] 과 같다.
}

-> 이 때 strings[i] 자체가 포인터가 된다.
그리고 위의 '참고'처럼, i 행의 문자를 \0까지 모두 출력하게 된다.

[출력]
Hello
World
Doodle

*(iArr+0) = 10;
-> 배열의 첫 번째 데이터에 10을 대입한다.
배열의 이름이 이미 배열의 시작 주소이므로, 시작 주소를 그대로 사용(0을 더하는 것과 같음)해도 된다.

*(iArr+1) = 10;
-> 배열의 두 번째 데이터에 10을 대입한다.
(위와 같은 원리가 배열의 인덱스가 0부터 시작하는 이유)


+문제풀이

1번)

//문제 1.
short sArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* pI = (int*)sArr;
int iData = *((short*)(pI + 2));
printf("1번 문제 정답 : %d\n", iData);
int* pI = (int*)sArr;

-> short 배열 sArr를 int형 포인터 pI가 가리키기 위해, sArr를 int 타입으로 강제 캐스팅(형변환)함

int iData = *((short*)(pI + 2));

-> sArr는 short형 배열로, 각 데이터 요소 크기 2바이트
-> int 포인터 pI의 주솟값 2를 증가시킴. 즉 8바이트 증가함.(int는 4바이트) 이 때 이상한 값 나올 것을 방지하기 위해, 접근하기 직전에 포인터를 short형으로 형변환해줌
답 5


2번)

//문제 2.
char cArr[2] = { 1, 1 };
short* pS = (short*)cArr;
iData = *pS;
printf("2번 문제 정답 : %d\n", iData);

-> 각 데이터가 1바이트인 char형 배열을 short형 포인터 pS가 가리킬 때, short형의 크기(2btye)로 접근함.
-> (하단 그림) 8비트(1바이트)씩 두 칸(맨 마지막 비트가 1로 채워짐)
-> 1바이트로 표현할 수 있는 최대치 : 255 (8비트가 전부 1로 채워질 경우)
(2^8 라 256가지 표현 가능. 0~255)

cArr을 char형으로 바라보면
[ 0 0 0 0 0 0 0 1 ][ 0 0 0 0 0 0 0 1 ]
와 같은 상태이고,
이걸 다시 short형으로 보고, *pS로 값에 접근하기로 했다. 따라서
[ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 ] 가 된다.
즉 257

답 257


추가

void Test(int a)
{
	a = 500;
}

int main()
{
	int a = 100;
    Test(a);
    printf("출력 : %d", a);
    return 0;
}
[출력}
출력 : 100
  • Test 함수의 변수 a와, main 함수의 변수 a는 별개이다.
    Test 함수의 a는 값이 500이 된 후. 함수가 종료되면서 사라져 버린다.

💡 Test 함수를 호출하면서 main 함수의 변수 a의 값을 바꾸고 싶으면?
👉 Test 함수에게 a의 주소를 넘겨준다.

void Test(int* a)
{
    *a = 500;
}

int main()
{
    int a = 100;
    Test(&a);
    printf("출력 : %d", a);
    return 0;
}
[출력}
출력 : 500

scanf 함수?

scanf_S("%d", &a);
  • scanf는 사용자가 콘솔 창에 입력한 데이터를 지정한 변수에 넣어주는 함수이다.
  • 이 때 입력받을 변수의 주소를 알려 줘야 접근해서 데이터를 넣어줄 수 있다.

[참고]
https://youtu.be/dnU6Rqj8DFU
https://boycoding.tistory.com/199
https://www.ijemin.com/blog/c-%ED%8F%AC%EC%9D%B8%ED%84%B0-%EC%B0%B8%EC%A1%B0-%EC%97%B0%EC%82%B0%EC%9E%90%EB%8A%94-%EC%AA%BC%EA%B0%9C%EC%84%9C-%EC%9D%B4%ED%95%B4%ED%95%B4%EC%95%BC-%EC%89%BD%EB%8B%A4/
https://boycoding.tistory.com/201
https://youtu.be/JsS1A0xwozo
https://youtu.be/Y-0KlEy6yxs
https://velog.io/@starkshn/CPP어소29포인터-문제-풀이-해답

0개의 댓글