주의) 이건 초보자 강좌가 아닙니다. 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; <- 이제 출력이야!
어때..? 이해 좀 됐어..?
나도 포인터라는 개념이 이해도 잘 안되고 어따 써먹는지도 영 모르겠어서 익히기가 엄청 어려웠었는데,
이거라도 읽고 좀 이해했으면 좋겠다.
궁금한 점이나 어려운점, 수정 바라는 부분이나 오류 있는 부분은 댓글로 달아줘!
읽어줘서 고마워
망했떼
아니면 포인터 설명했으니까 파이썬의 변수 특징 설명해보는건 어때
난 파이썬 변수가 재밌더라구
쿄코
어딜 드러운 구렁이를 대려와!!
신성한 C++을 해야지 쯧쯧
망했떼
힝 ㅠㅠ
그럼 gcc랑 gpp 차이점좀 알려줘
왜 gpp로 c언어 컴파일이 되는거야
쿄코
gcc - GNU
gpp - ATOM (MIT LICENSE)
C에서 가능한건 전부 C++에서 가능해서 cpp 컴파일러에서 C가 돌아가는거야.
C에다가 괴상한것들 추가한게 C++이거든..
박음직스럽다
으으으으으디 신성한 C 앞에서 클라스 따위를 들이밀어!!
멍청이
그리고 하스켈을 팔아먹는것이지
쿄코
다음은 올리디버거를 이용한 디스 어셈블링으로...(?)
망했떼
아이다 프로 써달라고 ㅋㅋㅋㅋ
쿄코
아 아이다 디자인 구려 ㅋㅋㅋㅋㅋ
망했떼
아이다 디컴파일러 최고라구
섭탱
끝까지 연재해 주십시오 휴먼
혹시 책 추천 가능하십니까 인강이나 유튜버나 C++은 너무 없어오
밀덕
열혈 C++
쿄코
열혈 C++ 사놓고 안일었어
난 책이랑은 안맞더라...
C는 학원에서 기초정도만 배웠고,
C++ 은 C랑 비슷해서 독학으로 가능했음..
일째 밤샘중
Effective c++, more effective c++
Stl 도서 찾아보시고
Modern c++ 책 보시고
댜기
그냥 아무 기본서 하나 읽고 그 다음이 C++ 기초플러스, 그 다음이 이펙티브 C++
바보똥개
뭔말인지 알겠당 ㅎㅎ
쿄코
바보똥개
스크립트 언어쓰는데
c++공부할꺼얏 히히
쿄코
"오늘도 사탄 한명이 일을 잃었습니다."
숨은음은
근데 배열 설명 없이 포인터만 달랑 설명하면 나중에 배열 얘기할 때 헷갈려하지 않게서?
이스F91
아니 왜 고생길에 들어올려고 하는거야? 하지마 하지마...
C/C++에 익숙해지면 다른 프로그래밍 언어 속 터져서 못써
쿄코
어셈블리 : 나 불렀어?
댜기
뭔 말도 안되는소리냐, C/C++ 10년 넘게 했지만 개발 속도에서 1년 한 C# 에 반도 안나온다. 절반 나오면 빠른거다. 보통 10~20% 수준.
그냥 C/C++ 가 필요한 영역들이 있다.
이스F91
오~10년 넘게 했으면 알건 알겠네.
이 분야에서 그런사람 요즘 좀처럼 찾기 어렵던데 반가워.
내가 얘기하고 싶었던건 포인터 덮어쓰기 같은걸 통해서 struct -> struct 변환 같은건 어지간한 언어에서는 찾기 힘들어서 한 얘기였음.
거기에 함수포인터를 통해서 run time에 실행 모듈을 바꿔치기 하고....뭐뭐, 그런것들 말이지.
그런거 쓰다보면 다른 언어는 전문 같은거 파싱할때 속터지더라.
C#은 가끔 테스트용 클라이언트를 만드느라고 쓰는데 생산성은 좋은데 답답한 면이 많더라고.
여튼, 말을 너무 짧게 했나 싶은데.... 댓글에 줄줄줄 길게 쓸 수 없잖아?
a1s2d3f4g5
다른언어엔 없는 c++기능이란건 대부분 다른 언어를 c++의 사고방식에 갖혀서 생각해서 그럼
gogogog
메모리 시작 주소가 0x00이 아니라 0x01이라 굉장히 불편함
쿄코
ㅋㅋㅋㅋㅋㅋㅋ
사실 컴퓨터 메모리처럼 4바이트로 하려고 했는데 그러니 너무 길어지더라..
살애기
Int는 왜 4byte 일까? 요것부터가 중요하다.
보라돌이얌
거야 int를 만들고 정의할땐 32bit cpu였으니깐
살애기
근데 16비트 시절에는 int가 2바이트였엉
보라돌이얌
으엉?
살애기
16bit시절엔 주소값이 크기가 2바이트 32bit시절엔 4바이트이고 그게 int의 값이 되고...
또 cpu의 비트수만큼 변수의 크기를 정하는게 효율적이고
보라돌이얌
아아..... 이제야 알겠군
첫 댓글이 의문이 아니었구나
난 첫 댓글이 물어보는건지 알았음
잠적자
응 러스트나 고 할거야~ 틀따라라딱딱언어 안쓸거야
쿄코
생각만 하고 가라 글 싸지르고 가지 말고
댜기
포인터 이해 못 했으면 초보자 강의가 맞음
지옥에서몸부림쳐라
쿄코
포인터 변수는 값을 담는 변수가 아니라, 값을 담는 변수의 위치를 담는 변수야.
가장 많이 사용하는 경우가, 내가 예시로 든 것 처럼 한 변수를 다른 함수에서도 값을 바꾸거나 하도록 짜는 경우가 있거든,
예를 들어 두 수의 합과 차를 두 변수에 나눠서 저장하는 함수가 있다고 하면,
void sumdif(int a, int b, int* sum, int* dif) {
*sum = a + b;
*dif = a - b;
}
이렇게 짜주는거지.
그러면,
int sum, dif;
sumdif(5, 3, &sum, &dif);
이리 해줬을때,
변수 sum에는 8이 들어가고, 변수 dif에는 2가 들어가지!
승냥
사용법에 대한 쉬운 예시는 아웃풋을 2개이상 받고싶을때 쓰는것
쿄코
참고로 tuple 쓰면 가능함
auto func() {
return tuple(30, 53, 11);
}
int a, b, c;
auto[a, b, c] = func();
승냥
오우쉣 c++에는 그런게 있네 c만 하다보니...
안드로이드박사
야 설명 잘한다 재밌네
그레이의4000가지그림자
크립토커런시트레이더
히히ㅣㅎhtml 짱 javascript 짱 완벽한 언어 html
쿄코
Please
일째 밤샘중
이쯤되면 컴고수
멍뭉멍
자 이제 포인터을 했으니 클래스와 객체지향 자료구조까지 해주세요!
레게강같은붐업
망할 포인터... 모든 구조체와 배열 클래스가 이것 땜에 난이도 제곱됨.
쿄코
구조체는 왜?
mas
착한 컴공과 1학년은 자바와 파이썬이나 준비하러 갑시다