
42서울 7기 라피신을 대비하여 정리한 CS50 2019 과정을 간략히 복기하여 재업로드한 내용입니다.
#include <stdio.h>
const int N = 3;
int main(void)
{
// 점수 배열 선언 및 값 저장
int scores[N];
scores[0] = 72;
scores[1] = 73;
scores[2] = 33;
// 평균 점수 출력
printf("Average: %i\n", (scores[0] + scores[1] + scores[2]) / N);
}
N 으로 고정된 정수 배열 scores 를 선언 및 초기화했다.#include <stdio.h>
float average(int length, int array[]);
int main(void)
{
// 사용자로부터 점수의 갯수 입력
int n = get_int("Scores: ");
// 점수 배열 선언 및 사용자로부터 값 입력
int scores[n];
for (int i = 0; i < n; i++)
{
scores[i] = get_int("Score %i: ", i + 1);
}
// 평균 출력
printf("Average: %.1f\n", average(n, scores));
}
n 을 사전에 선언하지 않고, 사용자가 입력한 임의의 정수 값에 따라 결정되도록 했다.char *s = “HI!”;
s 가 정의되어 있다고 생각해 보자. 
\0 을 널 종단 문자 또는 NUL 문자라고 한다. (NULL 이 아니다)char 자료형의 0 즉 문자 0 (아스키 코드 0x30) 과는 구별된다.char *names[4];
names[0] = "EMMA";
names[1] = "RODRIGO";
names[2] = "BRIAN";
names[3] = "DAVID";
printf("%s\n", names[0]);
printf("%c%c%c%c\n", names[0][0], names[0][1], names[0][2], names[0][3]);

