본문 바로가기

Datastructure/[3] 데이터구조

[3] 데이터구조 ③ 구조체

728x90
반응형

일반적으로 구조체의 형태는 다음과 같다. 이때 중괄호의 마지막에 세미콜론을 붙이는 것을 주의하자.

#include <stdio.h>
#include <string.h>
struct student {//구조체 정의
   char name[10];//멤버 선언
   int score;
};

 

구조체의 정의는 구조체 안에 어떠한 변수들이 사용되는지를 명시한 것뿐으로, 정의를 한다고 헤서 멤버 변수에 데이터를 저장하기 위한 공간이 할당되는 것은 아니다.

구조체 선언

struct 자료형_이름 변수명;//struct student st1;

 

이때 구조체 자료형 이름은 구조체를 정의하기 위해 사용되는 키워드이며 구조체 자료형의 이름을 명시한다.

명시된 이름은 변수가 아니라 자료형 이름, 즉 int와 같이 정의된 구조체의 형태를 가진 '자료형'임을 주의하자. 이때 구조체의 정의에서 메모리는 멤버들의 메모리로 구성된다.

 

구조체의 선언은 전역 변수와 지역변수 선언이 존재한다. 아래의 예시를 참조한다.

struct student {//구조체 정의
   /*생략*/
}st; //전역 (구조체) 변수 선언

void func(){
  struct student st1; //지역 (구조체) 변수 선언
}

 

구조체의 정의와 선언은 아래 조건을 따른다.

① 구조체를 사용하기 위해 자료형(구조체 묶음 이름)은 보통 함수 밖에서 지역 변수로 선언한다. 

② 자료형 정의 변수 선언을 동시에 하나의 문장에서 작성할 수도 있다. ( struct student {/**/}st; )

③ 구조체 자료형의 이름은 생략 가능하나, 구조체 정의에서 전역으로 선언하여 사용한다.

구조체의 대입은 모든 멤버 변수에 대한 대입 연산을 의미한다.

구조체 멤버 변수 사용

구조체 변수는 구조체 멤버 변수 전체를 나타내고(한 묶음을 통트려 표현하는 이름), 구조체의 각 멤버에 접근하기 위해서는 구조체 멤버 연산자(.)를 사용한다.

각 멤버 변수는 각 멤버에 접근하기 위해 사용 방법이 결정된다.

[ 구조체 기본 예제 ]

예제는 아래의 [더보기] 란을 확인한다.

더보기
#include <stdio.h>
#include <string.h>
struct student {//구조체 정의
   int id;
   char name[8];
   double grade;
};

int main(){
    struct student st1 = {10,"tom",3.2};
    st1.id += 20;
    strcpy(st1.name,"alice");
    st1.name[0]= 'A';

    printf("id : %d\n",st1.id);
    printf("name : %s\n",st1.name);
    printf("grade : %.2f\n",st1.grade);
}

주소 연산자와 멤버 연산자의 경우, 멤버 연산자 (.)가 주소 연산자(&)보다 우선순위가 높다.

또한 구조체의 멤버 연산자(.) 배열의 첨자 연산자([i])는 우선순위가 같으나 결합 수칙에서 왼쪽 우선이 적용된다.

구조체 선언

정수 변수를 묶어 배열을 만들듯이, 구조체 변수를 묶어 배열을 만들 수 있다. 단 동일한 구조체 자료형만 가능하다.

구조체가 원소로 사용된 배열을 '구조체 배열'이라 한다.

 

이때 구조체 배열의 인덱스는 각각의 구조체를 표현하며 초기화 시에는 구조체 배열의 크기를 인덱스로 중괄호 선언한다. 이때 생략된 부분은 0으로 초기화하며, 특정 인덱스의 구조체 멤버를 한 번에 정의할 수 없다.

구조체 포인터

구조체 포인터는 기존의 포인터에 대한 사용법을 응용하며 우선순위에 주의한다.

