먼저 대학에서 현제 자료구조 수업을 수강하고 있는 대학생이라
지식의 깊이가 부족할 수 있음을 양해 드림.
난 지금 학교에서 자료구조 수업을 듣고 있는 대학생이야.(2학년)
학교 수업 교재부터 윤성x의 열x 자료구x 같은 책을 보면
포인터를 초기화 시키는 함수를 만들어 놓고
밖에서 동적할당을 하고 해당 포인터를 초기화 함수의 실인자로 집어넣는것을 볼 수 있는데
초기화 함수를 만들어 놓고도 메모리 할당과 메모리에 값쓰는걸 따로 하는것은 꽤 별로지
사실 이는 이중포인터를 쓰면 쉽게 해결할 수 있는 경우야
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | typedef struct node{ int data; struct node* next; }node; void init_list(node** sentinel){ *sentinel = (node*)malloc(sizeof(node)); (*sentinel) -> data = 0; (*sentinel) -> next = NULL; } void single_init_list(node* sentinel){ //sentinel = (node*)malloc(sizeof(node)); //동적할당을 미리 다른 함수에서 해놓아야함 sentinel -> data = 0; sentinel -> next = NULL; } int main(int argc, char* argv){ node* single_test = (node*)malloc(sizeof(node)); single_init_list(single_test); node* double_test; init_list(&double_test); } | cs |
코드를 예로 좀 들어볼께 non-circular 단순 연결리스트와
그에 해당하는 node를 예로 들어볼께
이중 포인터를 사용하는 함수 init_list 와
그냥 포인터를 사용하는 함수 single_init_list가 있어
먼저 main함수를 보자
이중 포인터를 사용하면 double_test라는 포인터를 선언하고
그 주소를 한번에 초기화 함수의 파라미터로 넘길 수 있지?
근데 single_test부분을 봐 먼저 동적할당을 해준 이후에
single_init_list함수에 인자로 넘기는 것을 볼 수 있어.
동적할당을 해줘야만 함수인자로 넘겨서
함수에서 포인터에 관련된 여러 연산을 수행하기 때문이지
여기서 질문
왜 single 포인터를 사용할떄는 동적할당을 꼭 해줘야 할까?
왜냐하면 싱글포인터의 경우에는 처음에 초기화 없이 선언하면 쓰레기 값을 가지겠지?
초기화 안한 변수는(포인터도 변수니까) 함수에 파라미터로 넘겨주면 안되잖아?
쓰레기 값이 들어 있을테니 (여기선 포인터니까 어떤 메모리 주소를 가리킬지 모르잖아)
그래서 main함수에서 동적할당을 따로 하고 넘겨줘야되
이제 double_test를 볼까?
double_test도 single_test와 마찬가지로 single 포인터로 선언을 했어.
double_test의 사용을 보면 초기화 없이 주소값만(포인터 자체의 주소값만 포인터가 가리키는 주소값 아님!)
넘기는것을 볼 수 있지?
그러면 init_test함수에선 포인터 자체의 주소를받아와서 역참조를 통해서 동적할당을 해주고 있어.
여기선 왜 초기화도 안해줬는데 문제 없이 동작할까?
그 이유는 double_test를 선언할때 double_test자체의 주소는 쓰레기값이 들어가는게 아니라
프로세스가 실행된 이후에 배정된 메모리 공간에 맞게 배정을 받기 때문이야 (지역변수랑 마찬가지야!)
그러니까 double_test의 주소를 참조해서 double_test에 대해서 동적으로 메모리공간을 할당해줘도
문제가 없는거야
이렇게 짜면 뭐가 좋을까?
자료구조를 짜는데 더욱더 일반적으로 짤수가 있어
어짜피 초기화를 하는데 할당이랑 데이터 초기화랑 분리하는것보단
한번에 하는것이 깔끔하지.
학교에서 수업하는 교수님도 아무 말씀 없으시고
주변에 같이 공부하는 친구들한테도 말해봐도 잘 모르는경우가 태반이라
혹시 지금 C나 자료구조 배우고 있는 갤러들있으면 조금이나마 도움되라고
내가 공부하다가든 생각 적어봤어 ㅎㅎ
어려워 ㅠㅠ
저것도 일반화라고 부를 수 있냐
선개추 후감상
Node* new_list(...) 이렇게 해서 반환값으로 받는 건 어떰 안에서 할당 초기화해주고
정성글추천 ㅋ
그리고 에러시에 NULL 포인터 반환하고... 나중에는 del_list(Node*) 함수로 메모리 해제하는 거임
윤성우 책 코드가 영 구리긴 함
츄럴// 그것도 좋지 내가 한말의 요는 함수안에서 할당을 해주자는 얘기니까. 이중포인터는 그냥 방법중 하나 - 민소하게 더 민소민소하게!
윤성우 책 코드 이상해 ㅋㅋ - 민소하게 더 민소민소하게!
그 코드 스타일 따라할까봐 두려움
사실 Node를 초기화하는 함수도 따로 만들고 리스트 초기화시에 호출하는게 더 낫다고 생각함.. 과제 수준에선 과한 추상화일수도 있지만 규모가 좀만 더 커지면 그게 나을 듯
츄럴말 대로 해도 좋겠네 저것도 노드 초기화 해주는 함수로 바로 적용할 수 있어 꼭 sentinel에만 쓰는게 아닐 일반 node에 대해도 그냥 선언하고 넘기면 되니까 값받아서 data에 넣어주게 함수 바꾸고 insert함수에서 호출해서 앞노드 뒷노드 연결만 잘해주면 될듯
똥냄새
지랄 뭔 똥냄새 ㅡ.ㅡ - 민소하게 더 민소민소하게!
이게 포인터의 포인터로 넘어간다니까 머리가 돌아버리는거임
주소를 따서 넘긴다는걸 포인터로 볼 수도 있지만 이 변수에 대해 사이드 이펙트를 일으키기 위해 &를 붙여주고 안에서 *로 읽어서 수정한다 이런느낌이면
첫번째 포인터는 메모리 주소를 뜻하는 의미에서 진짜 포인터고 겹포인터는 그냥 변경을 일으키기 위해 넣은 의미가됨 레퍼런스로 넘기면 간단한데 C는 그게 안되니까
실제로 위 코드에서도 센티널에선 모두 앞에 *가 붙으니까 레퍼런스로 보낸 의미가 됨
이거랑은 좀 다른얘긴데 콘셀구조라는게 이씀 nullptr도 리스트로 보고 생성자는 어떤 리스트 앞에 값 하나를 붙이는것만 있으면 최초의 리스트 nullptr에 값을 계속 붙여주는걸로 리스트를 만들 수 있는데 가만생각해보면 이건 생성과 동시에 초기화되면서 뒤에 붙는 노드에는 변경이 없으니까 이중포인터 없이도 리스트 갖다붙이는게 가능
레퍼런스 의미로 사용된게 맞지 여기서는 메모리 직접접근의 의미를 살리기 어렵지. 콘셀구조는 처음들어보는데 한번 알아봐야겠다 - 민소하게 더 민소민소하게!
https://en.wikipedia.org/wiki/Cons
이것이 콘셀이다 희망편
고마워요 ㅎㅎ sicp2장에 나오는 내용들이네 읽어야 되는데 바빠서 요즘 진도를 못나갔는데 주말에 좀 봐야겠네 - 민소하게 더 민소민소하게!
??? 옛날부터 궁금했는데 이게 어려워?
당연한 내용 아니냐????
c에서 포인터가 어렵다고들 하는데;; 왜????????
나도 왜 어려운지 모르겠다 근데 학부 1,2학년들 헷갈려하는 사람 많아서 적어봤어 - 민소하게 더 민소민소하게!
같은 2학년인데 흑흑 ㅠㅠ 좋은글 고마워
나도 갑자기 이것저것 해보다 생각이 들어서 ㅎㅎ 부족하지만 프갤에 생각날때마다 써볼려구 - 민소하게 더 민소민소하게!
걍 생성자에서 하자 - dc App
c기준 - 민소하게 더 민소민소하게!