NUL 문자가 들어가야 하며, 문자열을 읽어들일 때 NUL 문자를 만나기 전까지를 하나의 문자열로 간주한다.* 과 &#include <stdio.h>
int main(void)
{
int n = 50;
printf("%p\n", &n);
printf("%i\n", *&n);
}
& 연산자를 사용한다.* 를 사용하면 그 메모리 주소에 있는 실제 값을 얻을 수 있다.*& 과 같이 두 연산자가 연속되면 상쇄되어 아무 연산자도 붙이지 않은 것과 같다.#include <stdio.h>
int main(void)
{
int n = 50;
int *p = &n;
printf("%p\n", p);
printf("%i\n", *p);
}
int *p p 바로 앞의 * 는 이 변수가 포인터라는 의미int **p 처럼 생긴 포인터라도 마찬가지다. p 바로 앞의 * 만 이 변수가 포인터라는 의미를 갖고, 남겨진 int * 는 해당 타입의 변수를 가리킨다는 뜻이다.int 는 이 포인터가 int 타입의 변수를 가리킨다는 의미
int *, double *, char * 의 크기는 모두 4바이트이다.int *, double *, char * 의 크기가 모두 8바이트다.char *s = “EMMA”;
s[0], s[1], s[2] 와 같이 하나의 문자가 배열의 한 부분을 나타낸다.s 는 결국 이러한 문자열을 가리키는 포인터가 된다.
0x123 에 있는 s[0] 를 가리키게 된다.char * 으로 선언된다.char 형 변수 한 개를 가리키는 포인터인 것이다.#include <stdio.h>
int main(void)
{
char *s = "EMMA";
// 배열 각 요소의 주소값을 출력
printf("%p\n", &s[0]);
printf("%p\n", &s[1]);
printf("%p\n", &s[2]);
printf("%p\n", &s[3]);
// 배열 각 요소에 저장된 값을 출력
printf("%c\n", *s);
printf("%c\n", *(s+1));
printf("%c\n", *(s+2));
printf("%c\n", *(s+3));
}
& 연산자를 사용하면 해당 값이 저장되어 있는 주소를 알 수 있다.* 연산자를 사용하면 해당 주소에 저장되어 있는 값을 알 수 있다.p[n] == *(p + n) 이다.malloc() 과 free()malloc()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);
}
s 에 저장하고, 또다른 문자열 t 를 선언해 s 로 초기화했다.t 의 첫 번째 문자를 toupper 함수를 이용하여 대문자로 바꿨다.t 뿐만 아니라 s 도 “Emma”라고 출력된다.s 라는 변수에는 emma 라는 문자열이 아닌 그 문자열이 있는 메모리의 주소가 저장되기 때문이다.e 의 주소다.s 에 저장된 값(= e 의 주소)으로 초기화된 t 도 s 와 동일한 주소를 가리키게 됐고, t 를 통한 수정은 s 에도 그대로 반영된다.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 함수를 사용해서 t 를 초기화하면 된다.malloc() : 정해진 크기 만큼 메모리를 할당하는 함수t 는 s 와 아예 다른 메모리 공간을 가리키고 있으므로, 루프를 돌면서 s 문자열에 있는 문자 하나 하나를 t 배열에 복사해주면 값은 동일하지만 각각 별도로 존재하는 두 문자열을 얻을 수 있다.free()malloc() 함수를 이용하여 메모리를 할당한 후에는 free() 함수를 이용하여 메모리를 해제해줘야 한다.malloc() 함수는 힙(heap) 부분의 메모리를 사용하는데, 이 힙 영역은 프로그래머가 직접 공간을 할당, 해제하는 공간이므로 명시적으로 해제해주지 않으면 프로그램이 종료되어도 해당 메모리 공간을 다시 사용할 수가 없다.머신 코드 영역에는 프로그램이 실행될 때 그 프로그램이 컴파일된 바이너리가 저장된다.글로벌 영역에는 프로그램 안에서 저장된 전역 변수가 저장된다.힙 영역에는 malloc 으로 할당된 메모리의 데이터가 저장된다.힙 영역에서는 malloc 에 의해 메모리가 더 할당될수록, 점점 사용하는 메모리의 범위가 아래로 늘어난다.스택 에는 프로그램 내의 함수와 관련된 것들이 저장된다.스택 영역에서는 함수가 더 많이 호출 될수록 사용하는 메모리의 범위가 점점 위로 늘어난다.힙 또는 스택 영역이 이렇게 점점 늘어나다 보면, 제한된 메모리 용량 하에서는 기존의 값을 침범하는 상황도 발생할 것이다.#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;
}
main 함수의 변수 x, y 는 swap 함수를 거치지만 결과적으로 각 변수의 값은 교환되지 않을 것이다.main 함수의 변수와 swap 함수의 변수는 각각 메모리상의 서로 다른 위치에 저장되어 있기 때문이다.swap 함수는 자신의 매개변수 a, b 에 각각 x, y 의 값 을 복사해 저장했을 뿐, 그 변수들을 직접적으로 조작하고 있는 것이 아니다.a 와 b 를 각각 x 와 y 를 가리키는 포인터로 지정해야 한다.스택 을 사용하는 예시로는, 사용자로부터 입력된 문자열이나 정수 값을 받아오는 함수를 들 수 있다.#include <stdio.h>
int main(void)
{
char s[5];
printf("s: ");
scanf("%s", s);
printf("s: %s\n", s);
}
scanf 는 사용자로부터 형식 지정자에 해당되는 값을 입력받아 저장하는 함수다.scanf 함수의 변수가 실제로 저장된 주소로 찾아가서, 사용자가 입력한 값을 저장하도록 하기 위함이다.#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 자료형으로 불러올 수 있다.
FILE 구조체는 스트림의 상태에 대한 정보를 저장하고, 파일 함수에서 사용한다. FILE 구조체는 stdio.h 헤더 파일에 정의된 typedef 이름이다.fopen 함수의 첫번째 인자는 파일의 이름, 두번째 인자는 모드로 r 은 읽기, w 는 쓰기, a 는 덧붙이기를 의미한다.
fprintf() 는 printf() 와 동일하게 stdio.h 헤더에 포함된 함수로, printf() 와는 값을 출력할 위치 가 다르다.
printf() 는 화면 출력, 즉 표준 출력 함수다.fprintf() 는 데이터를 형식에 맞추어 스트림(파일)에 쓴다. 따라서 첫 번째 인자로 파일구조체 포인터 (FILE *) 이 온다.#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);
}
fread 함수를 이용해서 파일에서 첫 3바이트를 읽어왔다.fread(배열, 읽을 바이트 수, 읽을 횟수, 읽을 파일);