컴퓨터는 연산을 처리하는 계산기
cpu - ram - ssd / hdd 순으로 구성됨
cpu가 연산속도가 가장 빠르고 그 다음이 ram, 마지막이 ssd / hdd 순으로 속도가 빠름
cpu는 속도가 빠르지만 용량이 적어 많은 정보를 저장하고 있을 수 없다.
그래서 실행할 명령과 데이터를 ram에 담고 있다가 cpu에서 처리할 때 가져오는 식으로 연산을 처리한다.
그리고 ram은 휘발성 메모리이기 때문에 데이터를 저장할 때는 하드디스크(ssd / hdd)에 이를 저장한다.
비유하자면
레지스터 = 도마
캐시 = 도마 옆 책상
램 = 방안에 있는 냉장고
하드디스크 = 식자재 창고
이런 식이다.
cpu의 연산을 처리하는 부분을 레지스터라고 한다.
빠르게 정보를 레지스터에 불러오기 위해 캐시(Cache)라는 저장공간을 활용하는데 계층 별로 L1, L2, L3 캐시로 나뉘어진다. (L1이 가장 빠르고 L3가 가장 느리다)
변수를 선언하면 특정 메모리 주소에 해당 변수를 위한 공간이 할당된다. 변수에 데이터를 입력하면 해당 메모리 주소에 데이터가 입력되는 형식이다.
다만, 특정 메모리 주소를 할당했을 때 문제가 될 수 있는 점이 있다.
컴퓨터는 여러 개의 프로그램을 작동시키기 때문에 할당한 메모리 주소가 이미 할당되어 있을 경우가 그것이다.
컴퓨터에서는 이러한 상황을 방지하기 위해 가상메모리와 물리메모리라는 것을 활용하는데
같은 주소 0x12F000가 주어졌더라도 실제 메모리에서는 다른 주소를 가리킨다.
이러한 변환방식을 페이징(paging)이라고 하고, 변환이 되는 최소의 메모리 단위를 페이지(page)라고 한다.
어떻게 변환을 수행할 지 기록한 테이블을 페이지 테이블(page table)이라고 하는데, 이 페이지 테이블은 각 프로그램마다 하나씩 갖고 있다.
따라서 구글 크롬에서의 0x1234와 그림판의 0x1234는 주소는 같지만 (가상메모리), 실제로는 다른 메모리 주소를 참조하게 된다. (물리메모리)
18 = 1 2 ^ 4 + 0 2 ^ 3 + 0 2 ^ 2 + 1 2 ^ 1 + 0 * 2 ^ 0 = 10010₂
십진법을 2로 나누고 나머지를 역으로 읽어주면 이진법으로 간단히 변형 가능
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
123 = 7 16 + 11 = 0x7B
19 = 16 + 3 = 0x13
16782 = 4 16^3 + 1 16^2 + 8 16^1 + 14 * 16^0 = 0x418E
0x4F38E
4 (100) F (1111) 3(0011) 8(1000) E(1110)
0x4F38E == 100 1111 0011 1000 1110 (반대도 마찬가지)
100 0001 1000 1110 == 0x418E
8비트 = 1바이트
8비트로 나타낼 수 있는 최대의 수
00000000 ~ 11111111 = 0 ~ 255 = 0xFF (총 256개의 수)
레지스터의 크기 = 컴퓨터 상에서 연산이 실행되는 최소 단위 = 워드)
64비트 컴퓨터 -> 1워드 = 64비트 = 8바이트
#include <stdio.h>
int main() {
/*printf("Hello World! \n");*/
//printf("This is Testing);
printf("Hello Human \n");
printf("Hello Computer \n"); // 주석을 잘 달아주어야 나중에 다시 쓸 때 편리하다
return 0;
}
// 변수 알아보기 1
#include <stdio.h>
int main() {
int a;
a = 10;
printf("a와 같은 값은 : %d \n", a);
return 0;
}
// 변수 알아보기 2
#include <stdio.h>
int main() {
int a;
a = 127;
printf("a 값은 %d 진수로 %o 입니다. \n", 8, a); // o : a를 8진수로 표현
printf("a 값은 %d 진수로 %d 입니다. \n", 10, a);
printf("a 값은 %d 진수로 %x 입니다. \n", 16, a); // x : a를 16진수로 표현
return 0;
}
// 변수 알아보기 3
#include <stdio.h>
int main() {
float a = 3.141592f; // float는 f를 붙여야지 안그러면 double로 인식
double b = 3.141592;
printf("a : %f \n", a);
printf("a : %f \n", b);
return 0;
}
// printf의 형식
#include <stdio.h>
int main() {
float a = 3.141592f;
double b = 3.141592;
int c = 123;
printf("a : %.2f \n", a); // 실수형 데이터를 소수점 2자리까지 표현
printf("c : %5d \n", c); // 정수형 데이터를 5자리까지 표현
printf("b : %6.3f \n", b); // 실수형 데이터를 6자리까지 표현하고 소수점은 3자리까지
return 0;
}
// 변수 선언시 주의할 점
#include <stdio.h>
int main() {
int a;
a = 1;
printf("a 는 : %d", a);
int b; // 최신버전 c는 중간에 변수 선언해도 상관없다.
return 0;
}
// 변수 선언시 주의할 점 2
#include <stdio.h>
int main() {
int a, A; // a와 A는 각기 다른 변수
int 1hi;
// (오류) 숫자가 앞에 위치할 수 없다.
int h123, h123i, h123456; // 숫자가 뒤에 위치하면 괜찮다.
int 한글이좋아;
// (오류) 변수는 오직 알파벳, 숫자, 그리고 _ (underscore)로만으로 이루어져야 한다.
int space bar;
// (오류) 변수의 이름에 띄어쓰기는 하면 안된다. 그 대신 _로 대체하는 것이 읽기 편하다.
int space_bar; // 이건 괜찮다.
int enum, long, double, int;
// (오류) 위 변수들은 이미 C 안에서 쓰이고 있는 "예약어"이다. 이런것들은 변수로 쓰면 안된다.
파란색으로 표현된 것은 다 예약어라고 보면 된다.
return 0;
}
// 좋은 변수 이름의 기준
/* 1. 무슨 데이터를 보관할 수 있어야 하고
2. (되도록이면) 영어로 읽히도록 해야 한다. 한국말을 영어로 풀어쓰면 이해하기 어렵다.
3. 한가지 스타일을 고수하자
*/
// 한가지 스타일 (둘 중 어느 것을 고르던 한가지로 하는 것이 좋다.
int this_is_some_variables; // 이렇게 _로 띄어쓰기를 표현하던가
int ThisIsSomeVariables; // 이렇게 대문자로 구분하던가 (Camel case)
int a, b, c; // 이렇게 짓지말고
int num_students, total_score; // 이렇게 구체적으로 지어주는 것이 좋다.
//산술연산
#include <stdio.h>
int main() {
int a, b;
a = 10;
b = 3;
printf("a + b는 : %d \n", a + b);
printf("a - b는 : %d \n", a - b);
printf("a * b는 : %d \n", a * b);
printf("a / b는 : %d \n", a / b);
return 0;
}
a = 5;
b = 5;
c = 5;
d = 5;
// 이거랑
a = b = c = d = 5
// 는 같다. (뒤에서부터 해석)
// 산술변환
#include <stdio.h>
int main() {
int a;
double b;
a = 10;
b = 3;
printf("a / b는 : %f \n", a / b);
printf("b / a는 : %f \n", b / a);
return 0;
}
// 나눠주면서 실수형으로 산술변환이 된다.
// 대입 연산자
#include <stdio.h>
int main() {
int a = 3;
a = a + 3;
printf("a의 값은 : %d \n", a);
return 0;
}
// 더하기 1을 하는 방법
#include <stdio.h>
int main() {
int a = 1, b = 1, c = 1, d = 1;
a = a + 1;
printf("a : %d \n", a);
b += 1;
printf("b : %d \n", b);
++c;
printf("c : %d \n", c);
d++;
printf("d : %d \n", d);
return 0;
}
// prefix, postfix
#include <stdio.h>
int main() {
int a = 1;
printf("++a : %d \n", ++a);
a = 1;
printf("a++ : %d \n", a++);
printf("a : %d \n", a);
return 0;
}
// 비트연산
#include <stdio.h>
int main() {
int a = 0xAF; // 10101111
int b = 0xB5; // 10110101
printf("%x \n", a & b); // a & b = 10100101
printf("%x \n", a | b); // a | b = 10111111
printf("%x \n", a ^ b); // a ^ b = 00011010
printf("%x \n", ~a); // ~a = 01010000
printf("%x \n", a << 2); // a << 2 = 1010111100
printf("%x \n", b >> 3); // b >> 3 = 00010110
return 0;
}
// int a = 10101111는 자료형이 int이므로 4byte = 32bit, 00000000 00000000 00000000 10101111이다.
// 그러므로 a << 2 = 1011010100 이다.
// ~a = 11111111 11111111 11111111 01010000 = 0xFFFFFF50
// 마찬가지로 b = 00000000 00000000 00000000 10110101이고 b >> 3은 00010110이 된다. (맨 왼쪽의 비트가 0)
// 오버플로우
#include <stdio.h>
int main() {
int a = 2147483647;
printf("a : %d \n", a);
a++;
printf("a : %d \n", a);
return 0;
}
2의 보수 - 비트 반전시키고 +1하고 해당하는 부호(0이면 양수 1이면 음수) 붙이면 된다.
1011 -> 0100 -> 0101 = -5
0111 -> 1000 -> 1001 = 9
0000 -> 1111 -> 0000
a = 0x7FFFFFFF -> a++ = 0x80000000 -> 반전(0111 1111 ... 1111) -> 1000 0000 ... 0000 = -0x80000000 = -2147483648
// 음수 자료형 오버플로우
#include <stdio.h>
int main() {
unsigned int b = -1;
printf("b에 들어있는 값을 unsigned int로 해석했을 때 값 : %u \n", b);
return 0;
}
* #include <stdio.h>
int main() {
unsigned int b = 4294967295;
printf("b : %u \n", b);
b++;
printf("b : %u \n", b);
return 0;
}