구조체와 포인터가 결합되는 경우는 아래와 같다.

 

① 포인터가 구조체의 멤버로 존재하는 경우

② 구조체가 가리키는 포인터 (구조체 포인터)

③ 구조체 포인터가 배열의 원소로 사용되는 경우 (구조체 포인터 배열)

[ 포인터가 구조체의 멤버로 존재하는 경우 ]

멤버 연산자인 (.)가 포인터 참조 연산자 (*)보다 우선순위가 높으므로 *st.pname은 구조체 st의 pname이 가리키는 변수를 의미한다. 이때  st의 pname이 가리키는 변수는 pname의 첫 번째 인덱스의 참조값 'M'이다.

 

예제는 아래의 [더보기] 란을 확인한다.

더보기
#include <stdio.h>
#include <string.h>
struct student {//구조체 정의
   int id;
   char *pname;
};

int main(){
    struct student st;
    st.id = 21011898;
    st.pname = "Marry";

    printf("id : %d\n",st.id);
    printf("name : %s\n",st.pname);
    // printf("grade : %.2f\n",st[0].grade);
}

 

[ 구조체를 가리키는 포인터 (구조체 포인터) ] ⭐️⭐️⭐️

구조체를 가리키는 포인터를 '구조체 포인터'라고 한다. 즉, 구조체 포인터에는 구조체 변수의 시작 주소가 저장된다.

 

일반 포인터와 마찬가지로 구조체 포인터 변수를 선언하기 위해서는 참조 연산자를 변수 앞에 붙이면 되며,

참조 연산자를 이용해 포인터가 가리키는 구조체 변수에 접근할 수 있다.

 

간접 참조(*)를 이용해 구조체 변수의 멤버에 접근하려면 참조 연산자를 사용하고 구조체 멤버에 접근하기 위해 멤버 연산자를 사용한다.

이때 멤버 연산자의 우선순위가 참조 연산자보다 높기에 괄호를 사용한다.

 

이때 포인터 구조체를 통한 간접 참조는 매우 빈번히 발생하며 괄호를 줄이기 위해 구조체 포인터에서 사용하는 전용 참조 연산자인 (->)를 사용한다.

(*) = ㅁ->

 

예제는 아래의 [더보기] 란을 확인한다.

더보기
#include <stdio.h>
#include <string.h>
struct student {//구조체 정의
   int id;
   char *pname;
};

int main(){
    struct student st;
    struct student *pst;
    st.id = 21011898;
    st.pname = "Marry";

    printf("id : %d\n",st.id);
    printf("name : %c\n",*st.pname);

    pst = &st;
    printf("p_id : %d\n",pst->id);
    printf("P_name : %c\n",*pst->pname);
    // printf("grade : %.2f\n",st[0].grade);
}

구조체와 함수

구조체 역시 함수의 인자로 전달 혹은 반환이 가능하다. 이때 구조체를 반환하는 함수의 자료형 역시 struct 형태임을 주의하자.

struct student {//구조체 정의
   /*생략*/
};

struct student init{
  struct student st1 = {10,"tom",3.2};
  return st1;
}

구조체 포인터 인자와 반환

구조체의 값 대신에 주소를 전달하고 반환받아 구조체 대입에 들어가는 불필요한 시간 소모를 줄일 수 있다. 이때 구조체 포인터를 반환하는 함수는 반환값(구조체 포인터)과 자료형이 같도록 함수 앞 *를 작성한다.

[구조체 포인터 인자 사용 예시]

구조체 포인터 인자를 사용할 때는 '포인터의 주소'를 인자로 받아야 한다! 함수의 구조체 변수가 실인자로 전달되면 구조체 함수에서는 주소를 이용한 간접 접근을 통해 인자로 받은 구조체 내의 값을 변경할 수 있다. 

 

반면 주소로 받지 않는 구조체는 지역변수로 사용되며 인자로 받은 구조체에 접근할 수 없다.

 

