호우동의 개발일지

Today :

article thumbnail

배열

  • <index , element> 쌍의 집합
    • 각 항목의 값은 인덱스 번호로 위치가 계산되어 참조
    • 직접 접근 방식, 접근 시간 복잡도 : O(1)

 


ADT

객체 : < Index, element> 쌍의 집합

연산

  • create(size) ::= size개의 요소를 저장할 수 있는 배열 생성
  • get(A, i) ::= 배열 A의 i번째 요소 반혼
  • set(A, i, v) ::= 배열 A의 i번째 위치에 값 v 저장

 


1차원 배열의 주소

  • 가장 첫 번째 인덱스 base
  • 인덱스를 n이라고 했을 때 arr [n] = base + nsizeof(자료형)
  • ex) int A [6]
    • A [0] : base
    • A [2] : base + 2*sizeof(int)

 


2차원 배열의 주소

2차원 배열 그림
2차원 배열 그림

  • 현재 사용하는 것은 행우선 배열(row-major order array)

 


Call by value vs Call by reference

  • 배열을 매개변수로 전달하는 것은 주소를 전달 → Call by reference
#include <stdio.h>

void callByValue(int x){
    x += 1;
}
void callByReference(int a[],int len){
    for(int i = 0; i< len; i++){
        a[i] += 1;
    }
}

int main(){
    int x = 1;
    int a[5] = {1,1,1,1,1};
    int len = 5;
    printf("전 x = %d, 전 %d %d %d %d %d\n",x,a[0],a[1],a[2],a[3],a[4]);
    callByValue(x);
    callByReference(a,5);
    printf("후 x = %d, 전 %d %d %d %d %d",x,a[0],a[1],a[2],a[3],a[4]);
}

출력 결과
출력 결과

  • Call by Value : 매개변수의 복사가 일어나 원본 x에 영향을 주지 못함
  • Call by Reference : 매개변수의 주소를 참조했기 때문에 원본 배열의 영향을 줌

 


구조체

  • 타입이 다른 데이터를 하나로 묶는 방법

구조체 사용 방식

  • 구조체의 선언과 구조체 변수의 생성
struct person{
    int age;
    float height;
    char* name;
}
int main(){
    struct person p;
}

 

  • typedef 이용
//person 생략 가능
typedef struct person{
    int age;
    float height;
    char* name;
}person;

int main(){
    person p;
}

 


배열과 구조체 응용 구현 : 다항식

다항식
다항식

다항식의 모든 항에 배열 저장

모든항에 배열 저장
모든 항에 배열 저장

  • 모든 차수에 대한 계수값을 배열로 저장
  • 하나의 다항식을 하나의 배열로 표현
#define MAX_DEGREE 100
typedef struct {
    int degree;
    int arr[MAX_DEGREE];
}polynomial;

int main(){
    polynomial poly = {5,{10,0,0,0,6,3}};
}
  • 장점 : 다항식의 각종 연산 간단 구현 가능
  • 단점 : 대부분의 항의 계수가 0이면 공간 낭비가 심해짐

 


다항식 덧셈 연산 구현

#include <stdio.h>
#define MAX_DEGREE 100
typedef struct {
    int degree;
    int arr[MAX_DEGREE+1];
}polynomial;

polynomial add1(polynomial a, polynomial b){
    polynomial c;
    int a_degree = a.degree;
    int b_degree = b.degree;

    int aPos = 0,bPos = 0,cPos = 0;
    c.degree = (a.degree > b.degree)? a.degree : b.degree;

    while(aPos <= a.degree && bPos <= b.degree){
        if(a_degree > b_degree){
            c.arr[cPos++] = a.arr[aPos++];
            a_degree--;
        }
        else if(a_degree == b_degree){
            c.arr[cPos++] = a.arr[aPos++] + b.arr[bPos++];
            a_degree--;
            b_degree--;
        }
        else{
            c.arr[cPos++] = b.arr[bPos++];
            b_degree--;
        }
    }
    return c;
}
int main(){
    polynomial a = {5,{10,1,2,3,4,5}};
    polynomial b = {3,{10,1,2,3}};
    polynomial c = add1(a,b);
    printf("%d\n",c.degree);
    for(int i = 0; i <= c.degree; i++) printf("%d ",c.arr[i]);
}

출력 결과
출력 결과

 


다항식 출력

