메모리 주소는 C에서 pointer라고 부른다.
이는 16진법으로 되어있으며
0x로 시작함. 이를통해 16진법임을 알 수 있게함.
가장 쉽게 접했던 16진법은 색의 코드다.
색은 RGB로도 표현 가능하지만
FFFFFF 이런식으로도 표현한다. 이는 16진법이다.
C언어에서 포인터(메모리주소)를 얻는 코드는 아래와 같다.
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%p\n", &n);
}
%P = POINTER 를 의미한다.
아래와같이 *을 이용해 주소로부터 값을 얻을 수 있다.
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%i\n", *&n); // 50
}
#include <stdio.h>
int main(void)
{
int n = 50;
int *p = &n;
printf("%p\n", p); // n의 주소
printf("%i\n", *p); // P가 가르키는 변수 = 50
}
int *p 에서 p앞의 *는 이 변수가 포인터라는 의미이고, int 는 이 포인터가 int 타입의 변수를 가리킨다는 의미.
보통 요즘 메모리의 경우 포인터는 2배의 사이즈를 갖는다.
typedef char *string
위와같은 방법으로 string이라는 자료형을 만들 수 있다.
string s = "EMMA";
라고 한다면, s의 포인터 값은 E의 포인터 값이다. 첫번째 글자(시작점)의 주소만 갖고 있으며 null값(\0)을통해 어디서 멈출지를 알게 된다.
그래서 s의 주소와 s[0]의 주소는 같다.
s[0] / s[1] 등, 대괄호 표현은 어떻게 작동할까?
이는 사실 s / (s+1) 로 작동한다.
서로다른 변수(스트링) s / t 에 같은 글자 "EMMA"를 지정해도 , s와 t를 비교하면 '다르다' 라고 나온다.
왜냐하면 s와 t는 서로다른 주소를 포인팅 하기 때문이다.
그렇다면 어떻게 두 스트링을 비교할 수 있을까?
내 개인적인 생각은 두 주소가 아닌 두 주소가 갖고있는 값의 2진 값을 비교해야된다고 생각한다. 어쨋든 메모리에 저장되는 값은 2진수로 표현되기 때문에 주소가 아닌 직접적인 2진수 저장값을 비교하면 같을 것이라고 생각한다.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
int main(void)
{
string s = get_string("s: ");
string t = s;
t[0] = toupper(t[0]);
printf("s: %s\n", s);
printf("t: %s\n", t);
}
t의 첫 글자만 대문자로 바꾸려고 하지만, 이는 결국
s가 가르키는 단어의 첫 글자까지 대문자로 만든다.
왜냐하면, t와 s는 결국 같은 주소값을 갖기 때문.
아래와 같이 변경해야 한다.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char *s = get_string("s: ");
char *t = malloc(strlen(s) + 1);
for (int i = 0, n = strlen(s); i < n + 1; i++)
{
t[i] = s[i];
}
t[0] = toupper(t[0]);
printf("s: %s\n", s);
printf("t: %s\n", t);
}
malloc 은 메모리를 할당하는 것이다.
strlen은 string의 길이를 받는다.
string의 길이에 + 1한 값만큼(null종단문자를 위한 자리)
메모리를 할당한다.(바이트)
그다음 그 새로운 메모리의 하나하나의 값을 저장한 뒤
첫번째 값만 대문자로 한다면 두 emma(s/t)는 다른 새로운 값으로 저장된다. (복사)
#include <stdlib.h>
void f(void)
{
int *x = malloc(10 * sizeof(int));
x[10] = 0;
}
int main(void)
{
f();
return 0;
}
int *x = malloc( 10 * sizeof(int) ); 를 보면,
포인터 x에 int의 사이즈(4바이트) 곱하기 10배의 (10개만큼) 메모리를 할당했다. (index로 0~9)
그래서 x[10]에 접근하려 한다면 에러가 생긴다.
메모리 안에는 구역이 있다.
머신 코드 영역에는 프로그램이 실행될 때 그 프로그램이 컴파일된 바이너리가 저장됨.
글로벌 영역에는 프로그램 안에서 저장된 전역 변수가 저장됨.
힙 영역에는 malloc으로 할당된 메모리의 데이터가 저장 됨.
스택에는 프로그램 내의 함수와 관련된 것들이 저장됩니다.
힙과 스택은 같은 영역에 있지만(?) 힙은 위에서 아래로 내려가고 스택은 밑에서 위로 쌓인다.
스택은 밑에서 위로 쌓이고 위에서 부터 역할을 마치고 사라진다.
따라서 swap을 하려면 아래처럼 해야된다.
#include <stdio.h>
void swap(int *a, int *b);
int main(void)
{
int x = 1;
int y = 2;
printf("x is %i, y is %i\n", x, y);
swap(&x, &y);
printf("x is %i, y is %i\n", x, y);
}
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
그냥 swap 함수에 x,y를 넣고, 교환을 하면
swap 함수 내에서만 변경된 뒤 스택에서 사라진다.
따라서 x,y값에 적용이 되지 않는다.
&을 이용하여 포인터 값을 넣고 그 포인터 값을 바꾸는 과정을 통해 교환을 할 수 있다.
stack overflow
스택이 넘치는 현상(계쏙 부르는 함수가 있다면)
heap overlow
힙이 넘치는 현상(계속 malloc 하면)
-scanf 을통해 값을 저장할 때는, 변수의 '주소(포인터)'를 인자로 줘서 값을 저장.
(swap과 같은 이유)
파일 쓰기 코드
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *file = fopen("phonebook.csv", "a");
char *name = get_string("Name: ");
char *number = get_string("Number: ");
fprintf(file, "%s,%s\n", name, number);
fclose(file);
}
fopen이라는 함수를 이용하면 파일을 FILE이라는 자료형으로 불러올 수 있다.
fopen 함수의 첫번째 인자는 파일의 이름, 두번째 인자는 모드로 r은 읽기, w는 쓰기, a는 덧붙이기를 의미한다.
사용자에게 name과 number라는 문자열을 입력 받고, 이를 fprintf 함수를 이용하여 printf에서처럼 파일에 직접 내용을 출력할 수 있다.
작업이 끝난 후에는 fclose함수로 파일에 대한 작업을 종료해줘야 한다.
파일엔 매직넘버라는게 있다. 파일 시그니쳐라고도 불리우는데,
예를들어 jepg파일의 경우
파일의 시작점에 인덱스[0,1,2]가 0xFF, 0xD8, 0xFF 이다.
이를 통해 이것이 JEPG파일인 지 알 수 있으며
많은 파일들이 이러한 특성을 갖고 있다.
아래는 파일 읽는 코드
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
return 1;
}
FILE *file = fopen(argv[1], "r"); //파일을 연다.
if (file == NULL)
{
return 1;
}
unsigned char bytes[3];
fread(bytes, 3, 1, file);
if (bytes[0] == 0xff && bytes[1] == 0xd8 && bytes[2] == 0xff)
{
printf("Maybe\n");
}
else
{
printf("No\n");
}
fclose(file);
}