관리자님
같은 질문이고 비슷한 질문일 수 있는 점 미리 죄송합니다..
①
unit 82
p.1030 완성된 소스 코드에서
//프로그램에서만 사용하는 구조체
typedef struct _ARCHIVE {
ARCHIVE_HEADER header;
FILE *fp;
FILE_NODE fileList;
} ARCHIVE, *PARCHIVE;
이렇게 정의를 하였는데
그 다음 p.1031를 보면
PARCHIVE archive = malloc(sizeof(ARCHIVE));로 메모리 할당하고
//아카이브 파일에 아카이브 헤더 저장
if (fwrite(&archive->header, sizeof(ARCHIVE_HEADER), 1, fp) < 1)
라고 되어 있습니다.
&archive -> archive로 &를 제거해야 맞는 거 같은데
왜 앞에 &를 쓰는지 모르겠습니다..
②
비슷하게
p.1030에 완성된 소스 코드에서
//프로그램에서만 사용하는 구조체
typedef struct _FILE_NODE {
struct _FILE_NODE *next;
FILE_DESC desc;
} FILE_NODE, *PFILE_NODE;
라고 정의가 되어 있습니다.
하지만 p.1032에
PFILE_NODE node = malloc(sizeof(FILE_NODE)); 메모리 할당하고
//파일 정보 읽기
if (fread(&node->desc, sizeof(FILE_DESC), 1, fp) < 1)
라고 되어 있습니다.
이것 또한 &node -> node로 &를 제거해야 맞는 거 같은데
왜 앞에 &를 쓰는지 모르겠습니다..
③
unit 83
p.1069에서
char *readFile(char *filename, int *readSize)로 함수를 정의하고
p.1071에
char *doc = readFile("example.json", &size);
이렇게 &size를 지정한 이유가
pointer to == address of 로 타입을 맞춰주기 위함이 맞을까요??
④
그렇다면
p.1070에
void parseJSON(char *doc, int size, JSON *json)로 함수를 정의하고
p.1071에
parseJSON(doc, size, &json);으로 하는 이유도
pointer to == address of 로 타입을 맞춰주기 위함이 맞을까요??
⑤
그리고 그 밑에
freeJSON(&json); // json 안에 할당된 동적 메모리 해제
라고 되어 있는데
freeJSON(json);이 아니라 왜 freeJSON(&json);인지 궁금합니다..
//프로그램에서만 사용하는 구조체
typedef struct _ARCHIVE {
ARCHIVE_HEADER header;
FILE *fp;
FILE_NODE fileList;
} ARCHIVE, *PARCHIVE;
이렇게 정의를 하였는데
그 다음 p.1031를 보면
PARCHIVE archive = malloc(sizeof(ARCHIVE));로 메모리 할당하고
//아카이브 파일에 아카이브 헤더 저장
if (fwrite(&archive->header, sizeof(ARCHIVE_HEADER), 1, fp) < 1)
라고 되어 있습니다.
&archive -> archive로 &를 제거해야 맞는 거 같은데
왜 앞에 &를 쓰는지 모르겠습니다..
---------------------포인터로 할당하든 말든 archive->header로 접근하면(질문에는 왜 archive->archive??)
구조체 정의는?
typedef struct _ARCHIVE {
ARCHIVE_HEADER header;
FILE *fp;
FILE_NODE fileList;
} ARCHIVE, *PARCHIVE;
여기서 보면
ARCHIVE_HEADER header;
이니까 이건 값이고
archive->header로 접근한 결과는? 타입이 ARCHIVE_HEADER이고, 이건 값이죠. 포인터가 아닙니다.
fwrite로 써야 하는 위치는? 주소로 알려줘야 합니다. 그러니
&archive->header입니다.
전부 다 동일 질문이고, 타입 이해 못하고 있는 것입니다.
그냥 그런가 하고 무시하고 넘어가세요.
& 제거하고 컴파일하면 에러 나고 결과도 동작하지 않을 겁니다.
함수 인수에 포인터(또는 & 붙여서) 전달해야 하는데 값이니까 & 붙인 겁니다.
&를 없애야 할 것 같다면 없애 보고 컴파일 에러나 경고문 보고, 경고 뜨고 컴파일되면 동작이 제대로 안 되고 맛이 가는 걸 보면 됩니다.
예제가 맞습니다.
자꾸 예제가 틀린 것 같다라는 의심보단 예제가 맞고, 내가 뭘 이해 못하지? 관점을 바꾸세요.
아래 다른 분의 2차원 배열 행렬에서 가로, 세로 거꾸로 쓰는 게 맞냐... 그러는데...
자기 생각이 맞다는 전제 깔고 질문하면 배우기가 어렵습니다.
내가 모르는 게 맞고, 모른다는 전제하에 이게 왜 맞을까? 이렇게 고민해야 배웁니다.
가로, 세로 거꾸로 쓰는데, 다른 책은 그렇게 안 하던데요? 처럼 나는 모르는데, 두 책이 서로 다른 얘기를 합니다! 이런 게 더 좋은 질문입니다.
그러면 두 책 중에 하나는 틀렸을 테죠.
'가로, 세로라고 쓰는 게 맞지, 왜 이 책은 세로, 가로라고 설명해?' 하는 질문은 전제가 "내가 올바르고, 책은 틀렸다" 같은 거라서요.
struct node {
int data;
struct node* next;
};
typedef struct node Node;
Node* create_node(int data) {
Node* new_node = (Node*)malloc(sizeof(Node));
new_node->data = data;
new_node->next = NULL;
return new_node;
}
간단하게 연결 리스트 만들었다고 봅시다.new_node->data의 타입은 뭔가요?
int data이니까 data는 값이네요. new_node->data의 타입은 int이니까 정수를 대입할 수 있습니다.
하지만, 만일 scanf로 data에 값을 넣고 싶다면요?
scanf("%d", new_node->data);
이렇게 하면 동작하나요?
값인데? 데이터를 쓸 주소지가 필요하죠? 왜요? scanf는 값을 쓸 메모리 주소를 받으니까요. 포인터이죠. 그러니까 숫자 입력 받을 때는 변수명 앞에 &를 쓰라고 하죠. int가 아니라 address of int로 타입을 쓰라는 겁니다. 포인터 가르치기 전에는 설명하기 곤란하니 & 쓰라는 거고...
scanf("%d", &new_node->data);
이렇게 써야 하죠.
이것도 buffer는 값을 쓸 메모리 위치죠. 포인터이니까요.
new_data->data에 정수 하나 쓰고 싶다고 결정했을 때도 마찬가지이죠.
fwrite( &new_data->data, ...)
data가 int이니까 int 하나의 값만 써야 합니다.
new_data->data로 쓰면 int이고 이건 그냥 값이니까 에러입니다.
*buffer이니까 값을 쓸 메모리 위치를 지정하는 것이니
&new_data->data로 쓰는 겁니다.
타입 시스템을 이해하면 나머지는 전부 다 type match 문제입니다.
어떻게 하면 타입 시스템을 이해해요?
해당 변수가 가리키는 타입의 이름을 영어로 읽으세요.
struct node {
int data;
struct node* next;
};
node의 타입은? struct node입니다.next의 타입은? pointer to struct node입니다.
data의 타입은? int입니다.
struct node a;
a의 타입은? struct node입니다. 이건 값입니다. 값이란 그 안에 data, next 멤버도 있는 값이죠.
struct node *b;
b의 타입은? pointer to struct node입니다. 값이 아니죠. 메모리 위치를 가리키는 포인터죠. 그 메모리 위치에는 struct node가 저장되어 있어야 합니다. int, long, 다른 타입이든 뭐든 저장되어 있으면 안 됩니다.
편지봉투에 쓰인 주소로 가면 엄마가 있다고 했는데, 갔더니 아빠가 있으면 곤란하잖아요.
a.data의 타입은? int입니다.
b->data의 타입은? int입니다.
&a.data의 타입은? address of int입니다.
&b->data의 타입은? address of int입니다.
이런식으로 타입을 읽어보세요.
지금까지의 질문은 공통점이 몽땅 다 타입을 못 읽는 겁니다.
타입을 읽을 생각을 하지 않고, fwrite 함수에서는 인수가 이렇게 쓰는 거야? 이건 포인터로 써? 이건 & 붙이는 거야? 안 붙여야 되는 거 아냐?
함수 인수 하나하나마다 타입을 이해하지 않고 자기만의 가설, 시나리오, 상상력을 덧붙이고 있죠.
기본 전제: "내가 옳다. 예제와 책이 틀렸다"
이 상태인걸요.
학습자의 전제: "나는 무지하다. 예제와 책이 일단 맞다고 생각해고 배운다"
태권도, 킥복싱, 유도 등... 무엇이든 '형'이 있습니다. 형식을 제대로 배운 다음에 나만의 형을 세우거나 해야죠. 형은 배우지도 않았는데, 이미 "나만의 C 언어 해석법"을 내세우고 있는 형국입니다.
void freeJSON(JSON *json)
에서 json 인자의 타입은? pointer to JSON이죠.
main() 함수에서 선언한 json 변수의 타입은?
JSON json = {0, };
json의 타입은? JSON이죠.
freeJSON(json)이라고 쓰면요?
함수는 pointer to JSON인데, 내가 쓴 인수의 타입은 JSON이죠.
그러니 둘은 type mismatch이고...
타입 시스템 이해도가 전혀 없습니다. UNIT 33 이후에 포인터부터 예제만 보면서 각 변수, 함수 인자의 타입을 다시 읽어보세요. type match인지, type mismatch인지.
타입 매칭을 이해 못하면 예제를 보거나 책을 봐도 의미가 없습니다. C 언어 책을 잠시 덮어두고 <파이썬 코딩 도장> 같은 언어를 공부하는 게 낫습니다.
다른 언어를 공부하면 시야가 넓어지니까요.
자바를 공부해도 좋지요.
UNIT 82, 83이면 거의 다 학습한 거고, 책 끝까지 볼 필요 없습니다. 타입 시스템 이해가 안 되면 나머지도 의미가 없어요. 안 봐도 되는 예제들이고요.
그 시간에 파이썬, 자바 입문서 사서 공부하고, 시야를 넓히세요.
궁극적으로 C 언어로 밥벌이 할 거도 아닌데 타입 이해 못하면 어때요. 다른 언어로 코딩 왕창 하다 보면 어차피 배우게 되어 있습니다. 굳이 어렵게 C 언어로 타입 시스템 이해할 필요 없습니다. 파이썬이나 자바에서도 충분히 이해할 수 있습니다. Go, Rust 같은 현대 언어는 타입에 더 엄격합니다. Rust는 아예 컴파일도 안 시켜주죠. Rust 도전해봐도 좋지요.
C/C++/Java 언어는 중괄호 위치로 개발자들끼리 싸우지만, Go 언어는 다 에러 처리하고, 자동 포맷하고, 들여쓰기는 공백 4칸이다. 이거 외에는 다 틀려... 중괄호는 이렇게 쓴다. 나머지는 다 틀려.
Rust 언어는 타입 불일치네? 컴파일 안 해. 여기 설명 친절하게 컴파일러가 1페이지씩 출력했으니까 읽고 이해하고 다시 코드 작성해와.
C처럼 관대한 언어로 타입 시스템 이해하려 하지 말고(왠만한 건 다 통과시켜주는 게 C 언어), Java로만 가도 컴파일 안 되고, Go로 가면 더 엄격해지고, 끝판왕 Rust로 가면 타입 시스템 이해 못하면 코딩도 하지 마! 하는 태도이니까 각자 다 접해보시길...
C로 시간 낭비 안 해도 됩니다. 다른 언어 섭렵하세요.
매번 올리는 질문 내용들이 거의 다
type match / type mismatch 내용들이네요..
정성껏 올려주시는 답변을 여러번 보고 다 이해했다고 생각해서
넘어가도 또 막히는 것을 느낄 때마다 답답한데
항상 이렇게 친절히 답변주셔서 감사합니다..
말씀대로 type match / type mismatch 생각하면서
다시 복습해보겠습니다.
다른 언어들도 조만간 시작하면서 시야를 넓혀보겠습니다.
감사합니다!!
https://github.com/codecrafters-io/build-your-own-x
언어 문법 간단히 배우고 자신의 언어에서 할 수 있는 기초 주제 하나씩 골라서 따라 해보면 됩니다.
1. 따라하기.
따라 하면서 잘 만들기. 완성하기.
2. 고치기
1을 끝냈으면 나만의 기능 추가는 뭘 할 수 있지? 하나씩 고쳐보기.
1은 코드 형식의 이해이고, 2는 동작 원리의 이해가 깊어지는 단계입니다. 보통 진짜 내 것이 된다는 건 2번입니다.
문법, 자료구조, 알고리즘보단 하나를 제대로 만들어보는 경험이 더 좋습니다.
학습법은 두 가지가 있습니다.
1. 로봇 조립 방식
대학교의 공부는 보통 로봇 조립 방식입니다. 운영체제, 컴파일러, 프로그래밍 언어론, 자료구조, 하드웨어, 알고리즘 등등 각 부품을 학습하고, 마지막에 이걸 다 조립해서 로봇을 완성하는 과제를 줍니다.
2. 성장형 방식 = 롤플레잉 방식
롤플레잉 방식은 게임에서 캐릭터를 키우는 것과 비슷합니다. 공격력이 약하면 공격력을 키우는 것, 방어력이 약하면 방어력을 높이는 것. 각각의 상황에 따라 선택하는 것이죠. 개개인의 수준에 따라 맞춤형으로 학습하는 것이죠.
프로그램을 하나 만들어보고 부족한 부분이 생기고, 이걸 학습해야지! 하고 공부하면 좋습니다.