주의) 이건 초보자 강좌가 아닙니다. C / C++의 함수 부분까지 이해한 사람이 읽기를 추천드립니다.
안녕 개붕이들아
오늘은 C++ 기초중에 가장 ㅈ같은걸로 유명한걸 알아보자 ㅎㅎ
(참고로 C에서도 같은 원리이기 때문에 포인터가 어려우면 읽어보는걸 추천해)
바로 포인터야.
영어로는 Pointer 라고 하는데
사전에 있는것처럼 무언가를 가리키는걸 뜻해.
근데 뭘 가리키는걸까..?
바로 변수의 주소야.
주소가 뭔 소리냐고?
이 변수는 메모리 (RAM) 라는 공간에 저장돼.
이 메모리 공간에는 각각 번호가 붙어있고,
이 번호가 바로 변수의 주소값이야.
사진으로 쉽게 설명해보자.
이게 우리의 램 공간이야.
여기에 int age = 34;
라는 명령어가 실행되면 무슨 일이 일어날까?
컴퓨터는 여기서, 자동으로 램에 빈 공간을 찾아.
그 다음에 그 빈 공간에다가 34라는 값을 저장하는 거지.
봐바!
우리의 똑똑한 친구 컴퓨터가 0x01 ~ 0x04에 34라는 값을 저장했어!
age = 53; 같은 명령을 실행시켜주면 저 34라는 값이 53이라는 값으로 바뀌는거야!
(여기서 age 변수가 메모리 공간을 4개나 차지한 이유는, int 자료형의 크기가 4바이트여서 그래.)
그럼 포인터는 뭘까?
포인터 변수는 바로 주소값을 저장할 수 있는 변수야.
일단 예시로 한번 알아보자.
int* pointer = &age;
일반적인 변수 선언같았는데, 자료형 뒤에 별이 붙어있지?
이렇게 별을 자료형 뒤에 붙이고 변수 이름을 적어주면 포인터 변수가 생성돼.
즉,
가리키고_싶은_자료형* 변수이름;
형식이야.
그럼 이 변수에 넣고있는 값인 &age는 뭘까?
&는 레퍼런스 라고 불러.
사전에서 말하는 것 처럼, 참조라는 뜻이야.
근데 참조한다는게 뭔 소리냐고?
정말 간단해.
레퍼런스(&) 기호를 변수 이름 앞에 붙이면 그 변수의 주소값을 알려주는거야.
예를 들어서 std::cout << &age << std::endl; 이라는 명령어를 실행하면,
age의 주소값인 0x01 이 출력되겠지.
std::cout << age << std::endl; 을 했을 때 34라는 age 변수에 저장된 값이 출력되는거랑은 다르게 출력되는게 보이지..?
그럼 이 포인터 변수는 메모리에 어떻게 저장되는걸까?
int *pointer = &age; 를 실행 했을때의 메모리 상태를 한번 보자.
봐바!!
우리의 메모리 공간에 새로운 변수가 생겼어.
0x05 ~ 0x08 위치에 새로운 값이 들어갔네!
포인터 변수는 바로 특정 변수의 주소값을 저장하고 있는 변수라는거지.
그럼 이 포인터를 어따가 쓰는걸까?
한번 응용해볼게.
int age = 65;
int* p = &age;
std::cout << p << std::endl;
std::cout << *p << std::endl;
어라? 마지막 std::cout 명령어에서 출력하는 변수가 이상하지 않아?
포인터 변수 이름 앞에 * 을 붙여놨잖아?!
일단 출력해보자.
????
std::cout << p << std::endl; 에서는 012FFB84 라는 값이 출력되고,
std::cout << *p << std::endl; 에서는 65라는 값이 출력됐네??
아하, 즉 포인터 변수인 p를 그대로 출력하면 포인터 변수가 가리키고 있는 주소가 출력되고,
포인터 변수인 p 앞에 별 (*) 을 붙인 상태로 출력하면 포인터 변수가 가리키고 있는 주소에 있는 변수의 값이 나오는구나!
그럼 실험을 조금 해보자!
int age = 65;
int* p = &age;
std::cout << "age : " << age << std::endl;
std::cout << "p : " << *p << std::endl;
age = 34;
std::cout << "age : " << age << std::endl;
std::cout << "p : " << *p << std::endl;
처음 age 변수에 65라는 값을 넣어주고,
age 와 *p 의 값을 각각 출력해준 다음,
age 변수에 34라는 값을 넣어주고,
다시 age 와 *p 의 값을 출력하는 코드야.
실행해보면...
우와!
age 의 값이 변함과 동시에 *p 의 값도 변했어!
왜냐고? p 는 포인터 변수고, 이 p는 age의 위치를 가리키고 있거든,
즉 age 의 값이 아닌 위치를 가리키고 있기 때문에, age가 변하면 *p 의 값도 변하는거야!
그럼 또 다른 실험을 해보자!
int age = 65;
int* p = &age;
std::cout << "age : " << age << std::endl;
std::cout << "p : " << *p << std::endl;
*p = 34;
std::cout << "age : " << age << std::endl;
std::cout << "p : " << *p << std::endl;
이렇게 하면 어떻게 될까??
이번에는 age 의 값을 바꾼게 아니라, *p 의 값을 바꿔봤어,
한번 보자.
오!! 이번에도 값이 변했어!
여기서 알 수 있는건 바로,
*p = 34; 는 즉, p가 가리키고 있는 메모리 주소에 있는 값을 바꾸는거지!
그러기 때문에 age 랑 *p 랑 같은 값이 나오는거고!
근데 이걸 어디에 써먹냐고?
엄청 좋은 예시로 함수가 있어.
주의) 이 예시는 조금 어려우니 이해가 안되면 그냥 건너뛰고 그 밑에 있는 예시를 읽어보세요.
예를 들어서 "23:37:54" 라는 문자열을 시간, 분, 초로 잘라서 변수 3개에 넣는 함수를 만들어보자.
void cutTime(std::string time, int* hour, int* minute, int* second) {
*hour = std::stoi(time.substr(0, 2));
*minute = std::stoi(time.substr(3, 2));
*second = std::stoi(time.substr(6, 2));
}
어라 오류뜬다고?
당연하지! std::stoi 함수는 iostream 에 없거든!
#include <string> 을 #include <iostream> 아래에 적어주자!
여기서 알아갈게 조금 있는데,
time.substr() 은 std::string 변수에 기본적으로 있는 기능이야.
.substr(위치, 크기);
형식으로 사용하고, 문자열의 위치 위치에 크기 만큼 문자열을 잘라내는 기능을 해.
그래서 substr(0, 2) 해준거지 (0위치에 2길이의 문자열을 잘라서 return)
어쨌든, 이걸 실행해보자.
int main(int argc, char* argv[]) {
int hour;
int minute;
int second;
cutTime("23:37:54", &hour, &minute, &second);
std::cout << hour << std::endl;
std::cout << minute << std::endl;
std::cout << second << std::endl;
return 0;
}
함수를 위에 선언해줬으니, 저렇게 main 문에 적어주면 실행되겠지?
각 변수들에 무슨 값이 들어갔는지도 알아야하니깐, 마지막에 저렇게 출력문도 적어보자.
이 상태로 실행하면?!
오오 23:37:54 라는 문자열이 23, 37, 54 라는 각각의 숫자로 나뉘어서 hour, minute, second 변수에 각각 들어갔어!
근데 뭔가 예시가 어려우니 좀 더 쉬운 예시를 들고와볼게.
이번에는 함수에 값이 들어가있는 int 변수를 넣으면 그 변수가 두배가 되도록 해보고 싶어.
해보자.
void doubleInt(int* num) {
*num = *num * 2;
}
이제 main 문에
int a = 34;
doubleInt(&a);
std::cout << a << std::endl;
넣고 실행해보면...
34의 두배인 68이 변수 a에 저장되어서 나왔어!
어떤식으로 작동된건지 뜯어보자!
int a = 34; <- 여기서 a 라는 변수를 만들고 34라는 값을 넣어줬어.
doubleInt(&a); <- 이제 doubleInt 라는 함수를 실행 해주는거야, doubleInt 함수는 int 형 포인터 변수를 필요로 하니깐 a의 주소값을 보내줬어.
void doubleInt(int* num) { <- doubleNum 함수의 포인터 변수인 num 에 a의 주소값이 도착했어!
*num = *num * 2; <- 이 주소값이 가리키고 있는 변수(a) 의 값을 2 곱해서 넣어줘.
}
std::cout << a << std::endl; <- 이제 출력이야!
어때..? 이해 좀 됐어..?
나도 포인터라는 개념이 이해도 잘 안되고 어따 써먹는지도 영 모르겠어서 익히기가 엄청 어려웠었는데,
이거라도 읽고 좀 이해했으면 좋겠다.
궁금한 점이나 어려운점, 수정 바라는 부분이나 오류 있는 부분은 댓글로 달아줘!
읽어줘서 고마워
쿄코
이게 아마 내 프로그래밍 시리즈중 마지막이 되지 않을까...?
고라니당해서고자라니
재밌네 좋네 유익하네 잘가르치네 고마워 덕분에 몰랐던걸 좀 더 이해하고 감 ㅇㅇ
unconnected
ㅊㅊ
숲속을샅샅히
정성 ㅊㅊ
포인터 활용은 linked list 예제 어때?
쿄코
linked list는 구조체 개념까지 나가야돼서.. C++ 은 예제 짜기가 상당히 까다롭더라..
숲속을샅샅히
그럼 포인터 활용은 함수 인자 전달 방식 중 call by value와 call by reference로 인한 설명도 추가해줘
쿄코
그것들도 설명하고 싶었는데 call by reference는 나도 가끔 햇갈려서 공부 더하고 오겠음 ㅇㅇ
숲속을샅샅히
무조건항복
[이미 뇌가 오버플로우 해버린 댓글입니다.]
쿄코
뇌가 승희해버렸구나
Istari
잘 정리해서 ㅊㅊ
Istari
간단한 예제로 배열에 포인터 접근 정도 추가하면 좋을 것 같아!
3060ti
포인터에서 한번 벽느끼지 않나? 나만 그랬나 ㅜ
쿄코
프로그래밍에서 벽은 꽤 많더라..
첫번째 난관 - 반복문
두번째 난관 - 포인터
세번째 난관 - 파일입출력
네번째 난관 - 클래스
다섯번째 난관 - 템플릿
내가 지금 다섯번째에 있어서 그 다음은 모르겠다..
템플릿은 아직도 어따 써먹을지 감도 안옴..
댜기
반복문에서 난관 겪는 애들은 프로그래머로서의 재능이 없으니 하루라도 빨리 포기하길 추천.
컴공 나왔는데 그거 이해 못 한 애들은 90%의 확률로 다른 일 한다.
사비
포인터에서한번 링크드리스트에서한번..
이게 벽 수준이구 이 너머로 그냥 산이었음..
저장함보기
포인터는 처음엔 어려운데 한번 배우면 메모리 주소를 직접 참조가능하다는게 갠적으로 너무 편리함
쿄코
솔직히 포인터 없는게 프로그래밍 언어냐 ㅋㅋㅋㅋㅋ
JAVA : ?????
후교척
하지만 까보면 다 포인터 ㅋㅋ
빌런히어로
고맙다 개붕아
이 글 읽고 덕분에 구글입사했다
콘치즈콘
사실 포인터는 로우레벨쪽 다룰때 더 확 와닿지 예를 들자면 DWORD *Hpet = (DWORD*)0xFED00000; 뭐 이런거 말이지... 내가 로우레벨쪽으로 밥벌어먹고 살아서 그런지 그냥 메모리 맵에 매핑되는 기분으로 본다.
쿄코
그거 윈도우에서는 꽤나 위험한 짓이야..이미 다른 프로그램이 그 주소 사용중이라면... 으..
콘치즈콘
나는 OS보다 위에 군림하는 코드를 만지는 쪽이라서 ㅎㅎ 그리고 저 주소는 일반 프로그램이 손댈일이 없다. High Precision Event Timer base address 거든..
쿄코
콘치즈콘
mrquta
사실 윈도우는 보호모드를 사용하기 때문에 단순 주소지정으로 다른 프로그램이 사용중인 주소에 바로 접근할 수 없음. 그냥 접근하려고 한 주소가 할당되어 있지 않으면 지가 segment fault로 죽을 뿐...
망했떼
어셈 코딩하는 개붕이야?
콘치즈콘
어셈도 가끔 쓴다 ㅇㅇ
안나콤니니
컴공 뉴비라 강의 듣고 나서 관련 교수들 잡고 계속 질문해도 못 알아들어서 그냥 유야무야 넘겼었는데 가르치는게 우리 학교 교수진들보다 낫네 ㅋㅋ
쿄코
야호 개붕이 교수된다
멍청이
가긴 어딜가
이제 좌측값 우측값 유니버설 레퍼런스 설명해야지
쿄코
으앙
a = b;
에서 a 가 좌측값, b가 우측값 (?)
멍청이
b가 변수라면 b도 좌측값이지
어서 거지같은 c++의 깊고 어두운곳으로 개붕이들을 인도하란말이야
쿄코
그렇게 원한다면야...
arr[10]은 10[arr] 이랑 같음
멍청이
[ ]연산자는 *(x+y)니까 당연히 같겠지
쿄코
x = ( a > b ) ? 5 : 8;
이거 알지?
이거 lValue로도 사용 가능함 ㅋ
(x == 0 ? a : b ) = 1;
멍청이
어허 이놈 은근슬쩍 나한테 요상망측한거 가르치려들지 말고
얼른 우측값 레퍼런스를 주제로 다음강의를 준비하란말이야
쿄코
(๑˘・з・˘) 더 괴상한것도 많은데..
sizeof num; 이라 써도 오류가 안난다던지
void함수도 return; 이 가능하다던지
멍청이
void aaa(char *a, char *b) { while (*a++ = *b++) ; } 라고 써도 문제없이 돌아간다던지?
근데 그런건 별 상관없는데, 굳이 우측값 레퍼런스 아니더라도 포인터 다음에 배열 강좌정도 더 써보는게 어떰
쿄코
C++ 하면서 배열 쓴 적이 거의 없어가지고.. vector만 써서..
배열도 나쁘진 않은데 다음번에 쓰게 된다면 동적할당으로 해볼까 생각중이야
멍청이
그리고 new delete 강의하면서 스마트포인터까지 강의하게되는것이지
아주좋다
쿄코
아 스마트 포인터 잘 안써서 잘 설명할 자신 없는데...
나도 공부 많이 해야겠다...
쿄코
C++ 코드에 http:// 로 시작하는 사이트 주소를 넣어도 오류가 안뜸
망했떼
형 대학원생이야?
멍청이
떽 그런말하면 못써요
망했떼
gcc랑 gpp 차이점도 알려줘
왜 gpp로 c언어 컴파일이 되는거야
멍청이
c++이 c언어 슈퍼셋이라 그런거 아님? 컴파일러는 잘 몰라
쿄코
맞아
망했떼
형냐 이제 시간복잡도도 간단하게 설명해줘
피보나치를 예로 드는거야
쿄코
미안 시간 복잡도는 나도 도저히 이해가 안돼
어쩐지 나가는 코테마다 시간 초과 뜨더니만은...