구조체

강한친구·2022년 2월 11일

C / CPP

목록 보기
2/19

분명 포인터만 정리하고 안하려고했는데 구조체를 만나서 이것도 정리할겸 글을 하나 더 쓰기로 하였다.

구조체란

파이썬에서도 쓸수는 있는 기능이라고 알고있는데 한번도 써본적이 없고, 자바의 class랑 매우 비슷하다고 생각된다.

우선 기본 사용예부터 보겠다.

#include <stdio.h>
int copy_str(char *dest, char *src);

struct book {
    char name[20];
    char auth[20];
    char pub[20];
    int borrowed;
};

int main() {
    struct book bk;
    copy_str(bk.name, "Book Name");
    copy_str(bk.auth, "Author");
    copy_str(bk.pub, "Publisher");
    bk.borrowed = 0;

    printf("%s \n", bk.name);
    printf("%s \n", bk.auth);
    printf("%s \n", bk.pub);
    printf("%d \n", bk.borrowed);

    return 0;
}

int copy_str(char *dest, char *src) {
    while (*src) {
        *dest = *src;
        dest++;
        src++;
    }
    *dest = '\0';
    return 1;
}

책 제목、작가, 출판사를 받고 기록하는 구조체이다. java의 class와 매우 유사하다. class처럼 상속구조를 만들수도 있다 (뒤에 나온다.)

이처럼 복수의 자료형을 한번에 정리할때 유용하게 사용할 수 있는 도구이다. struct book bk를 bk02로 설정한다면 또 다른 구조체를 쓸 수 있는것이다.

명심할점은 구조체 struct역시 자료형으로 취급되며

구조체 포인터

구조체도 포인터가 있다.

#include <stdio.h>
int copy_str(char *dest, char *src);

struct human {
    char name[20];
    int age;
    int gender;
};

int main() {
    struct human hm;
    struct human *ph = &hm;
    return 0;
}

human 구조체를 hm이라 선언하고 똑같이 구조체 자료형포인터를 설정하고 그 포인터에 hm의 주소 (&hm)을 넣어준다.

그렇다면 이를 어떻게 활용할 수 있을까? 방법은 두개이다.

  1. (*ph).age 활용
    포인터를 통해 값에 접근할 수 있음은 이미 포인터실습을 통해서 깨우쳤을것이다. 다만 주의할점은 꼭 포인터앞에 괄호를 사용해야한다는 것이다.
#include <stdio.h>
int copy_str(char *dest, char *src);

struct human {
    char name[20];
    int age;
    int gender;
};

int main() {
    struct human hm;
    struct human *ph = &hm;
    
    (*ph).age = 30;
    return 0;
}

이러면 ph의 age가 30으로 지정된다.

하지만 좀 불편해서 우리는 다른 방법을 주로 사용한다

  1. ph->age = 30;
#include <stdio.h>
int copy_str(char *dest, char *src);

struct human {
    char name[20];
    int age;
    int gender;
};

int main() {
    struct human hm;
    struct human *ph = &hm;
    
    ph->age = 30;
    return 0;
}

-> 화살표로 보이는 이 기호를 통해 지정할 수 있는것이다.

여기까지가 구조체의 포인터이다. 기존의 포인터와 크게 다를게 없다.

구조체 내부의 포인터?

#include <stdio.h>
struct test {
  int c;
  int *pointer;
};
int main() {
  struct test t;
  struct test *pt = &t;
  int i = 0;

  t.pointer = &i;

  *t.pointer = 3;

  printf("%d \n", i);

  *pt->pointer = 4;

  printf("%d \n", i);
  return 0;
}

구조체 내부에 포인터가 있는 경우도 기존의 포인터와 동일하게 작동한다.
구조체 포인터가 구조체를 포인팅하고(pt = &t)
pointer의 값을 구조체 선언 t, 혹은 구조체 포인터
pt를 통해서 수정할 수 있다.

