1. 기본 구조체 포인터 (* 선언에 없음)

struct로 구조체를 만드는데 java로 따지면 hashmap정도가 되겠다.

마침표 .을 이용해서 값을 저장하고 꺼내올 수 있다.

#include <stdio.h>
#include <string.h>

struct tagAddress
{
	char name[30];		// 이름
	char phone[20];		// 전화
	char address[100];	// 주소
};

void main( void ) 
{
	struct tagAddress ad;

	strcpy( ad.name, "홍길동" );
	strcpy( ad.phone, "02-1234-5678" );
	strcpy( ad.address, "서울시 양천구 목동아파트 13단지" );

	printf( "이름 : %s \n", ad.name );
	printf( "전화 : %s \n", ad.phone );
	printf( "주소 : %s \n", ad.address );
}

결과값

이름 : 홍길동
전화 : 02-1234-5678
주소 : 서울시 양천구 목동아파트 13단지

 

2.  구조체를 포인터 구조체로 선언함. 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

struct tagAddress
{
	char name[30];		// 이름
	char phone[20];		// 전화
	char address[100];	// 주소
};

void main(void)
{
	struct tagAddress ad; //일반 구조체변수 선언
	struct tagAddress *pad; //포인터 구조체변수 선언
	//포인터 구조체를 선언하면 일반 구조체변수를 포인터 구조체에 저장 해야한다.
	pad = &ad;

	strcpy(ad.name, "홍길동");
	strcpy(ad.phone, "02-1234-5678");
	strcpy(ad.address, "서울시 양천구 목동아파트 13단지");

	printf("이름 : %s \n", ad.name);
	printf("전화 : %s \n", ad.phone);
	printf("주소 : %s \n", ad.address);
	printf("\n======포인터 구조체 적용====\n\n");

	//값 입력
	strcpy((*pad).name, "홍길2");
	strcpy((*pad).phone, "02-1234-5672");
	strcpy((*pad).address, "서울시 양천구 목동아파트 2단지");

	printf("이름 : %s \n", pad->name);
	printf("전화 : %s \n", pad->phone);
	printf("주소 : %s \n", pad->address);
}

 

결과값

이름 : 홍길동
전화 : 02-1234-5678
주소 : 서울시 양천구 목동아파트 13단지

======포인터 구조체 적용====

이름 : 홍길2
전화 : 02-1234-5672
주소 : 서울시 양천구 목동아파트 2단지

 

3. 구조체 안에 포인터 변수가 있는 형태

구조체 안에 포인터 변수를 알아보자

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

struct tagAddress
{
	char name[30];		// 이름
	int phone;		// 전화
	char address[100];	// 주소
	char *email[100];  // 이메일  //구조체안에 변수는 포인터변수던 그냥이던 똑같이 변수를 넣고 반환할 수 있다.
	int *byear; //생년월일  //구조체안에 변수는 포인터변수던 그냥이던 똑같이 변수를 넣고 반환할 수 있다.
};

void main(void)
{
	struct tagAddress ad; //일반 구조체변수 선언
	struct tagAddress *pad; //포인터 구조체변수 선언
	//포인터 구조체를 선언하면 일반 구조체변수를 포인터 구조체에 저장 해야한다.
	pad = &ad;

	//값 입력
	strcpy(ad.name, "홍길동");
	//strcpy(ad.phone, "0212345678"); XX 사용안됨 int 형이므로 =이용

	ad.phone = 101234; //값 처음에 0이 들어가면 안된다.
	
	strcpy(ad.address, "서울시 양천구 목동아파트 13단지");
	strcpy(ad.email, "dream1@nate.com");
	ad.byear = 97090911; //값 처음에 0이 들어가면 안된다.

	//출력

	printf("이름 : %s \n", ad.name);
	printf("전화 : %d \n", ad.phone);
	printf("주소 : %s \n", ad.address);
	printf("이메일 : %s \n", ad.email);
	printf("생년월일 : %d \n", ad.byear);

	printf("\n======포인터 구조체 적용====\n\n");

	//값 입력
	strcpy((*pad).name, "홍길2");
	ad.phone = 201234; //값 처음에 0이 들어가면 안된다.
	strcpy((*pad).address, "서울시 양천구 목동아파트 2단지");
	strcpy((*pad).email, "dream1@nate.com2");
	ad.byear = 97090912; //값 처음에 0이 들어가면 안된다.

	//출력
	printf("이름 : %s \n", pad->name);
	printf("전화 : %d \n", pad->phone);
	printf("주소 : %s \n", pad->address);
	printf("이메일 : %s \n", pad->email);
	printf("생년월일 : %d \n", pad->byear);
}

결과값

이름 : 홍길동
전화 : 101234
주소 : 서울시 양천구 목동아파트 13단지
이메일 : dream1@nate.com
생년월일 : 97090911

======포인터 구조체 적용====

이름 : 홍길2
전화 : 201234
주소 : 서울시 양천구 목동아파트 2단지
이메일 : dream1@nate.com2
생년월일 : 97090912

 

