어린 시절에는 할머니집, 고모, 각종 친척집들을 하루종일 동분서주하느라 정신이 없었는데 이번 추석은 서울에서 언니랑 둘이 보낸다. 서울이 이렇게 고요한 적이 있었던가.. 알바 끝나고 오후에는 거실에서 비가 주룩주룩 오는 창밖을 바라보고 있는데 도로에 차가 한 대도 없었다. 비가 와서 괜시리 더 울적했다. 북적북적, 사람냄새가 그리운 순간이었다.
제기동역 에스컬레이터 공사장 내부에 한 인부가 보였다. 연휴에도 일을 하시는구나.. 남들 쉴 때도 일을 해야하거나 더 바쁜 직업들에 대해 생각했다. 오, 주식 시장은 쉬지 않을테니 펀드매니저들은 바쁘겠군. 추석에 고향에 가는 사람들을 실어 나르는 철도 기관사와 비행기 조종사들을 생각했다. 나, 연휴에 혼자서 쉴 수 있다는 것만으로 감사한 일이었구나.
그래도 명절이라고 아침엔 언니랑 목욕을 갔다왔다. 뽀송해진 몸과 마음에 잠깐이나마 기대었다. 알바 가는 길에는 주룩주룩 내리는 비를 머금은 은행잎도 보았다. 노란 은행잎 위에 자리잡은 비구슬이 예뻐 사진을 찍어왔다. 그런데 젠장 갑자기 머리가 너무 아파왔다. 결국 터덜터덜 집으로 걸어와 약을 먹고 세 시간을 내리 잤다. 요즈음 잠이 많이 부족했나보다. 이렇게 내 몸상태를 알 수 있으니 다행이었다.
몇 해째 명절을 이렇게 보내며 다짐한 것이 있다. 나는 정말로, 따뜻하고 북적북적한 명절을 내 아이들에게 선사해 줘야지!
서로 한 해동안 어떻게 지냈는지 다정히 묻고 용돈을 쥐어주며 토닥이고, 편 갈라 윷놀이 한 판하고 기름냄새 잔뜩 배이게 전도 부쳐먹어야지
가족의 안정감과 든든함을 느끼게 해줄거다. 받지 못한 것도 줄 수는 있기에
요즈음은 맛있는 음식을 먹는 시간, 혼자 가만히 앉아서 생각하거나 글을 쓰는 시간, 달리기 하는 매 순간, 그리고 독서하는 시간이 좋다. 특히 지하철이나 버스 등 이동시간을 독서시간으로 활용하는데, 가방에 든 책 하나의 무게가 참 든든하다.
어제 언니랑 얘기를 하다가 sns와 쇼츠 등의 폐해에 대한 이야기가 나왔다. 우리들이 실험쥐가 된 것마냥, 언니가 무료한 요즈음 그것들을 자주 경험했더니 마음이 더 불안해지고 조급해진 것 같다고 했다. 전적으로 동의했다 !! 나는 한 10개월정도 전부터 sns를 자주 하지 않는다. 공부도 공부지만, sns는 가공되고 남들에게 보여주고 싶은 모습을 추린 콘텐츠가 많은 비중을 차지하는 것 같다. 수많은 비난과 평가, 원치않는 과도한 정보가 쏟아져 들어오고 눈과 뇌는 깨어있는 모든 시간 피로하다. 사실 나는 x세대에 살고싶다. 연락 한 통을 하려면 삐삐 치러 가야하는 그 시절 ㅎㅎ 편지와 메모가 더 자주 왕래하던 그 시절이 궁금하다. 어쨋든, 사실 그런 이유 뿐만이 아니라 왜인지 요즘은 마음이 조급하다. 나와 다른 사소한 것 하나도 이해하기가 힘들고 화가 정말 없는 내가 화도 짜증도 자꾸 나고 심지어 ? 그것을 표출한다
음... 마음의 명상이 필요한 시간인가보다. 당분간은 규칙적인 생활을 하며 해야 할 것에 집중하는 시간이 필요한 것 같다. 오히려 좀 피곤해도 매일 일찍 일어나고 규칙적으로 살다보면 마음이 점차 안정됨을 느낀다. 어떻게 해야 할지는 알고있는 듯 하니 다행이다.
오후 알바 때 스카에 고3들이 많이 보였는데, 수능을 한 달 남짓 앞둔 시점이라 연휴에도 열심이다 싶더라. 그래서 소심하지만 응원의 말을 남기고 왔다 ㅎㅎㅎ 대견한 아이들.. 뭐든 본인의 삶에 최선을 다하는 것은 참 멋지다
Data structures are forms of organization in memory.
1. Stacks and Queues
Queues : one form of abstract data structure (enqueued, dequeued)
Stacks (push, pop)
FIFO
First in first out
LIFO
Last in first out
typedef struct
{
person people[CAPACITY];
int size;
}
stack;
- Limitation of the code above : unable to grow as items are added to it.
2. Resizing Arrays
There's an array :
In memory, there are other values being stored by other programs, functions, and variables.
If you want to store a fourth value '4' in our array.
You can allocate a new area of memory of size 4 and move the old array to a new one.
This new area of memory would be populated with garbage values
However, it's bad designed - Every time we add a number, we have to copy the array item by item.
It would be nice if we were able to put the '4' somewhere else in memory. (this would no longer be an array though because it's not contiguous)
list.c
#include <stdio.h>
int main(void)
{
//List of size 3
int list[3];
//Initialize list with numbers
list[0] = 1;
list[1] = 2;
list[2] = 3;
//Print list
for (int i = 0; i < 3; i++)
{
printf("%i\n", list[i]);
}
}
We can leverage our understanding of pointers to create a better design in this code:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
//List of size 3
int *list = malloc(3 * sizeof(int));
if (list == NULL)
{
return 1;
}
//Initialize list of size 3 with numbers
list[0] = 1;
list[1] = 2;
list[2] = 3;
//List of size 4
int *tmp = malloc(4 * sizeof(int));
if (tmp == NULL)
{
free(list);
return 1;
}
//Copy list of size 3 into list of size 4
for (int i = 0; i < 3; i++)
{
tmp[i] = list[i];
}
//Add number to list of size 4
tmp[3] = 4;
//Free list of size 3
free(list);
//Remember list of size 4
list = tmp;
//Print list
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
//Free list
free(list);
return 0;
}
Notice that the 'tmp' list was created.
free(list) : since the block of memory that list points to is no longer used.
3. Linked Lists
three useful primitives
struct
a data type that you can define yourself
.
dot notation allows you to access variables inside that structure
*
is used to declare a pointer or dereference a variable
-> operator : goes to an address and looks inside of a structure
A linked list is one of the most powerful data structures within C. It allows you to include values that are located at varying areas of memory. They allow you to dynamically grow and shrink the list as you desire.
Three values stored at three different areas of memory :
We could utilize more memory to keep track of where the next item is :
We would keep one more element in memory, a pointer, that keeps track of the first item in the list.
These boxes are called nodes.
A node contains both an item (int number) & a pointer called next.
For example, a word 'Toad' would be stored as follows:
Toadette and Tom would be stored as:
5주차에서는 Queues, Linked lists, Trees, Dictionaries, 그리고 Tries 까지 다양한 data structure의 개념과 활용 방법에 대해서 알아보았다. 컴퓨터의 메모리를 정리할 때 어떤 data structure을 사용하느냐에 따라 속도와 메모리가 차지하는 크기가 달라지기 때문에 중요한 영향을 끼친다. 저번주에 이어서 보이지 않는 메모리 영역을 다루고, pointer의 개념이 아직 미숙해서 처음으로 강의를 다시 뒤로 돌려 반복해서 듣기도 한 내용들이다. 아직까지 완벽하게 이해했다고 자신할 수는 없지만, Section과 Pset5를 듣고 고민하는 과정에서 더 큰 이해를 할 수 있길!! 오늘도 수고 많았다 안뇨옹
ㅎㅎ 사실 논 건 아니고 매일 시간 내서 문제를 풀려고 노력은 했지만 너무 어려워서 한참 걸렸어요..ㅠㅠ
뒤로 갈수록 배운 거에서 나오는 게 아니고 응용을 요구합니다 !!!
약간.. 개념 배우고 심화 문제 나오는 수능같은 그런 느낌
진짜로 literally 손으로 머리 싸매고 낑낑댐..
어쨋든 어제 두세시간을 쏟고 마지막 과제를 제출하고 나서야 문풀 리뷰를 할 수 있게 되었네용
리뷰하면서 못 다한 이해를 해보도록 하겠습니다 ^~^
1보 후퇴엔 3보 전진~~
레츠고 !
4-1) Volume
: 볼륨 조절하기
WAV files store audio as a sequence of “samples”: numbers that represent the value of some audio signal at a particular point in time.WAV files begin with a 44-byte “header”, and after the header, it contains a sequence of samples, each a single 2-byte (16-bit) integer representing the audio signal at a particular point in time. Scaling each sample value by a given factor has the effect of changing the volume of the audio. Write a program to modify the volume of an audio file.
<Distribution code>
The program should accept three command-line arguments. (input, output, factor)
The program should read the header from the input file and write the header to the output file.
The program should then read the rest of the data from the WAV file, one 16-bit(2-byte) sample at a time. It should multiply each sample by the factor and write the new sample to the output file.
★ atof() : used to convert a string into a floating-point number
A file format (likeBMP,JPEG, orPNG) that supports “24-bit color” uses 24 bits per pixel.A 24-bit BMP uses 8 bits to signify the amount of red in a pixel’s color, 8 bits to signify the amount of green in a pixel’s color, and 8 bits to signify the amount of blue in a pixel’s color.If the R, G, and B values of some pixel in a BMP are, say,0xff,0x00, and0x00in hexadecimal. In this problem, you’ll manipulate these R, G, and B values of individual pixels, ultimately creating your very own image filters.
<Distribution code>
//TODO #1
: Implement grayscale (흑백 필터)
We should make sure the red, green, and blue values are all the same value. But what value to make them?
→ If the original red, green, and blue values were all pretty high, then the new value should also be pretty high. If the original values were all low, then the new values should also be low.
You can take the average of the red, green, and blue values of each pixel to determine what shade of grey to make the new pixel.
//Loop over all pixels
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
//Take average of red, green, and blue
int average = round((image[i][j].rgbtRed + image[i][j].rgbtGreen + image[i][j].rgbtBlue) / 3.0);
//Update pixel values
image[i][j].rgbtRed = average;
image[i][j].rgbtGreen = average;
image[i][j].rgbtBlue = average;
}
}
The result being like :
//TODO #2 : Implement Sepia (오래된 붉은갈색 느낌 필터)
For each pixel, the sepia color values should be calculated based on the original color values per the below:
Any pixels on the left side of the image should end up on the right, and vice versa.
//Loop over all pixels
for (int i = 0; i < height; i++)
{
for (int j = 0; j < (width/2); j++)
{
//Swap pixels
RGBTRIPLE temp = image[i][j];
image[i][j] = image[i][width-j-1];
image[i][width-j-1] = temp;
}
}
★ Notice that j loop is until (width/2) because only half of the pixels of a row should be reflected!
★ image[i][width-j-1] means the pixel on opposite side of a row.
The result being like:
//TODO #4
: Implement Blur (블러 처리된 필터) ★
가장 어려워서 며칠간 고민했던 부분!!
We will use "box blur", which works by taking each pixel and, for each color value, giving it a new value by averaging the color values of neighboring pixels.
The new value of each pixel would be the average of the values of all of the pixels that are within 1 row and column of the original pixel (forming a 3x3 box).
Ex) For pixel 6 : new value of averaged the original color values of pixels 1,2,3,5,6,7,9,10, and 11.
For a pixel along the edge or corner, like pixel 15 : pixels 10,11,12,14,15and 16.
We are going to create a copy of image:
//Create a copy of image
RGBTRIPLE copy[height][width];
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
copy[i][j] = image[i][j];
}
}
Let's write psedocodes first, stey-by-step!
//전체 이미지의 각 픽셀 iterate
//copy 만들기
//3x3 box 픽셀 iterate
//픽셀이 valid한 경우 (edge case, corner case 포함)
//각 픽셀에 새로운 RGB 값 할당
//각 RGB 값의 평균을 copy에 직접 할당
//전체 픽셀에 대해 copy 값을 image 값에 원래대로 할당
즉, 전체 이미지에서 3x3 사이즈의 픽셀 박스를 iterate하면서 각 RGB값의 평균을 새로이 할당하고, 마지막에는 copy에 할당한 값을 원래대로 image에 돌려놓으면 됩니다!
① 3x3 box 픽셀 반복문
★새로운 변수 int di, int dj를 만들어서 상하좌우 한 칸씩을 뜻하는 -1부터 1까지 반복하는 box iteration을 만든다.
★이것을 전체를 반복하는 변수 i와 j에 더하여 전체 픽셀을 한정적인 3x3 box로 반복할 수 있게 새로운 변수 ni, nj로 정의한다.
② valid pixel 조건문
: 유효한 픽셀이라는 것은 edge나 corner 픽셀의 경우도 포괄해야 함을 뜻한다.
이는 새로이 정의된 변수 ni, nj가 0보다 크거나 같고 각각 height, width보다 작아야함을 의미한다.
③ 새로운 RGB 값 할당하기
: 먼저, 새로운 RGB값을 담아둘 변수 Red, Green, Blue를 정의한 후 각각에 값을 할당한다.
: float으로 정의한 이유는 각 값이 딱 정수로 떨어지지 않을 수 있기 때문. 이렇게 정의하면 정확도가 올라갈 수 있다.
: 각 픽셀의 빨간색, 초록색, 파란색 값을 각각 Red, Green, Blue에 더해준다.
④ counter 만들기
: 평균을 구하기 위해서는 몇 개의 유효한 픽셀의 RGB 값이 더해졌는지 알아야 한다.
따라서, 유효한 픽셀을 카운트 할 때마다 counter 변수를 하나씩 업 시켜준다.
먼저, counter 변수를 0으로 정의한다.
: 중요한 것은 counter을 0으로 정의하는 위치이다. 각 box를 반복한 후에 다시 0으로 reset 해야하기 때문에 box iteration 앞에 정의한다.
: 유효한 픽셀 조건문 안에 counter ++를 해준다.
⑤ RGB 값 평균 구하고 할당하기
: RGB 값의 평균은 직접적으로 copy에 할당한다. 이 때 주의할 점은 변수 Red, Green, Blue 값이 float 이기 때문에 round()를 통해 가까운 정수로 변환해준다.
: 이 때에도 중요한 것은 위치이다. Box iteration 바깥에서 copy에 할당해준다.
⑥ copy 값을 image 값으로 옮기기
: 각 픽셀의 값을 다 옮겨 할당해야 하므로, 전체 픽셀을 반복하는 for문 아래에서 할당시켜준다.
완성된 Blur 코드를 전체적으로 보면 다음과 같다:
The result being like:
4-3) Recover
: 삭제된 사진 복원하기
We spent the past several days taking photos around campus, all of which were saved on a digital camera as JPEGs on a memory card. Unfortunately, we somehow deleted them all! We’re hoping (er, expecting!) you can write a program that recovers the photos for us!
(약간 초등학교 수학 문제중에 달력을 찢었다.. 이런 거 생각났다 ㅋㅋㅋㅋㅋㅋ 왜 찢어.. 왜 삭제해..)
Background
JPEGs have "signatures", patterns of bytes that can distinguish them from other file formats. The first three bytes of JPEGs are 0xff, 0xd8 and 0xff.
The fourth byte is either 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, or 0xef. The fourth byte's first four bits are 1110.
Digital cameras only write to cards in units of 512 B. (block size = 512 bytes) →It means you can read 512 of memory card's bytes at a time for efficiency's sake.
위 그림처럼 각 색이 각 JPEG 파일을 의미한다. JPEG가 시작하는 블럭이 있고, 시작하지 않는 블럭이 있다.
Let's writepsedocodesfirst!
//Accept a single command-line argument
//Open the memory card
//While there's still data left to read from the memory
//Create JPEGs from the data
① single command-line argument
: 하나의 command-line argument가 아닐 시 에러 메세지로 경고한다. (argc != 2)
② open the memory card
: card memory로 가서 argv[1]을 여는데, read 모드로 연다. ("r") 만일 file이 제대로 열리지 않을 시 에러메세지로 경고한다.
③ memory에서 더 이상 읽을 데이터가 없을 때까지 조건문
: 파일에서 데이터를 읽을 때, 일시적으로 그 데이터를 보관할 "buffer"가 필요하다. 데이터가 512 bytes로 저장되어 있으므로, 해당 사이즈로 buffer을 만들면,
uint8_t buffer[512];
이렇게 만들 수 있겠지만, 코드에 constant(magic number)를 쓰는 것은 바람직하지 않으므로 보완하여 다시 쓸 수 있다.
★ Notice that there's no ; when you use #define
그 다음, fread()를 사용하여 계속해서 남은 데이터를 읽는다. 이 때, fread() returns the number of bytes it has read, so make sure that the condition is equal to BLOCKSIZE(512).
④ If start of new JPEG
: 앞에서 memory block은 JPEG의 시작일 수도, 아닐 수도 있다고 언급했다. 먼저 JPEG의 시작일 경우 조건문을 생각해보자.
A variable : a name for some value that can change → has an address somewhere in memory
type : int *
name : p
value : &calls (the address of calls)
Reasons to use pointers
Pointers enable additional capabilities like:
You could write a function that allows you to pass something in by reference and not just by copy. The code you write is cleaner as a result.
You could ask your program for dynamic memory depending on what the user actually needs. Your programs can now scale their usage of memory according to user behavior.
2. Passing by Copy vs Passing by Reference
<Passing by Copy>
This seemed to be swapped in swap, but it's still the same in main function !
<Passing by Reference>
By copying the addresses of a and b(the pointer to a/ the pointer to b), we actually copy in the address, and we change the values exactly where those values currently in memory.
Let's debug this in VS Code and see step-by-step:
Here's my code swapping two values, a and b.
If I run debug50 :
① Notice that a = 10, b = 50
Let's step into the swap function!
② In variable window, you can see the addresses of a and b now. (&a, &b)
Let's step over :
③ Notice that the value of location a(*a = 10) is handed over to temp.(temp = 10)
Let's step over:
④ Now, the value of b(*b = 50) is handed over to a.(*a = 50)
Let's step over for the last time :
⑤ Notice that the value of temp(10) is handed over to *b.(*b = 10)
And now you can check that a and b are swapped.
3. FILE I/O
We can also open up files, read data from them, and write data to those files!
fopen
opens a file for future reading/ writing.
fclose
closes a file.
Always fclose all files you fopen!
fopen
"r" means 'read' mode.
"w" means 'write' mode.
fclose
<Reading and Writing>
fread
reads data from a file into a buffer*.
fwrite
writes data from a buffer* to a file.
*a buffer is a chunk of memory that can temporarily store some data from the file.
Why do we need a buffer?
→ We often don't know how big a file is, so the best we can do is to look at small bits of it, not the entire file itself.
<Reading from a File>
From where are you reading?
To where are you reading?
What size is each block of data you want to read? (ex. text file-1 byte/ image file-3 bytes)
How many blocks do you want to read?
fread(Towhere, what size , how many , Fromwhere);
ex) fread(buffer, 1, 4, f);
<Practice with Reading>
Create a program, pdf.c, that opens a file given as a command-line argument.
Check if that file is a PDF. A PDF always begin with a four-byte sequence, corresponding to these integers: 37, 80, 68, 70
① Open up a file
② Create a buffer
③ Use fread to read 4 individual bytes from that file
④ Print those out
uint8_t : Unsigned Int of 8 bytes (always positive, takes 8 bits or 1 byte long) Meaning, you can store a positive Integer with max number being 2^8-1.
If we use int buffer instead of uint8_t, we might not get the values we expect,
because int buffer[4] is 16 bytes long(an integer is 4 bytes long), whereas we're only going to read four of them.