유의할점은 pt->pointer는 (pt->pointer)의 값이라는 뜻이다. ->, .같은 연산자들은 *보다 우선순위에 있다.

구조체 복사

#include <stdio.h>
struct test {
  int c;
  int *pointer;
};
int main() {
  struct test t, t2;
  t1.c = 1;
  t2 = t1;
}

구조체도 기존의 변수로 int a = 0; b = a; 하는것처럼 동일하게 할 수 있다.

구조체를 인자로

#include <stdio.h>

struct test {
    int age;
    int gender;
};

int add_one(int *a);
int set_human(struct test *a, int age, int gender);


int main() {
    struct test ta;

    set_human(&ta, 10, 1);

    printf("%d, %d\n", ta.age, ta.gender);

    return 0;
}

int add_one(int *a) {
    *a += 1;
    return 0;
}

int set_human(struct test *a, int age, int gender) {
    a->age = age;
    a->gender = gender;

    return 0;
}

구조체를 변수처럼 취급하기에 우리는 구조체를 함수의 변수로 사용할 수 있다.

인자로 구조체 포인터를 설정하고, 구조체 변수 ta의 주소 &ta를 함수의 인자로 넣어서 작동하게 만드는것이다.

구조체 안의 구조체

#include <stdio.h>

struct employee {
    int age;
    int wage;
};

struct company {
    struct employee data;
    char name[10];
};

int main() {
    struct company TS;
    
    TS.data.age = 31;
    TS.data.wage = 10000;

    printf("%d, %d\n", TS.data.age, TS.data.wage);

    return 0;
}

Java의 상속처럼 구조체도 구조체안에 구조체가 있을 수 있다.
위의 예시처럼, company 구조체 안에 employee 구조체를 data로 선언하고 이를 main함수에서 company.data.age같은 방식으로 접근할 수 있는것이다.

기타 구조체의 기능

#include <stdio.h>

struct test {
	int a;
    int b;
    
} t;

int main() {
	t.a = 10;
    t.b = 20;
    
    return 0;
 }
 

이런식으로 구조체 뒤에 변수명을 써주면 해당 변수가 바로 구조체로 선언되게 된다.

#include <stdio.h>

struct test {
	int a;
    int b;
    
} t = {10, 20};

int main() {
	t.a = 10;
    t.b = 20;
    
    return 0;
 }
 

또 이런식으로 뒤에 struct안의 변수들의 값을 순서대로 적어주면 선언과 동시에 값 설정도 가능하다.

Union

공용체는 그렇게 자주사용하는 기능은 아니라는데 생각보다 유용하다고 하다. 공용체 안에 선언된 변수가 같은 주소를 공유하는것이다. 즉 하나의 값을 바꾸면 다른값도 변한다.

#include <stdio.h>
union A {
  int i;
  char j;
};
int main() {
  union A a;
  a.i = 0x12345678;
  printf("%x", a.j);
  return 0;
}
// 출처 https://modoocode.com/71

이와 관련해서 빅엔디안, 리틀엔디안같은 메모리 읽는 방식에 관한 설명이 있는데 나중에 따로 하겠다.

Enum

파이썬에서는 Dict기능을 써서 키 별로 값을 정해줄 수 있는데, c에서는 dict를 만들기 쉽지 않다.

dx dy문제들을 풀때 이 dict형이 유용하게 쓰일 수 있는데, c에서는 enum을 쓰면 쉽게 풀 수 있다.

enum = { N, E, W, S };

이런식으로 선언하게되면 N은 0, E는 1..같은 값을 가지게 된다.

만약

enum = { N = 4, E, W, S };

로 선언하게 된다면, E = 5, W = 6 같이 선언되게 된다.

마치며

이제 c의 핵심기능은 대부분 배웠으니, 알고리즘 문제를 풀어보면서 언어에 적응하고 cpp를 공부해봐야한다.

0개의 댓글