예제는 아래의 [더보기] 란을 확인한다.

더보기
#include <stdio.h>
#include <string.h>
struct student {//구조체 정의
   int id;
   char name[8];
};

void init_v(struct student st){
    st.id = 0;
    strcpy(st.name," ");
}

void init_p(struct student *pst){
    pst->id = 0;
    strcpy(pst->name," ");
}

int main(){
    struct student st = {21011898,"Lee"};
    init_v(st);
    printf("값 전달 후: %d , %s\n",st.id,st.name);
    init_p(&st);
    printf("값 전달 후: %d , %s",st.id,st.name);


}

[구조체 포인터 인자 사용 예시]: 구조체 포인터 인자를 사용한 복소수 프로그램

정답 코드는 아래의 [더보기] 란을 확인한다.

더보기
#include  <stdio.h>
struct complex{//구조체 정의
    /* data */
    double n,i;//구조체 멤버 선언
}st1,st2;//구조체 선언

struct complex *larger(struct complex *a,struct complex *b){
    double abs_a,abs_b; 
    abs_a = (a->n)*(a->n) + (a->i)*(a->i);
    abs_b = (b->n)*(b->n) + (b->i)*(b->i);

    return abs_a>abs_b?a:b;
}
// struct complex add(struct complex a,struct complex b){//구조체 함수 정의

//     struct complex c;//구조체 선언
//     c.n = a.n + b.n;//구조체 a와 b의 정수부분을 더함
//     c.i = a.i + b.i;//구조체 a와 b의 허수부분을 더함
//     // printf("");
//     return c;//구조체 반환
// }
int main(){//메인 함수

    struct complex rst;//결과값 구조체 선언
    scanf("%lf %lf",&st1.n,&st1.i);//a구조체의 수 입력받음
    scanf("%lf %lf",&st2.n,&st2.i);//b구조체의 수 입력받음

    struct complex *rst_dir = larger(&st1,&st2);
    printf("lager : %.1lf + %.1lfi",rst_dir->n,rst_dir->i);
    // rst = add(st1,st2);//결과값 저장
    // printf("%.1lf + %.1lfi",rst.n,rst.i);//결과 출력
}

중첩 구조체와 자기참조 구조체

구조체 멤버에는 기본 자료형 뿐만 아니라 구조체 포인터와 같이 구조체에 관련된 자료형 역시 멤버로 사용 가능하다. 이때 각 멤버의 자료형에 따라 사용법이 결정된다.

중첩 구조체

중첩된 구조체에 대한 초기화와 멤버 참조 등은 단순히 구조체 사용법을 한 번 더 적용하면 된다.

//생략
stu.no = 20170121;
stu.s.math = 90;
stu.s.english = 80;

 

중첩 구조체를 정의할 때에는 자기 자신과 동일한 구조체 자료형은 멤버로 사용할 수 없는데, 이는 자기 자신을 정의하기 위해서는 자신이 필요하여 순환 오류에 따라 정의가 되지 않는다.

더보기
#include <stdio.h>

struct score
{
    double math;
    double english;
    double total;
    double average;
};

struct student
{
    int no;
    struct score s;
};

int main(void)
{
    struct student stu;

    stu.no = 20170121;
    stu.s.math = 90;
    stu.s.english = 80;
    stu.s.total = stu.s.math + stu.s.english;
    stu.s.average = stu.s.total / 2;

    printf("학번 : %d\n", stu.no);
    printf("총점 : %lf\n", stu.s.total);
    printf("평균 : %lf\n", stu.s.average);

    return 0;
}

자기참조 구조체

자신과 동일한 구조체는 멤버로 사용할 수 없지만 자신과 동일한 구조체의 '포인터'는 멤버로 사용이 가능하며, 자기참조 구조체의 코드는 아래와 같다.

struct abc{   
  int num;   
  int age;   
  struct abc* p;
};
728x90
반응형
댓글