[2021-02-22 월] TIL

노을·2021년 2월 22일
0

TIL

목록 보기
45/81

C언어 배열과 포인터

배열의 인덱스는 왜 0번부터?

0[a] == a[0]이건 정말 충격

  • 교환법칙: a[b] = (a+b) : 0[a] = (0+a)
  • C언어에서만 가능하다고 한다.

int a[] = {2,4,6,8}; // 전역변수, 데이터영역, a의 주소 0x10e26???
double d[] = {1.0,3.0,5.0};
int main(){
    //int a[4] = {2,4,6,8}; // 로컬변수, 스택영영, a의 주소 0x7ffe????
   
    int *a = malloc(sizeof(int)*10); //new 와 비슷, int의 10개 크기만큼 heap에 생성
    a[0] = 2; // *(a+0) == 1
    a[1] = 4;
    a[2] = 6;
    // c언어는 gc가 없으니까 수동으로 free() 해줘야함.
    
   
    printf("address: %p", a); 
    printf("%d\n",*a); // output: 2 == a[0] 
    printf("value *a: %d\n", *(0+a)); // 0+a , a+0 가능 , 교환법칙 성립, 이건 c언어에서만 성립됨.
    printf("value *a: %d\n", 0[a]); // a[b] = *(a+b) 정수연산이 되네..?, 덧셈의 교환법칙이 성립하고..
    printF("value *(a+2): %d\n", 2[a]) //output 6 == *(a+2)
        
        // 이렇게 배열을 만든 장점은
         -- 배열의 임의의 원소에 접근할 때 복잡도가 1.
         -- 단순히 주소의 덧셈 연산만으로 접근이 가능.
         -- 이게 c언어의 배열이라는 것에 정체
         -- 공간복잡도도 줄일 수 있음. 메모리에 주르륵 쌓으면 됨.
       
    
    int *b = a;
    printf("a == b ? %d\n", a == b); // output: true

    b=b+1; // b의 주소값이 한 칸 더가게됨.
    printf("address b: %p\n", b+1);
    printf("value *b : %d\n",*(b+1)); //output: 4
    
    double *p = d;  // double형 포인터
    printf("addres %p\n", p);
    printf("address p +1 %p \n", p+1);
    printf("address p +1 %f \n", *(p+1));
    
    free(a); // 자바의 IO close를 비슷. 메모리 해제의 책임을 개발자가 쥔다. 
    // 안할경우 Memory leak 발생
    // 장기적으로 프로그램이 죽는 원인이 됨.
    return 0;
}

C언어의 함수형 프로그래밍

  • 함수의 주소를 포인터로 접근
  • 이거 정말 신기했다.
  • C언어는 정말 뭐든 할 수 있구나! 그만큼 파일럿이 잘해야겠지만 놀랍다..
  • C언어는 만들어질 때, 프로그래머는 천재를 뛰어넘어 Almighty하다라는 게 가정이었다고 한다.
 
void each(int *a, int size, void (*apply)(int){ // void (*apply)(int) :: 매게변수가 int
    for(int i=0; i<size;i++){
        apply(a[i]);
    }
}
void foo(int x){
    printf("x: %d \n", x);
}
int main(){ 
    int a[] = {1,2,3};
    foo(a[0]);
    printf("P: %p\n", foo);  // 코드영역에 존재함.
    each(a, 3, foo); // 
    return 0;
}

Java Object 클래스 - clone() 메서드

  • 사용하기 위해서 구현 해야하는 Cloneable 인터페이스 안에 아무것도 없다.
   	public interface Cloneable {} 
  • 자바 개발자 Josh Bloch의 인터뷰에 따르면 설계에 결함이 있어 사용을 지양한다고 함.

Bill Venners: In your book you recommend using a copy constructor instead of implementing Cloneable and writing clone. Could you elaborate on that?
Josh Bloch: If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken. There are a few design flaws, the biggest of which is that the Cloneable interface does not have a clone method. And that means it simply doesn't work
...

  • 얇은 복사
    • ex) cloe()..
    • 단순히 인스턴스의 변수 값만을 복사함, 참조가 가리키는 객체의 인스턴스는 복사되지 않음.
  • 깊은 복사
    • clone()카테고리는 아니지만, 깊은 복사를 구현할 수 있는 종류가 3가지 있다.
    1. 직접 값을 넣어서 객체 새로 생성
    2. 복사 생성자 또는 복사 패토리 메서드
    3. Clonable 인터페이스를 구현하여 clone() 활용
      • 기본 clone()은 얇은 복사이므로, 약간의 커스텀이 필요하다.
  • clone() 햇갈렸던 것
 @Override
    public Circle clone(){ //m 깊은 복사, 공변  반환타입 ( Object -> Circle )
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        Circle c = (Circle)obj;
        c.p = new Point(this.p.x, this.p.y);
        return c; // 공변  반환타입
    }