void poly_read(polynomial a){
    for(int i = 0; i < a.degree; i++){
        printf("(%dx^%d)+",a.arr[i],a.degree-i);
    }
    printf("%d",a.arr[a.degree-1]);
}
int main(){
    polynomial a = {5,{10,1,2,3,4,5}};
    poly_read(a);
}

출력 결과
출력 결과

 


포인터

  • 다른 변수의 주소를 가지고 있는 변수
  • & 연산자 : 변수의 주소 추출
  • * 연산자 : 포인터가 가리키는 곳의 내용을 추출
int main(){
    int a = 32;
    int *p; //포인터 선언
    p = &a; // 변수 a의 주소 참조
    printf("%d\n",*p);
    *p = 4; // 포인터 p가 가리키는 내용 변경
    printf("%d",*p);
}

출력 결과
출력


자체 참조 구조체

  • 필드 주에 자기 자신을 가리키는 포인터가 한 개 이상 존재하는 구조체
  • 연결 리스트나 트리에 많이 등장
#include <stdio.h>

typedef struct{
    int age;
    Person* link; // 자체 참조
    char test;
}Person;

 


포인터의 포인터

포인터의 포인터 참조 그림
포인터의 포인터 참조 그림

#include <stdio.h>

int main(){
    int a = 1;
    int *p;
    int **pp;
    p = &a; // 변수 a의 주소 참조
    pp = &p; // 포인터 p의 주소 참조

    printf("a의 값 : %d p의 값 : %d pp의 값 : %d\n",a,*p,**pp);
    printf("변수 a의 주소 : %d p가 가리키는 주소 : %d *pp가 가리키는 주소 : %d\n",&a,p,*pp);
    printf("p의 주소 : %d pp가 가리키는 주소 : %d",&p,pp);
}

출력 결과
출력 결과

  • 모두 변수 a의 주소를 참조하고 있기 때문에 값은 1로 같다
  • 두 번째 출력에서 주소가 모두 같은 것을 확인할 수 있다.
  • 세 번째 출력은 p의 주소인데, 이는 pp가 중간에 p를 한번 참조했다가 다시 한번 참조하는 이중포인터라는 뜻

 


포인터 관련 연산자

  • *p++ : 포인터가 가리키는 값을 가져온 다음, 포인터를 한 칸 증가
  • *p-- : 포인터가 가리키는 값을 가져온 다음, 포인터를 한 칸 감소

포인터 관련 연산자
포인터 관련 연산자

  • (*p)++ : 포인터가 가리키는 값을 증가
  • **pp : 포인터를 가리키는 포인터
  • void *p : p는 아무것도 가리키지 않는 포인터
  • void (*f)(int) : f는 함수를 가리키는 포인터
    • 포인터의 형변환 : 필요할 때마다 형변환이 가능
      • void *ppi = (int *) p;

 


함수 포인터 매개변수

  • 포인터는 주소를 참조하는 것이기 때문에 당연히 Call by Reference
void swap(int* x, int* y){
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
int main(){
    int a = 1,b = 2;
    swap(&a,&b);
    printf("a = %d b = %d",a,b);
}

출력 결과
출력 결과

 


포인터 사용 시 주의사항

  • 포인터가 아무것도 가리키고 있지 않을 때는 NULL로 설정
    • int *pi = NULL; → 초기화가 안된 상태에서 사용 금지
  • 포인터 타입 간의 변환 시에는 명시적인 타입 변환 사용
int *pi;
float *pf;
pf = (float*)pi;

 


동적 메모리 할당

  • 정적 메모리 할당
    • 메모리의 크기를 프로그램 시작 전 결정 → 도중에 크기 변경 불가
    • 처음 결정된 크기보다 입력이 더 크면 처리 못하고 더 작으면 메모리 낭비

  • 동적 메모리 할당
    • 프로그램의 실행 도중에 메모리 할당받는 것
    • 필요한 만큼만 할당받고 필요한 때의 사용하고 반납 → 메모리 효율적 사용
    • Heap(힙) 사용

 


동적 메모리 할당 코드

  • (자료형 *) malloc(sizeof(자료형) : 동적 메모리 할당
  • free(동적할당한 변수) : 동적 메모리 반납
  • sizeof(자료형): 변수나 타입의 크기 반환(바이트 단위)
struct Person{
    int age;
    char* name;
};

int main(){
    struct Person *p;
    p = (struct Person*)malloc(2*sizeof(struct Person));

    p ->age = 1;
    p->name = "high";
    (p+1) ->age = 2;
    (p+1) -> name = "How";

    free(p);
}