배열과 포인터(c언어)

Jung·2021년 2월 26일
0

수업

목록 보기
3/20

C언어의 배열

배열의 인덱스가 0부터 시작하는 이유:
address base + index * k
(TCPL 책읽으면 알수있음)

(java) int[] a : a라는 변수의 타입이 정수 배열타입이다.
(c) int a[4] = {2,4,6,8}

배열 선언을 int main(int argc, char const *arv)에 선언하면, 배열을 스택에 쌓는것이기 때문에 스택에 생긴다. 주소는 0x7f.(스택 위치)

int main (~~)의 바깥에 배열을 선언하면, 주소가 0x10으로 시작하며, 데이터 영역에 있고 전역변수이다. (java로 따지만 static 변수이라, 메소드영역에 저장된다.)

a: printf("%d\n", a) 하면 배열이 저장된 주소가 출력된다.
a: a가 가리키는 곳의 값. printf("%d\n", a)로 하면, 값을 출력해주는 de-referencing. pointer가 가리키는 pointee.
2가 출력된다. 즉 *a주소로가면 철번째 값을 쓸수있다는 뜻이다.
그렇다면 두번째 값을 쓰려면 어떻게 해야할까?

int b =a;
a==b는 true.
printf("%p\n", b) 주소 출력 (0x10~~20)
printf("%d\n",
b) 값 출력 (2)

printf("%p\n", b+1) 주소 출력 (4바이트만큼 늘어난 것이 출력)
printf("%d\n", *(b+1)) 값 출력 (4)

double d;
double p =d; (포인터 형식도 d에맞춰 double형으로 해줘야)
printf(%p, p)
printf(%p, p+1) 주소출력 (8바이트
printf(%f,
(p+1))
더블포인터란, 가리키는 곳에 double이 있다는 것이다.
int포인터에서 +1만큼 가면 int크기인 4바이트만큼, double포인터에서 +1만큼가면 double크기인 4바이트만큼 증가해있다.

배열 a가 있을 때,
*(a+2)와 a[2]는 서로 일치한다. 완전한동치이다.
c언어에서 배열은 일종의 포인터 변수이고, 배열의 첨자연산은 포인터가 가리키는 곳에 연산을 해서 읽는것이다.

배열을 1부터시작하면 메모리가 낭비된다. 1부터시작하면 메모리 시작하는 곳에 값을 넣을 수 없고 ..

(a+0) 와 (0a)교환법칙성립하므로 잘나온다.
a[0]와 0[a]는 2로 잘 나온다. a[b]와
(a+b)가 같으므로 윗줄의 법칙을 적용해서 성립.

주소의 덧셈연산만으로 배열에 접근가능하다. 메모리에 주루룩 쌓아 공간복잡도를 낮출수있다.

int a = malloc(sizeof(int)4) (malloc은 java의 new와 같다)
heap에 만들어진다.
a[0]=2; a[1]=4; 같이 배열일일이 저장가능하다.
c는 GC가 없기에 수동으로 메모리 해제해야한다. free(a);
메모리 해제 책임을 개발자가 갖고, 그렇지 않으면 메모리 누수가 발생해 계속 실행하면 프로그램 메모리가 점점 커져 프로그램이 죽는 원인이 된다.

c언어의 배열의 굉장한 단점이 있다. arrayList가 배열보다 좋은 점이 하나 있다면, size를 추후에 조절할 수 있다. c언어배열은 기존 것을 지우고, 사이즈 allocate (malloc)하고, 원래 것 복사하고, 등등... size조절과정이 아주 복잡하다.


Java의 람다

함수형 프로그래밍을 지원하기 위해 만들어졌다.
input이 같으면 output이 같아야한다.
Java의 함수는 일급객체가 아니기 때문에 메소드를 매개변수로 쓸 수 없다.
그에 반해 C언어는 함수의 포인터를 넘겨줄 수 있다.
int의 포인터란, 가리키는 곳에 int가 있으면 int의 포인터인것처럼 함수의 포인터는 가리키는 곳에 함수가 있으면 된다.
c언어는 매개변수로 포인터를 쓸수있기 떄문에 함수가 일급객체이다.

void foo(int x){
printf(%d, x)
}

int main (~~){
a[] = {1,2,3};
foo(a[0]);
printf(%p, foo)
}

%p는데이터 영역과 가깝고 코드 영역에 출력된다. 가장 위에서부터 코드 데이터 힙 스택이다.
함수의 위치가 출력된다.
C언어를 만들때 가정이, 우리 프로그래머는 전지전능하다고 생각해서 모든 것이 가능하게 만들었다.

void each(int *a, int size, void (*apply)(int)){
	for(int i =0; i<size, i++){
    foo(apply(a[i]);
    }
}

void foo(int x){
printf("%d", x)
}

int main (~~){
a[] = {1,2,3};
foo(a[0]);
printf("%p", foo)
each(a,3,foo);
}

apply 매개변수가 int란 뜻...??
하면 x에 1, 2,3이 각각 프린트 된다.


메서드를 매개변수로 넘기기 위해 인터페이스에 메서드 하나만을 선언. 이것이 람다의 조상인데 지저분하고 불편하므로 람다가 된다.

@Functional Interface는 메서드가 단 하나여야 한다.

매개변수 단 하나 함수로 받으려고 인터페이스 만드는 것 불편하므로, 기본으로 사용할 수 있는 인터페이스 여럿을 만들어놨다. 예를 들면 Runnable(쓰레드이기도 함.) 메서드하나뿐이고 run()이다. 매개변수없고 리턴타입도 없는 run()을 Runnable이라고 하겠다고 한것이다.
이 다음 매개변수 없고 리턴타입 있는 것을 Supplier.

매개 없고, 리턴 void: Runnable
매개 0, 리턴 있음: Supplier
매개 1, 리턴 void : Consumer
매개 1, 리턴 있음: Function.
매개 2, 리턴값: BifFunction

따라서 FUnction을 많이 사용함.
forEach에 들어가는 애들은 Consumer이 들어간다. 매개변수가 있고 리턴이 없기 때문이다.
우리가 사용할 수 있는 미리 정의된 람다는 매개변수 2개까지 받는다. 람다로 매개변수 3개 받으려면, 만들어서 써야한다. Functional Interface로 직접 만들면 된다.
JDK에서 기본적으로 사용하는 람다는 매개변수가 두개까지이다.

a.forEach(System.out::println);
에서 ::은 축약표현이다.

스트림: 원소를 하나하나 처리할 수 있는 무언가.. 데이터의 연속적인 흐름...
스트림 쓸 떄 두번 연속 쓸 수 없다. 한번 흘러가면 끝난다. 쓰는 이유, 장점은 무엇이냐면, 간결해지고 가독성이 높아진다.
for Loop보다 성능이 나쁘다. 함수형이므로 side effect가 없어 병렬처리 할 수 있다. parallel stream 검색해봐라.
스트림에 순서가 있긴 하지만, 순서로 생각하면 안된다.

매개변수를 다 복사한다. 간단한 for loop을 복잡하게, 여러번 돌 일이 있는데, 스트림은 한번 돈다.
lazy loading이란게 있어서


공부할 키워드

  • 디자인패턴(싱글턴, 팩토리 등등..)

0개의 댓글