사용자가 컴퓨터에서 프로그램을 운용할 수 있도록 컴퓨터 하드웨어와 특정한 시스템 소프트웨어를 결합한 것
컴퓨터 하드웨어의 구성요소를 갖는다.
컴퓨터 시스템이 아닌 경우
마이크로 컨트롤러(microcontroller)
범용적이고 재프로그래밍 가능하며 프로세서, 내부 메모리, 2차 저장 장치가 있고 입출력이 가능하지만, 운영체제가 없다
기타 용어
RAM용 DIMM(dual inline memory module)
단일 기판 컴퓨터(single-board computer:SBC)
시스템 온 칩(system-on-a-chip:SoC)
라즈베리파이
멀티코어(multicore)
GNU C 컴파일러(GCC)
시스템 커널 이름 (예-Linux)
무료 오픈 소프트웨어 (Free Open Source Software:FOSS)
1 C 프로그래밍 언어
파이썬은 인터프리터 프로그래밍 언어(interpreted programming language)
가상 머신처럼 작동
C소스코드를 바이너리 실행 파일로 변환하는 C 컴파일러
MakeFile의 원리?
변수
데이터 보관을 위해 변수를 통해 저장 위치를 명명한다
변수의 범위(scope)는 변수가 의미를 갖는 순간과 수명을 정의
변수의 타입(type)은 표현할 수 있는 값의 범위와 해당 데이터에 대한 작업을 수행할 때 해석되는 방식으로 정의
함수
프로토타입
프로시저 (procedure)
스택
실행 스택 (execution stack)은 프로그램에서 활성함수의 상태를 추적
각 함수호출
매개변수와 지역 변숫값을 포함하는 새로운 스택 프레임(stack frame, 활성화 프레임 또는 활성화 레코드) 생성
스택 맨 위에 있는 프레임은 활성 프레임‘
현재 실행중인 함수 활성화를 나타내며, 범위로 지역 변수와 매개변수만 있다.
함수가 호출되면 새로운 스택 프레임이 생성되고 지역 변수와 매개변수를 위한 공간이 새 프레임에 할당
함수가 반환되면 스택 프레임이 스택에서 제거된다.
함수는 인수를 값으로 전달 (pass by value), 복사의 의미
배열과 구조체
데이터 요소의 컬랙션 생성을 지원하는 방법
배열은 타입이 동일한 데이터 요소로 정렬된 컬렉션
구조체는 타입이 상이한 데이터 요소를 컬렉션
배열과 구조체를 조합해 더 복잡한 타입과 구조 생성
구조체는 구조화된 타입 정의 (c언어에서 지원)
구조체 심화
C컴파일러는 필드를 구분할 때 저장 위치나 구조체 변수 메모리가 시작되는 오프셋(offset)을 사용한다
C 구조체 타입은 lvalue, 대입문의 왼쪽에 나타날 수 있음을 의미
구조체 필드의 값은 대입문 왼쪽에 있는 구조체 필드 값으로 복사
student2 = student1; // 복사
strcpy(student2.name, student1.name); // 한 필드 변경
크기
printf(“%lu”, sizeof(strict studentT);
함수 전달시 복사된다. 즉 값으로 전달 (pass by value)
배열은 함수 전달시 배열과 size를 같이 보내자.
lvalue
LVALUE, 대입문의 왼쪽에 올 수 있는 표현식으로, 메모리 저장 위치를 나타내는 표현
C의 기본타입의 단일변수, 배열 요소, 구조체 모두 lvalue
정적으로 선언된 배열의 이름은 lvalue가 아니다.
정적으로 선언된 기본 주소는 바꿀 수 없다.
int x;
x = 10; // x는 lvalue
char arr[10];
arr[3] = ‘ch’; // arr[3] = lvalue
arr = “hello”; // arr, 정적으로 선언된 배열의 기본 주소는 바꿀 수 없다.
struct studentT {
int age;
}
struct studentT student1, student2;
student1.age = 18; // age 필드는 lvalue
student2.name = student2.name; // 정적으로 선언된 기본 주소는 바꿀 수 없다. name 필드는 lvalue가 아니다.
# 2. C 프로그래밍 심화
- 포인터(pointer)
- 프로그램 상태 접근을 위한 간접 계층 제공
- void *는 일반포인터, 모든 타입에 대한 포인터 또는 지정되지 않은 타입을 위한 포인터
- c 시스템의 메모리 주소가 항상 같은 바이트 수로 저장하기 때문에 일반 포인터 타입을 허용한다.
- 32비트 -> 4바이트, 64비트 시스템 -> 8바이트
- 역참조가 불가능하다. 따라서 리캐스팅 필요
- `array = (int *)malloc(sizeof(int) * 10);`
- 포인터 변수
- 특정 타입을 저장할 수 있는 메모리 위치의 주소를 저장
- 널 포인터를 역참조하면 프로그램에 충돌 발생 위험
- 포인터(ptr) -> 변수(*ptr)
- 변수(a) -> 포인터(&a)
- 포인터 산술
- 포인터에 값을 더하면 다음 값을 가리킨다. `cptr++;`
- 동적 메모리 할당(dynamic memory allocation)
- 프로그래밍이 실행될 때 필요한 공간과 크기를 변화에 맞게 조절해서 필요한 만큼 공간을 할당하고 그 이상 필요하지 않는 공간은 반환
- 동적으로 할당된 메모리는 프로그램 주소 공간의 힙 메모리 영역을 차지한다.
- 동적으로 메모리 요청하면 포인터 변수에 해당해야 하는 메모리 청크(chunk)를 제공
- <img width="285" alt="image" src="https://github.com/user-attachments/assets/7289e89b-10e0-4b5c-847f-0e87934a753e">
- 프로그램의 메모리
- <img width="279" alt="image" src="https://github.com/user-attachments/assets/ad3d02c8-7780-4b7c-8fc8-c533e1d118d9">
- 운영체제 이외의 부분은 실행 중인 프로그램에서 사용 가능
- 지역 변수와 매게변수는 스택(stack)메모리에 상주
- 전역변수는 데이터 영역에 저장
- 힙(heap) 메모리 부분은 동적 메모리 할당과 관련된 프로그램 주소 공간의 일부
- 동적으로 할당되면서 더 높은 주소로 늘려간다.
- 힙 메모리는 익명 메모리라는 점을 기억하자.
- 익명이란 힙의 주소가 변수명에 바인딩 되지 않음을 뜻한다.
- malloy과 free
- 프로그램이 힙에 메모리 할당하고 해제할 수 있게 한다.
- malloc에 sizeof(<Type>) * count 형식으로 총 바이트 수를 구하는 식으로 전달
- <img width="285" alt="image" src="https://github.com/user-attachments/assets/0230ebd0-7372-444a-afcb-1a70c834b4de">
- `[i]`는 메모리에 있는 배열의 기본 주소에서 오프셋 i에 있는 위치를 역참조한다.
- 힙 메모리 관리
- malloc으로 할당된 힙 공간의 청크 사이에 텅 빈 공간 청크가 생긴다.
- 힙 메모리 관리자는 메모리 바이트를 할당할 뿐만 아니라 메모리 앞에 몇 바이트 추가로 헤더를 할당한다.
- 헤더에는 할당된 힙 공간의 청크에 대한 메타데이터가 있다.
- 이를 활용해 free에는 주소값만 전달해도 해제 가능하다.
- 힙 메모리 관리자는 전형적으로 특정 크기의 사용 가능한 공간을 빠르게 검색할 수 있도록 힙 내부의 공간들이 갖는 크기를 정리한 목록을 가진다.
- C구조체 (struct)
- struct 타입은 이종의 데이터 모음(collection)을 나타낸다.
- 서로 다른 타입으로 이루어진 집합을 하나의 일관된 단위로 취급하는 메커니즘
- struct를 위한 포인터
```c
struct studentT *sptr;
(*sptr).grad_yr = 2021;
sptr->gpa = 3.5;
### gcc
- `$ gcc -o myprog myprog.c --static -lpthread -Ireadline`
- POSIX 스레드 라이브러리(pthread)
와 readline 라이브러리는 gcc 커맨드 라인에서 명시적으로 연결해야 한다.
- --static 옵션은 정적 연결을 요청하는 한 방법이다.
$ gcc -E myprog.c
$ gcc -E myprog. c › out
$ vim out
- 프리컴파일러(precompiler) 단계가 먼저 실행 되어 전처리기 지시문을 확장 한다
- 프리컴파일러 단계의 중간 결과를 보려면 gcc에 -E 플래그를 전달한다
$ gcc -S myprog. c
$ vim myprog.s
- 컴파일 단계는 컴파일 작업을 수행 하며 기계 없앰 불리 코드로 변환 한다 gcc에 -S 플래그를 전달 한다
$ gcc -c myprog.c
$ objdump -d myprog.o
- 어셈블리 단계는 어셈블리 코드를 재배치 가능한 2진 객체 코드(myprog.o)로 변환한다.
- 기계어 명령을 포함하지만 자체적으로 실행할 수 있는 완전히 실행 가능한 프로그램은 아니다.
- 바이너리 파일(*.o)은 objdump같은 툴을 사용해 표시 가능
$ gcc myprog.c
$ ./a.out # a.out의 결과 파일인 .a
$ objdump -d a.out
- 링크 편집 단계는 실행 가능한 단일 파일(a.out)을 생성
- 재배치된 바이너리(*.o)와 라이브러리(.a 또는 .so)에서 생성된다.
- 결과파일인 .a에서 라이브러리 함수 사본을 포함한다.
- 애플리케이션에 의한 모든 라이브러리 함수 호출은 라이브러리 함수가 복사되는 a.out 위치에 바인딩된다. (함수 이름을 메모리 주소로 대체한다)
- 동적 라이브러리 연결시 사본을 포함하지 않고, 런타임 시 추가 연결 단계가 있다.
$ ldd a.out
- ldd 유틸리티는 실행 파일의 공유 객체 종속성을 나열한다.
- c에서 라이브러리 생성
- 헤더파일 : 라이브러리에 대한 인터페이스 정의
- .c파일 : 라이브러리의 구현체 생성
- C를 어셈블리로 컴파일
- `gcc -m32 -S simple ops.c`
- -m32(32비트 버전) 커맨드 라인 옵션을 사용해 IA32 어셈블리 생성을 지정해 IA32 어셈블리 텍스트 파일 (.s)로 컴파일
- `vim simpleops.s`
- `gcc -m32 -c simpleops.s`
- 재배치 가능한 객체 바이너리 파일(.o)로 컴파일
- `gcc -m32 -o simpleops simpleops.o`
- 32비트 실행 파일 생성
- `objdump -d simpleops.o`
- 매핑하는 기계 코드와 어셈블리 코드를 출력
- 어셈블리 코드
- 다양한 시스템에서 컴파일과 실행이 가능한 고급 언어인 C와 달리
- 어셈블리 코드는 저수준 언어이며 특정 하드웨어 아키텍처를 지정한다.
- 운영체제의 코드 일부는 종종 어셈블리 코드로 구현된다.
프로그래밍이 실행될 때 필요한 공간과 크기를 변화에 맞게 조절해서 필요한 만큼 공간을 할당하고 그 이상 필요하지 않는 공간은 반환
프로그램 메모리 구조
운영 체제, 코드, 데이터, 힙, 스택
애플리케이션 프로그래밍 인터페이스(API): C 소스 코드에 포함하는 헤더 파일(.h 파일)에 정의되는 라이브 러리다. 헤더는 라이브러리가 사용자에게 내보내는 항목을 정의한다. 이 정의에는 대개 라이브러리 함수 프로 토타입이 포함되며 타입, 상수 또는 전역 변수 선언도 포함된다.
0 시작하며
1 C 프로그래밍 언어
student2 = student1; // 복사
strcpy(student2.name, student1.name); // 한 필드 변경
printf(“%lu”, sizeof(strict studentT);
char arr[10]; arr[3] = ‘ch’; // arr[3] = lvalue arr = “hello”; // arr, 정적으로 선언된 배열의 기본 주소는 바꿀 수 없다.
struct studentT { int age; } struct studentT student1, student2; student1.age = 18; // age 필드는 lvalue student2.name = student2.name; // 정적으로 선언된 기본 주소는 바꿀 수 없다. name 필드는 lvalue가 아니다.
infile.txt 파일을 읽기 위해 a.out의 stdin을 리디렉션함
$ ./a.out < infile.txt
outfile.txt 파일을 출력하기 위해 a.out의 Stdout을 리디렉션함
$.1a.out> outfile.txt
a.out의 stdout과 stderr을 out.txt 파일로 리디렉션함
$ ./a.out & outfile.txt
세 가지 모두를 다른 파일로 리디렉션함
(‹stdin, 1› stdout, 2› stderr):
$ ./a.out < infill.txt 1› outfile.txt 2› errorfile.tt
$ gcc -E myprog.c $ gcc -E myprog. c › out $ vim out
$ gcc -S myprog. c $ vim myprog.s
$ gcc -c myprog.c $ objdump -d myprog.o
$ gcc myprog.c $ ./a.out # a.out의 결과 파일인 .a
$ objdump -d a.out
$ ldd a.out