나는 obj = super.clone() 이 부분이 단순히 Object obj = Object target 이런 느낌인줄 알아서, 아래 깊은 복사를 위한 코드들이 의미가 있나 싶었는데 알고보니 clone() 내부적으로 객체를 복사해서 새로 생성하는 구조였다.

  • 이해가 안돼서 해본 코딩


public class CloneHashCodeCheck {
     static class Point implements Cloneable{
        int x, y;
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        @Override
        public String toString() {
            return "Point{" + "x=" + x + ", y=" + y + '}';
        }
        @Override
        public Object clone(){ 
            Object obj = null;
            try{
                obj = super.clone();
            }
            catch (CloneNotSupportedException e){
                e.printStackTrace();
            }
            return obj;
        }
    }
    public static void main(String[] args) {

        Point original = new Point(3, 5);
        Point copy = (Point)original.clone(); // 복제
        System.out.println(original);
        System.out.println(copy);

        System.out.println("==================");

        System.out.println("original.hashCode(): "+ original.hashCode());
        System.out.println("copy.hashCode(): "+copy.hashCode());

    }

}


================== OUTPUT ==================
    
Point{x=3, y=5}
Point{x=3, y=5}
==================
original.hashCode(): 460141958
copy.hashCode(): 1163157884 // 다름을 확인할 수 있음.


느낀점

  • C언어 문법은 어느정도 안다고 생각했는데, 배열의 교환법칙은0[a] == a[0] 과 함수를 포인터로 줄 수 있다는 건 정말 충격이었다.. 학부과정 내내 C언어를 쓰다보니까 잘알고 있다고 착각을 했던 것 같다.. 반성해야지 부끄럽다.
  • 그리고 그동안 일반적인 배열의 인덱스가 왜 0번부터 시작하지? 라는 생각을 단 한번도 해본적이 없는데, 오늘 수업을 통해 생각을 한번 해볼 수 있었고, 자료를 찾아보고 정리하는 시간을 가졌다. 메모리 시작번지가 0번이라는 등..여러 이유들과 관련 수학적 증명이 있긴 하지만 예전에 호눅스님이 말씀하신 것처럼 이또한 프로그래머들이 쓰다보니까 경험적으로 이게 좋더라~ 하면서 이런 개념이 자리가 잡힌 것 같다.
  • clone() 파트가 예전에 코드스쿼드 가입? 미션중에 루빅스 큐브를 풀 때 객체 배열을 하는 데 애먹었던 기억이 있어서 오늘 시간 투자를 많이했다. 몇가지 난해해서 좀 어려웠지만 프레디 한테 질문을 해서 힌트를 얻을 수 있었다. 덕분에 clone()을 지양해야하는 이유와 깊은복사의 여러가지 좋은 방법들 특히 복사 팩토리 메서드 방법은 너무 신기했고 앞으로 잘 써먹을 것 같다.
  • 오늘도 재밌게 공부를 한 것 같아서 뿌듯하다, 많은 성과는 없지만 천천히 주변을 공부해가는 것도 좋은 공부법이라고 생각한다. 남들이 스프링하고 코딩테스트 준비한다고 조급해하지말고 계속 내 스피드대로 천천히 살피면서 공부하자.

참고

profile
카르페디엠

0개의 댓글