4. 구조체안에 포인터멤버가 있고 메모리를 할당하는 형태

코드를 보기전에 설명을 하면은

 

* 일반 선언된 구조체안에 멤버에 값을 넣어줄 때는

d1.numPtr = &num, d1.c = 'x'형태로 쓴다. (포인터 멤버(*)든 일반 멤버든 상관 없음)

 

* 포인터로 선언된 구조체안의 멤버에 값을 넣어줄 때는

d2->numPtr = &num, d2->c = 'y' 형태로 쓴다. (포인터 멤버(*)든 일반 멤버든 상관 없음)

또는 (*d2).numPtr, (*d2).c 형태로 써도 된다.

 

화살표 꺽쇠모양은 -> 이 포인터의 값(*d2)을 불러와서 그 값안의 멤버(numPtr)를 호출하는 기능이다.

따라서 꺽쇠는 포인터로 선언된 구조체에만 써야한다.

 

 

(*d2).numPtr을 쉽게 설명하면  dr이라는 구조체를 호출하여 자물쇠*를 푼다. 그뒤 푼값의 numPtr을 가르킨다.

참고로 자물쇠를 풀지않은 d2의 멤버가 정수형일 경우 주소를 가르킨다.

 

그래서 정수형일 경우는 &num처럼 &를 붙혀줘야한다.

 

꺽쇠는 *를 붙혀 자물쇠를 풀고 값을 가르키는 2가지 기능이 있다는 것을 알아두자.

 

printf로 값을 볼 때는 주소값이 아닌 값을 봐야한다.

 

일반 선언된 구조체는

printf("*d1.numPtr : %d\n", *d1.numPtr);

printf("d1.c : %c\n", d1.c);

로 볼수 있다.

 

d1.numptr자체는 주소이므로 *를 붙혀서 자물쇠를 풀어줘야 한다.

 

포인터로 선언된 구조체는 아래와 같이 프린트해야 한다.

printf("*d1->numPtr : %d\n", *d2->numPtr);

printf("d1->c : %c\n", d2->c);

 

 

풀소스 코드

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

typedef struct Data_s {
    char c;
    int* numPtr;    // 포인터
}Data;

int main()
{
    int num = 10;
    Data d1;    // 구조체 변수 선언
    Data* d2 = malloc(sizeof(Data));  // 구조체 포인터 변수 선언 및 메모리 할당

    //값 저장
    d1.numPtr = &num; //구조체 d1의 멤버인 numPtr에 num의 메모리 주소를 전달
    d2->numPtr = &num; //d2에 있는 Data의 메모리 주소를 역참조하여 멤버인 numPtr에 num의 메모리 주소 전달
    
    d1.c = 'x';
              //
    printf("d1.c : %c\n", d1.c);
    printf("*d1.numPtr : %d\n", *d1.numPtr);   // 구조체의 멤버를 역참조
    
    printf("*d2->numPtr : %d\n", *d2->numPtr);  // 구조체 포인터의 멤버를 역참조
    
    //아래처럼도 쓸수 있음
    //printf("*d2->numPtr : %d\n", *((*d2).numPtr));
    
    printf("sizeof(Data) : %d\n", sizeof(Data));    // 구조체의 크기

    //  *d2->numPtr = *((*d2).numPtr)= *(numPtr)


    //값 저장
    d2->c = 'y';                 //구조체 포인터 역참조하여 멤버 c에 접근해 b 값 넣기

   printf("(*d2).c : %c\n", (*d2).c);      //  b: 구조체 포인터를 역참조하여 c에 접근
                                   // d2->c과 같음

   printf("%d\n", *(*d2).numPtr); // 10: 구조체 포인터를 역참조하여 numPtr에 접근한 뒤 다시 역참조
                                   // *d2->numPtr과 같음

    free(d2);

    return 0;
}

 

이 단원에서 가장 중요한 것은

-> 는 포인터의 자물쇠를 *를 붙혀서 풀어, 정수인 경우 주소값을, 문자열인경우 값을 반환 한다는 것이다.

d2->numPtr = &num    이것은 (*d2).numPtr = &num와 같다.

 

포인터*는 나중에 참조가 되는 값으로 먼저 참조하려면 반드시 소괄호를 붙혀줘야 한다.

 

문자열과 정수가 다른 이유는 문자열은 값자체가 1byte로 이루어져 있지만 정수는 int형인 경우 4바이트에 21억4700만까지이기 때문에 반드시 주소값으로 처리를 해줘야한다.

 

자바나 파이썬은 이런 것을 고려할 필요가 없지만 c언어는 고려해야한다.

 

같은 원리로 정수형의 경우 =라는 shallow copy가 아닌 deep copy로 해야한다.

 

memcpy와 strcpy는 malloc()으로 메모리를 할당하고 =으로 얕은 복사를 하는 두가지 기능을 다 담고 있다.

 

 

 

 

 

 

 

 

 

 

+ Recent posts