다시 오신 걸 환영해요
이번 강의에서는 포인터 사용 사례를 살펴볼 텐데요
언제 포인터를 사용할지 어떻게 사용할지 어떻게 생성할지는 이미 알고 있죠
선언하고 정의하는 거죠
기존 변수 주소를 그들에게 할당하는 방법도 알죠 연산자의 주소를 이용해서요
그걸 참조 해제하는 방법도 알죠 포인터가
가리키는 값을 얻는 방법도요
실제로 어떻게 사용할까요?
C#은 여러분이 포인터에서 수행할 수 있는 몇 가지 기본 작업을 제공하죠
포인터에 주소를 할당할 수
있어요 이미 배웠죠
주소가 있는 기존 변수죠
연산자의 주소를 이용하여 변수의 주소를 볼 수 있습니다 거기에
포인트를 가지고 있죠 기존의 포인터를 다른 포인터에 할당할 수도 있어요
포인터에 주소를 할당하는 두 가지 방법이죠
그 자체로 배열 이름이 될 수 있어요
배열 얘기는 아직 안 했지만 포인터로 레이즈를
할 경우 이름만 지정하면 돼요 연산자 주소를 쓸 필요도 없어요
지금까지는 기존 변수를 사용하고 그 변수의 주소를
전달하는 것에 집중했어요
하지만 3가지 방법이 언급돼 있어요
재발견 포인터도 있어요 방금 배웠는데 간접 연산자를 별표로
표시하는 거예요
포인터에 저장된 값을 제공하죠 포인터∙∙∙ 포인터가
가리키는 곳을 가리키는 포인터 값은
이걸 사용하세요 포인터 주소도 가능해요
이건 포인터를 생성할 때 포인터가
저장되는 실제 주소예요
주소를 이용할 수 있고 포인터에 연산자가 있네요
포인터 연산도 할 수 있어요 이것에 관한 강의를 따로 듣죠
플러스, 마이너스 연산자를 이용해서 포인터에
정수 값을 추가할 수도 있고 포인터에서 정수 값을 뺄 수도 있어요
특정 표기법도 쓸 수 있고 메모리
작업도 빨라지죠 특히 배열을 쓸 때요
일반적으로 플러스 연산자를 이용해 포인터에
정수를 추가하면 해당
포인터의 주소를
여러 바이트로 설정하죠
포인터에서
숫자를 빼면 해당 주소를
특정 바이트로 설정해요
1로 줄일 수도 있어요 배열에서
사용하면 아주 흔한 방법이죠
배열에서 다음 요소로 이동하고 싶을 때 인덱스를 사용하는 대신 포인터를
이용해 1을 증가시키면 돼요
인덱스를 사용하는 대신 이전 요소로 이동하고 싶다면 1을
가리키며 그걸 감소시킬 수 있죠
포인터 산수죠 독점적으로 얘기할 테지만
포인터로 할 수 있는 거예요
이것도 표기법이에요 메모리 안에서 빨리 움직이며
효율적으로 할 수 있도록
해주죠
두 점의 차이점도 찾을 수 있어요
포인터가 2개 있고 요소들이 같은 배열이라면 요소들이
얼마나 떨어져 있는지 알아야 할
겁니다 요소들은 빼도 돼요
이게 2점
차이죠 여러분이 인덱스 5라면 인덱스 5에
포인터 포인터가 있고 인덱스 15에도 포인터 포인터가 있을
겁니다 그럼 마이너스 포인트로 요소들이
몇 개 떨어져 있는지 알
수 있어요 왜냐하면 16진수 주소에서 다른 16진수 주소를
빼고 연속 메모리 배열을 가정하면 얼마나 떨어져
있는지 보여줄 테니까요
관계형 연산자를 이용해 2개의 포인터를 비교할 수 있죠
포인터는 같은 형식이어야 해요 둘 다 정수를 가리키고 있다면 형식이
같아야 하죠
이건 주소가 더 큰지 적은지 알려주는 것 뿐이에요
배열의 어느 부분에 있는지 식별할 수 있어요
뺄셈도 두 가지예요
방금 뺄셈 얘길 했는데요
하나의 포인터를 취해 다른 것에서 뺄 수 있죠
그럼 포인터가
돼요 정수에서 포인터를 하나
빼도 포인터가 되죠
포인터를 하나 빼면 정수가 되고 포인터에서
정수를 빼면 포인터가 되죠 그 두 가지예요
몇 가지 예제를 더 살펴볼 거예요
포인터의 증가나 감소를 사용해 배열을 제한할
때는 조심하세요 여러분이 포인터 연산을 사용하는
대부분의 경우는 배열을 연산할 때예요
빨리 움직이게 해주거든요 간단한 구문이에요
하지만 배열 오류가 있다는 걸 기억하세요
너무 많이 증가하면 배열에서
벗어나게 되죠 너무 많이 줄이면 언더
경계가 되고 마이너스 숫자가 되죠
컴퓨터 자체는 포인터가 여전히 배열 요소를 가리키는지 추적하지 않아요
배열의 연산점을 이용할 때는 조심해야 해요
표현에 포인터를 사용하고 싶다면 그것도 할 수 있어요
이전에 말씀 드렸듯이 포인터를 레퍼런스로 수정하고 15를
추가했죠 20마리 잡은 것 같아요
파이 포인터 값은 연산식에서 사용할 수 있고요
정수 형식의 포인터로 정의된 변수는 정수
연산의 법칙을 이용해서 평가되죠
규제 운동으로 그걸 보여줬죠 변수와 포인터가
있다면 그걸 null로 초기화하고 그 변수의 주소를
포인터에 서명해요 그런 다음 *numumer +=25라고 하면 되죠
별이 가리키는 값을 얻는다는 걸 기억하세요
눔버 값은 눔버 더하기 25와
같다고 말해주죠
값은 35가 될 거예요
가리키는 것을 바꾸지 않습니다 별표를 이용하기 때문입니다 우리가 가리키는
주소도 바꾸지 않습니다 주소 자체도 바꾸지
않습니다 연산자의 주소가 없기 때문이죠
그게 가리키는 값을 바꾸고 있어요
숫자 변수의 값을 25로 증가하면 별표는 pnumum이라는
변수가 가리키는 내용을 묻는다는 걸 나타내요
포인터가 변수 x를 가리키면 그 포인터는
같은 데이터 타입의 포인터로 정의되죠
끝 포인터를 생성하면 부동 소수점 수 포인터 데이터
타입의 주소만 가질 수 있습니다
부동 소수점 수나 int요 여러분이 선언한 것에 따라서요
그래서 식에서 인다이렉션 연산자를 사용하는
건 같은 식에서 x를 사용하는 것과 동일하죠
부동 소수점 수를 쓰거나 int를 쓰거나 인다이렉션
연산자를 쓰면 탈장할 수 있어요 부동 소수점 수를
쓰면 뭐든 할 수 있죠 가리키는 대로요
int에 포인터로 정의된 변수는 모든 변수 유형의
주소를 저장할 수 있죠
int 값은 999입니다 int에 numumer를 할당할
수 있습니다 numer가 int에 포인터인 한
추가할 수 있죠
이 구문은 새 변수 값으로 작동되고
새 콘텐츠 값은 1024가 되죠
numum은 값을 가리키죠
값의 주소요 그래서 포인터가 값을 가리키게
되면 이 변수 값의 값을 바꾸게 되죠
포인터 한 지점에서 변수를 바꾸는 개념이란 걸 기억하세요
여러 변수에서 바꾸죠
포인터는 적절한 형식의 모든 변수의 주소를 포함할 수 있어요
포인터 변수 하나를 이용해 다양한 변수의 값을 바꿀 수 있죠 int나
2개 부동 소수점 수가 있다면요 포인터 유형과 호환되는 유형인 한
해당 값을 변경할 수 있어요 그게 부동 소수점 수를
가리키고 값이 정수라면 numers 값을
바꿀 수 없어요 당신은 못 할 거예요 컴파일 오류가 생기겠죠
여기 또 다른 예가 있어요 변수의 주소를 얻는
방법을 보여드릴게요 이 메인 화면에서는 실제
식에서 포인터 산수를
할 거예요
배열에선 아무것도 안 할 겁니다 따라서 저기서 수치화나 수치화 포인터를
안 할 거예요 값을 감소시키죠 2개의 긴 타입이 있죠
num1과 num2요 그리고 긴 타입의 포인터가 있어요 null로 초기화하죠
넘버원 범위에 넘버원 주소를 부여하죠
그리고 pnum 값을 L로 할당했어요
즉 num1이 2l이 된다는 거죠
만약 num2를 증가시키면 num2는 아직 아무 것도
하지 않았죠
pnum의 값은 2l이니 num1의 값은 2l이죠
L은 길이를 나타내는 접미사예요
긴 num2 값을 증가시키면 0l이고
1l이 될 거예요
num2 += *pnum이라고
하면 num은 2l이니까 1l + 2l이
되겠죠 num2는 이제 3l이 되고요
그런 다음 num2의 주소를 pnum으로 할당하면
pnum은 num2의 주소와 같아지죠
우리가 가리키고 있는 pnum을
증가시키면 이제 4l가 되죠
콜드블록 내부에서 살펴보겠지만
제가 설명할게요 Num1은 2l이 되겠죠
Num2는 같을 거예요 pnum을
가리키고 있죠
Pnum이 3L니까 3L로 예상하고 있어요
Pnum은 3l가 되고 pneum+num2는 6l가
될 거예요
제 생각이 맞는지 보죠 코드블록으로 가보죠
여기 코드블록이 있어요 손으로 해보려고 했는데
이제 맞는지 봐야죠 디버거를 사용하죠
여기 놓을게요 프로그램에서 값이 어떻게 바뀌는지 볼 수 있어요
그럼 이걸 빌드하고 디버그해보죠
이게 시작되고 10번 라인에서 멈출 거예요
워치 윈도우를 불러오면 워치에서
디버그를 할 수 있습니다 워치에서 값을
볼 수 있어요 지금 num1은 0이고
num2는 0이에요 pnum이 null을 가리키고 있죠
그래서 0x0인 거죠
null은 = 0이란 걸 아니까요
if 문에서 비교할 때 사용할 수 있죠
10번째 행을 실행하면 pnum이 num1 주소를 갖게 되죠
그냥 넘어가죠
pnum의 실제 주소는 0x60ff08이에요
11번째 행을 실행한 후에는 pnum 값이 2l이라고 설정해요 워치 윈도우에는
그렇게 설정된 게 없죠 하지만 실행해보죠 pnum은
여전히 같은 주소예요
Num1은 이제 바뀌었어요 Pnum이 Num1의
주소를 갖고 있었거든요 우리가 바꿨죠
이제 num1은 2예요 12번째 줄에선 num2를 증가시키죠
num2는 1l이 될 거예요
1이 될 거예요 P넘 주소가 그대로예요
이제 num2에 pnum을 추가할
거예요 Pnum remember는
num1을 가리키니 num2는 3이어야죠
14로 가면 num2는 3, num1은 여전히 2죠
라인 15에서 pnum에게 num2의
새 주소를 할당할 거예요 이게 끝나면 0x60ff08이
아니죠
우린 060ff04가 될 거예요
이제 pnum이 가리키고 있는 2라는 값을 증가하면
2는 3이고 2는 이제 4가 돼야 해요
4시인 게 보이죠
모든 값을 출력합니다 num1의 값은 2가 되어야
하죠
넘버 투의 값은 4가 돼야 해요
Pnum이 num2를 가리키고 있으니 여전히 4죠
그런 다음 4 더하기 pnum 값을 입력합니다 4인
num2는 8이어야 하죠
그래서 6번으로 틀렸을지도 몰라요
이제 감시 창에서는 그걸 볼 수 없어요 재생하고 printf문으로
결과물을 보죠 2448이 나와야 해요
비디버그 모드에서 실행해보죠
2-4-8이 보이네요
자, 됐어요 이해가 되셨길 바라요
돌아가서 하던 얘기 계속하죠
그럼 입력을 받을 때 어떻게 포인터를 사용할 수 있을까요?
지금까지 scanf를 써서 입력값을 입력할 때는 ampersand 연산자를 사용해야
합니다 float 데이터 타입일 때는요 그건 캐릭터 배열을 할 때만 가능했는데
그 이유는 캐릭터 A가 나중에 얘기할 캐릭터
포인터이기 때문이죠
하지만 scanf를 입력 값으로 쓰거나 문자 배열이 아닌 일반
데이터 타입에서 사용한다면 add나 sub연산자를 사용해야 해요
그 이유는 scanf의 두 번째 인수가 포인터이기 때문이죠.
주소를 보내야 해요 포인터는 주소 값만 있어요
이미 주소를 포함하고 있는 포인터가 있다면 scanf를 위한 인자로 포인터 이름을 사용할 수 있어요.
그래서 ampersand 연산자를 쓸 필요가 없어요. 당신이 포인터를
만들었다면 말이죠. scanf에 대한 두 번째 인수가 포인터이기 때문에
그냥 포인터 이름을 쓰면 돼요.
과거에는 값의 값을 저장하고 싶으면 scanf
두 번째 인수에 ampers_
value를 적었어요.
하지만 우리가 만든 포인터가 있고 이 예제에서
그렇듯 이미 주소를 가리키고
있다면 scanf를 사용할 때 우리가 말할 수 있는 건 값의 이름, 포인터의 이름뿐이에요
여기 정수가 있는 경우를 보여드릴게요. 정수에
대한 포인터가 있기 때문에 &를 사용할 필요는 없어요.
그냥 %d, p 값이라고 하죠
문자 배열 외에 변수의 정규명을 scanf에
전달할 수 있는 다른 방법도 있어요 포인터가 있다면요
나중에 입력한 것도 프린트할 수 있어요 그 값은 입력한
것으로 바뀌는 걸 보실 수 있습니다 해당 주소를
가리키고 있으니까요
널을 테스트해요
한 가지 꼭 기억해야 할 규칙이 있어요 아주 중요한 거예요
실책에 관해 얘기했는데 실책일 경우 어떻게 될까요?
비의도적 포인터 아무것도 가리키지 않는 포인터
메모리를 할당받지 않은 포인터요
이렇게 하면 어때요? 포인터를 선언했지만 실제로
초기화한 적은 없어요 그런 다음 그걸 할당하려고 하죠
참조를 취소하고 할당하는 건 큰 실수죠
두 번째 라인은 값 5를 Pt가 가리키는
곳에 저장하고 싶다는 뜻이에요 하지만 Pt는
아무것도 가리키지 않죠 초기화되지 않았으니까요
이제 pt은 임의 값을 갖게 되죠
5가 어디에 배치될지 알 수 없어요 메모리 어디에든 배치될 수 있죠
무해할지도 몰라요 기존 데이터나 프로그램의
기존 상태 변수를 과대평가할 수 있어요
프로그램이 다운될 수도 있어요 문제가 심각해요
여러분이 포인터를 생성하고 포인터를 저장하기 위해 메모리를 할당할 때 메모리는
데이터 저장을 위해 할당하지 않아요
인스타 pt을 통해 포인터에 대한
메모리를 할당하지만 메모리를 할당하지
않습니다 포인터, 주소의 값에 대한
메모리는요 포인터는 주소를 포함해요
포인터를 생성했지만 가리키는 것에 대한 메모리는 생성하지 않았죠
규제를 철회할 수 없어요
데이터를 저장하기 위해 메모리를 할당하지 않아요
포인터를 사용하기 전에 이미 할당된 메모리 위치를 할당해야 합니다
아니면 meloc을 이용한 동적 메모리 할당을 사용해야 합니다
기존 변수 주소를 포인터에 할당하거나 아니면 meloc을
동적 메모리 할당이죠
하지만 명심하세요 포인터를 사용할 때마다 여러분이 만들었는데
Null이라면 그 전에 Null인지 확인해야죠
그건 잠시 후 얘기할게요 포인터를 생성했는데 초기화가
안 됐다면 아무것도 할당하지 마세요 다른 변수를
가리키거나 초기화하거나 메모리를 할당하지 않는 한요
여기서 null이 사용되는 거죠
규제 오류가 없는지 Null을
테스트할 수 있어요 프로그램이 다운되지 않게 하세요
Null은 기본적으로 아무것도 가리키지 않는다는
걸 알죠 그래서 모든 변수와 포인터를 Null로 초기화해야
해요
항상 이런 걸 해야 해요
Null은 C의 특별한 기호로 일반적인 숫자의 포인터와
같은 0을 나타내죠
아래쪽은 0을 이용해 null에 포인터를 설정하죠
따라서 그걸 통과시키거나 null에 할당하기 싫다면 그냥 0이라고 하면 되죠 아무것도
가리키지 않는 것과 같다고 할 수 있어요
Null은 0과 동등하기 때문에 p
값이 Null인지 테스트하고 싶다면 명시적인 테스트를
사용하면 돼요 포인터는 null이라고 할 수 있죠
포인터가 null이면 0이란 뜻이에요
그럼 뭔가 할 수 있죠
0도 아니고 Null도 아니고 1 참이나 다른 값이면
크래시를 예방하는 방법이죠
규제를 실행하기 전에 메모리를
할당 받았는지 아닌지 확인하세요
따라서 탈환 전에 null을 확인해야죠 포인터요
함수에서도 종종 그렇게 해요 함수는
매개 변수로서 포인터를 갖거든요
호출자가 나쁜 데이터를
넘기거나 포인터를 넘기는데 메모리를 할당받지 않는다면 코드는
충돌할 수 있어요 여러분이 해야 할 일은 발신자가 유효한 주소를
갖고 있는지 확인하는 겁니다 그래서 null인지 확인할 수 있죠
기본적으로 if 체크가 항상 있어야 한다는
거죠 p 값이나 포인터가 아니라요
포인터가 아니라면 재발견할 수 있어요
조언에 대해 많이 얘기하고 있어요 많은 얘기를 나눴어요
감당하기 벅차실 거예요 잠깐 쉬면서 이전 강의를
들으면서 이 개념을 머릿속에 떠올려 보세요
포인터는 주소를 포함할 뿐이죠
하지만 세 부분으로 나뉘어서 문제가 될 수 있어요
긍정적으로 생각해요 정신 차리세요 혼란스러운
거 알아요
주소로 작업하는 것에 대해 얘기했었죠
가치에 대해 얘기했죠 포인터 자체를 다루는 법도 배웠고
포인터를 가리키는 변수를 다루는 법도 배웠죠
기억해야 할 게 너무 많아서
감당하기 힘들고 좌절할 수도 있어요
원인을 파악하는 건 늘 어려워요 연습을 많이 해야 하죠
이 개념을 이해하는 가장 좋은 방법은 지금까지 말씀드린
모든 개념을 사용하는
단편 프로그램을 연습하는 거예요
그러니 값을 포인터로 얻는 프로그램을
작성하세요 아마 간접 연산자를 써서 참회를
하겠죠 포인터를 이용해서 값을 바꾸는 프로그램을 만드세요
이게 탈의실이에요 포인터를 이용해 값을 얻는 것이 포인터의 이름이
되는 거죠 주소를 의미하는 포인터요 주소 프린트하기 모든 구문을 살펴봤죠
이걸 계속 반복해서 연습해야 능숙해질 수 있어요
자신감을 가져야 하고 숙달하려면 이런 개념을 세세하게
연구해야 해요
포인터에 관해 아직 할 얘기가 많지만 이쯤에서 한 발짝 물러나서 지금까지
얘기한 걸 복습하는 게 좋을 것 같네요 머리에 새겨두세요
고마워요