- 구조체 선언 시에는 초기화가 불가능하다.
visual studio에서는 가능했었던 것으로 기억하는데, GCC를 이용해서 컴파일 할 때에는 이게 안된다.
- 함수(기능)별로 구분하고 그에 해당하는 기능들을 하나씩 추가해서 테스트 우선해보는 방식으로 작성하는 것이 좋다.
- Call by value, Call by reference를 구분해야만한다.
C언어는 공식적으로 Call by value만을 지원한다. 그리고 Call by reference를 pointer를 통해서 흉내낸다.
다른 언어보다 C언어에서 이 문제가 더 중요하고 더 헷갈리는 것 같다. 이 문제 때문에 몇 일을 버렸는지...
- 다차원배열과 포인터 표현
이게 컴파일 에러에 영향을 끼친 것은 아니어서, 그저 추가적인 공부가 되었다. 메모리의 구조가 다차원배열과 다중 포인터가 다르다는 것을 알 수 있었다.
배열과 포인터의 차이
배열은 길이정보와 그 주소를 포함한다. 포인터 변수는 단지 포인터로서의 정보(주소)만 담고 있다. 따라서 배열을 포인터형으로 변환하면, 길이정보를 잃는 것이다.
다차원배열과 등가 포인터
2차원 배열의 경우 arr[5][5] = (*ptr)[5]
이 같다. 이는 5개의 인덱스를 가지는 배열을 통해 인덱스를 채우는, 5개의 인덱스를 가진 배열 혹은 포인터이다. *ptr[5]
는 주소를 담는, 5개의 인덱스를 가진 배열을 의미한다.
2차원 배열의 경우 char arr[5][5]
에 대해서 arr + 1
로 인덱싱한다고 하면, arr
의 주소에서 1만 추가되는 것이 아니라, arr
주소에서 그 다음의 인덱스인 arr+ 5
의 주소로 인덱싱된다.
참고할만한 링크(다 읽어보는 걸 추천)
http://pelex529.blogspot.com/2009/01/2.html
https://mynameisdabin.tistory.com/32
https://mynameisdabin.tistory.com/9?category=786516
https://pridiot.tistory.com/217
https://kookyungmin.github.io/language/2017/08/11/C04/
https://skmagic.tistory.com/67
- 그 외의 포인터 문제들
포인터와 문자열 문제
name은 포인터일 뿐 문자열을 저장할 수 있는 곳이 아니다. 즉 아래와 같은 방법을 사용하려면 포인터가 아닌 배열로 선언해야한다. 혹은 동적할당을 통해서 따로 메모리를 배정받아야한다. name에 문자를 저장할 수 없기 때문에 scanf에서 에러가 발생한다.
char* get()
{
char *name;
printf("Input name : ");
scanf("%s", name);
return name;
}
또 이런 것도 에러.
char* get()
{
char *name = "abc";
printf("Input name : ");
scanf("%s", name);
return name;
}
이와 같이 할 경우는 우선 메모리 어딘가에 "abc"를 저장하고 그 주소를 name에 대입한다. "abc"는 string literal로 취급한다. 즉, 이렇게 초기화된 경우 name의 값을 바꿀 수 없게 된다.
이중포인터의 활용
scope를 벗어낫다고 malloc으로 할당받은 메모리가 해제되지는 않는다는 것이다.
#include <stdio.h>
int main(){
int *p;
{
p = malloc(80);
}
*p = 1;
printf("%d\n", *p);
}
아래는 오류가 난다.
#include <stdio.h>
void func(int *p){
p = malloc(80); // malloc으로 복사된 p의 값을 새로 바꿔주는 것 뿐이다. 원래의 argument를 바꿔주는 것이 아니다.
}
int main(){
int *p;
func(p);
*p = 1;
printf("%d\n", *p);
}
이중 포인터를 활용해서 해결할 수 있다. 이런 경우를 위해서 이중포인터가 존재한다.
#include <stdio.h>
void func(int **p){
*p = malloc(80);
}
int main(){
int *p;
func(&p);
*p = 1;
printf("%d\n", *p);
}
아예 실제로 메모리 주소의 참조가 어떻게 되는지 출력해보자.
#include <stdio.h>
void func(int *p){
printf("In the function\n");
printf("%x\n", &p);
printf("%x\n", p);
}
int main(){
int *p;
func(p);
printf("\n");
printf("In the main function\n");
printf("%x\n", &p);
printf("%x\n", p);
}
출력은 아래와 같다.
In the function
3ad55cc8 // 복사된 p의 주소
3ad55dd0 // p가 담고있는 값
In the main function
3ad55ce0 // 원래 p의 주소
3ad55dd0 // p가 담고있는 값
즉 p가 가지고 있는 주소값 자체는 잘 복사되서 전달되지만, func()에 p가 인자로 전달되면서 새로운 복사된 p가 메모리에 새로 배정받는다. 그리고 거기에 있는 값에 영향을 줘봤자, main에 있는 p의 값을 바꿔주진 않는다.
그럼 구조체 연산에 대해선 어떨까
#include<stdio.h>
typedef struct Test{
int a;
int *str;
} Test;
#include <stdio.h>
void func(Test *t){
t->str = malloc(80);
(t->str) = 1;
printf("In the function\n");
printf("%x\n", &t);
printf("%x\n", t);
printf("%x\n", &(t->a));
printf("%x\n", t->a);
printf("%x\n", &(t->str));
printf("%x\n", t->str);
}
int main(){
Test *t = malloc(sizeof(Test));
func(t);
printf("\n");
printf("In the main function\n");
printf("%x\n", &t);
printf("%x\n", t);
printf("%x\n", &(t->a));
printf("%x\n", t->a);
printf("%x\n", &(t->str));
printf("%x\n", t->str);
}
출력은 다음과 같다.
In the function
e4796a18
53ee4260
53ee4260
0
53ee4268
1
In the main function
e4796a40
53ee4260
53ee4260
0
53ee4268
1
보면 func()에서는 아예 넘겨받은 t에 대해서 새로운 struct를 생성해주는 것을 알 수 있다.
구조체에서 ->은 *(구조체).
과 동등하다고 배웠으니,구조체의 주소를 parameter로 넘겨받아도 ->를 통해서 정확한 멤버에 접근할 수 있다.
#include<stdio.h>
typedef struct Matrix{
// row and coloum
int n;
int m;
// data of matrix
char ** matData;
char textMat[TEXT_LENGTH];
} Matrix;
Matrix *initializeMatrix(Matrix * matStruct){
matStruct->matData = malloc(sizeof(char*) * matStruct->n);
for(int i = 0; i < matStruct->n; i++){
matStruct->matData[i] = calloc(matStruct->m, sizeof(float) * matStruct->m);
}
matStruct->matData[0][0] = 1;
matStruct->textMat[0] = 1;
printf("%x\n", &matStruct);
printf("%x\n", matStruct);
printf("%x\n", &(matStruct->matData));
printf("%x\n", matStruct->matData);
printf("%x\n", matStruct->matData[0][0]);
printf("%x\n", &(matStruct->textMat));
printf("%x\n", matStruct->textMat);
printf("%x\n", matStruct->textMat[0]);
return matStruct;
}
int main(){
Matrix * matStruct;
initializeMatrix(matStruct);
printf("\n\n", &matStruct);
printf("%x\n", &matStruct);
printf("%x\n", matStruct);
printf("%x\n", &(matStruct->matData));
printf("%x\n", matStruct->matData);
printf("%x\n", matStruct->matData[0][0]);
printf("%x\n", &(matStruct->textMat));
printf("%x\n", matStruct->textMat);
printf("%x\n", matStruct->textMat[0]);
return 0;
}
출력은 아래와 같다.
9b8fc98
9b8fdc0
9b8fdc8
80cd3260
1
9b8fdd0
9b8fdd0
1
9b8fcd0
9b8fdc0
9b8fdc8
80cd3260
1
9b8fdd0
9b8fdd0
1
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=dw5637&logNo=100043598497
https://chanywa.com/343
https://thrillfighter.tistory.com/124
- NULL, \0, NUL, 0 의 차이
나보다 이분이 더 잘 설명해주시지 않을까?
https://code4human.tistory.com/116
- 디버거 꼭 쓰자. 디버거를 얼마나 잘 쓰는지가 프로그램의 완성 시간을 가른다.
'개발 기록 > 행렬 계산기' 카테고리의 다른 글
행렬 계산기 개발 일지 - 8 (0) | 2021.05.30 |
---|---|
행렬 계산기 개발 일지 - 7 (0) | 2021.05.28 |
행렬 계산기 개발 일지 - 6 (0) | 2021.05.23 |
행렬 계산기 5일차 (0) | 2021.05.12 |
행렬계산기 개발 4일차... (0) | 2021.05.03 |