[C]_23.03.28

‍전희주·2023년 3월 28일
0

다우_C 입문 강의

목록 보기
7/7

보충 설명

  • free는 잘 했는데 왜 주소값과 저장된 문자열이 그대로일까?
    • free함수는 ptr에 저장된 값을 바꾸지 못하고 ptr은 여전히 같은 메모리 공간을 가리키고 있다고 한다.
    • 이것을 Dangling pointer라한다.
      즉 객체에 대한 참조가 포인터 값에 대한 수정 없이 삭제되거나 할당 해제돼서 포인터가 계속 할당 해제된 메모리를 가리킬 때이다.
    • free()는 해당 포인터가 가리키는 메모리공간을 할당 가능하게 해줄 뿐 전달된 포인터 변수에는 아무짓도 하지 않는다.

포인터 활용

double_pointerEX.c

//double_pointerEX.c
#include <stdio.h>

funcA(char* ptr[]);

int main()
{
	char* ptr[] = { "kingdom test", "Advance C programming", "C++ programming",
					"one two three", "multi campus",
					"seoul 서울시 강남구 양재동 100번지",
					"busan 부산시 해운대구 해운대동 바다 2 번지", NULL };

	funcA(ptr);

	return 0;
}

// ptr의 길이를 출력
funcA(char** ptr)		// char *ptr[]  / 포인터배열을 실인수로 전달한다면 가인수는 이중포인터로 받아야 함
{
	// 방법1
	int i;

	while (*ptr)		// NULL 주소가 아닐 때까지
	{
		printf("%u : ", ptr);
		for (i = 0; *(*ptr + i); i++)
		{
			printf("%c", *(*ptr + i));
		}
		printf(" / length : %d \n", i);
		ptr++;
	}


	// 방법 2
	int len = 0;
	char* tmp;

	while (*ptr)
	{
		tmp = *ptr;

		while (*tmp)		// char 1바이트씩 읽어들임
		{
			len++;
			tmp++;
		}

		printf("%u, %u : %s, Length : %d \n", ptr, *ptr, *ptr, len);
		ptr++;
	}


	printf("End.");
}

enum_pointerEx.c

//enum_pointerEx.c
#include <stdio.h>
#include <string.h>

typedef enum Season
{
    SPRING,
    SUMMER,
    FALL,
    WINTER,
} SEASON;

char* GetSeason(int season)
{
    switch (season)
    {
    case 0:
        return("봄");        // 데이터 세그먼트의 read-only data 영역에 들어감
    case 1:
        return("여름");
    case 2:
        return("가을");
    case 3:
        return("겨울");
    default:
        break;
    }

    // 방법2
    static char ret[100];

    switch (season)
    {
    case SPRING:
        strcpy(ret, "봄");
        break;
    case SUMMER:
        strcpy(ret, "여름");
        break;
    case FALL:
        strcpy(ret, "가을");
        break;
    case WINTER:
        strcpy(ret, "겨울");
        break;
    default:
        break;
    }
    return ret;
}

int main()
{
    char* ptr;
    enum  Season season = SPRING;
    ptr = GetSeason(season);
    printf("선택한 계절: %s \n", ptr);
    return 0;
}

pointer5.c

// pointer5.c

#include <stdio.h>

int main()
{
	int num[5] = { 100,200,300,400,500 }, i; 
	int* ptr[5]; // 포인터 배열 => 배열 요소 개수만큼 메모리 할당 수행 20byte
	             //int(*ptr2)[5]; // 배열 포인터 변수 4 byte
	char word[3][20] = { "kingdom", "prince", "princess" }; 
	char* ptr2[3]; 

	printf("ptr sizeof: %d \n", sizeof(ptr)); 

	for (i = 0; i < 5; i++)
		ptr[i] = num + i; 
	//ptr[i] = &num[i]; 


	for (i = 0; i < 5; i++)
		printf("%d ,", *ptr[i]);
	printf("\n"); 

	//문자열 출력 
	for (i = 0; i < 3; i++)
	ptr2[i] = *(word + i); 
	//ptr2[i] = *(word + i);  // *(*ptr2 + i) = *ptr2[i]      //ptr[i][4] = *(*(ptr + i) + 4) 

	for (i = 0; i < 3; i++)
			printf("%s, %c\n", ptr2[i], *ptr2[i]); // i=0 => *ptr2[0] : 'k' 지칭(1byte) // %s => 받은 문자열부터 \0까지 출력 

	printf("\n"); 

	return 0; 

}

다중 포인터의 동적 메모리 할당

  • 동적 메모리 할당은 2차원 배열, 2차원 포인터 등과 같이 다중포인터를 사용하는 것이 가능합니다.
  • ref) https://studyc.tistory.com/18

double_pointer2.c

//double_pointer2.c
#include <stdio.h>
#include <string.h>
int main()
{
	char** ptr, tmp[100];
	int x, y, i;
	printf("\n문자열의 수 ? ");
	scanf("%d%*c", &x); //3

	ptr = (char**)malloc(x * sizeof(char*));		// sizeof(char*) => 4byte
	if (ptr == NULL)		// heap을 할당할 때마다 필요 -> 언제 고갈될 지 모르기 때문
	{
		perror("Error : ");
		exit(1);
	}

	for (i = 0; i < x; i++)
	{
		printf("%d,input string ? ", i + 1);
		gets(tmp);
		*(ptr + i) = (char*)malloc(strlen(tmp) + 1);
		strcpy(*(ptr + i), tmp);
	}

	printf("\n문자열 출력\n");
	for (i = 0; i < x; i++)
		printf("%p, %s \n", *(ptr + i), *(ptr + i));

	printf("\n추가할 문자열의 수 ? \n");
	scanf("%d%*c", &y);		// 2

	ptr = (char**)realloc(ptr, sizeof(char*) * (x + y));

	if (ptr == NULL)		// heap을 할당할 때마다 필요 -> 언제 고갈될 지 모르기 때문
	{
		perror("Error : ");
		exit(1);
	}

	for (i = x; i < (x+y); i++)
	{
		printf("%d,input string ? ", i + 1);
		gets(tmp);
		*(ptr + i) = (char*)malloc(strlen(tmp) + 1);
		strcpy(*(ptr + i), tmp);
	}

	printf("\n문자열 출력\n");
	for (i = 0; i < (x+y); i++)
		printf("%p, %s \n", *(ptr + i), *(ptr + i));
	printf("\n"); 

	for (i = 0; i < (x + y); i++)
		free(ptr[i]);				// heap은 사용하고 나면 깨끗하게 비워야 함
	free(ptr);

	ptr = NULL; 

	return 0;
}

메모리 누수(leak) 확인

  • c => 구조적인 programm (함수 중심)
  • c++ => oop(객체 지향 프로그램)(객체 중심) , 확장성이 좋음

(참고) vld(visual leak detector)을 통한 메모리 누수 확인

  • free 함수 사용안하면 메모리 누수 발생
    🔽

단일 링크드(연결) 리스트

s1_linked.c

//s1_linked.c 

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

struct EMP {
	char name[20];
	int salary;
	float height;
	char comAddr[50];
	struct EMP* next; 

};

int main()
{
	//struct EMP empPtr;					//이렇게 선언하면 구조체 변수 100바이트를 할당받는다
	struct EMP* head, *tail;
	struct EMP* empPtr;						//이렇게 선언하면 구조체 포인터 4바이트를 할당받는다

	head = tail = NULL; // heap segment 내 값 버리기 


	while (1)
	{


		empPtr = (struct EMP*)malloc(sizeof(struct EMP)); // size 바이트의 메모리를 힙에서 할당해 반환 
		// empPtr에는 할당된 메모리의 주소를 담음 

		if ((empPtr == NULL))
		{
			perror("Error :");
			exit(1);
		}

		//데이터 입력
		printf("성명 ? (입력종료:end) ");
		gets(empPtr->name);
		if (strcmp(empPtr->name, "end") == 0)
			break;
		printf("월급 ?");
		scanf("%d", &empPtr->salary);
		printf("키(신장) ?");
		scanf("%f%*c", &empPtr->height);
		printf("회사주소 ?");
		gets(empPtr->comAddr);

		empPtr->next = NULL;  

		if (head == NULL)
			head = tail = empPtr; 
		else
		{
			tail->next = empPtr; // (*구조체_포인터).멤버"는 "구조체_포인터->멤버"와 완전히 동일한 연산문이다. // (*tail).next 
			tail = empPtr; 
		}

	} // while (1) 
	empPtr = head; 
	//데이터 출력
	while (empPtr) // 	while (empPtr != '\0') 
	{
	
		printf("%s, %d, %.2f, %s \n", empPtr->name, empPtr->salary, empPtr->height, empPtr->comAddr); 
		empPtr = empPtr->next; 
	
	}

	//메모리 해제
	free(empPtr); // empPtr이 가리키는 메모리를 해제 
	empPtr = NULL;

	return 0;
}
  • 다음 노드의 주소를 포인터 변수에 저장해두고 연속 출력 수행

  • tail의 역할: 새로운 노드가 유입되면 기존의 노드와 맵핑해줌

  • 단일 링크드 리스트 핵심 단계

struct EMP* head, * tail;

empPtr->next = NULL;

while (empPtr)          //메모리 해제  while (empPtr != '\0') 
{
	x = empPtr;
	empPtr = empPtr->next;
	free(x); // 반복문을 통해 수 백개의 노드 데이터 삭제 

}

-구조체 문자열 변수는 크기 확인 후 대입 필

s2_linked.c

// s2_linked.c

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

struct EMP {
	char name[20];
	int salary;
	float height;
	char comAddr[50];
	struct EMP* next;

};

int main()
{
	//struct EMP empPtr;					//이렇게 선언하면 구조체 변수 100바이트를 할당받는다
	struct EMP* head, * tail;
	struct EMP* empPtr, * x;						//이렇게 선언하면 구조체 포인터 4바이트를 할당받는다
	char tmp[100]; //임시 배열 활용 

	head = tail = NULL; // heap segment 내 값 버리기 


	while (1)
	{


		empPtr = (struct EMP*)malloc(sizeof(struct EMP)); // size 바이트의 메모리를 힙에서 할당해 반환 
		// empPtr에는 할당된 메모리의 주소를 담음 

		if ((empPtr == NULL))
		{
			perror("Error :");
			exit(1);
		}

		//데이터 입력 (구조체 문자열 변수는 크기 확인 후 대입 필) 
		do{
			printf("성명 ? (입력종료:end) ");
			gets(tmp);
		} while (strlen(tmp) >= sizeof(empPtr->name)); 
		
		strcpy(empPtr->name, tmp); 

		if (strcmp(empPtr->name, "end") == 0)
			break;

		printf("월급 ?");
		scanf("%d", &empPtr->salary);
		printf("키(신장) ?");
		scanf("%f%*c", &empPtr->height); 

		printf("회사주소 ?");
		gets(empPtr->comAddr);
		empPtr->next = NULL;  // 끝.(next 가 가리킬 부분이 없다.)

		if (head == NULL)
			head = tail = empPtr;
		else
		{
			tail->next = empPtr; // (*tail).next // tail 안에 next라는 자기구조체 참조 포인터로 empPtr을 가리킨다.
			tail = empPtr;
		}

	} // while (1) 
	free(empPtr); // empPtr이 가리키는 메모리를 해제 


	empPtr = head;
	//데이터 출력
	while (empPtr) // 	while (empPtr != '\0') 
	{

		printf("%s, %d, %.2f, %s \n", empPtr->name, empPtr->salary, empPtr->height, empPtr->comAddr);
		empPtr = empPtr->next;

	}


	empPtr = head;
	while (empPtr)          //메모리 해제  while (empPtr != '\0') 
	{
		x = empPtr;
		empPtr = empPtr->next;
		free(x); // 반복문을 통해 수 백개의 노드 데이터 삭제 

	}


	empPtr = NULL;

	return 0;
}


이중 링크드(연결) 리스트

d1_linked.c

//d1_linked.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct EMP {
	char name[20];
	int salary;
	float height;
	char comAddr[50];
	struct EMP* before;
	struct EMP* next;
};

int main()
{
	struct EMP* head, * tail;
	struct EMP* empPtr, * x;

	head = tail = NULL;		// heap에 아무것도 들어있지 않음

	while (1)		// 데이터 입력
	{
		empPtr = (struct EMP*)malloc(sizeof(struct EMP));  // size 바이트의 메모리를 힙에서 할당해 반환 
		// empPtr에는 할당된 메모리의 주소를 담음 

		if (empPtr == NULL)
		{
			perror("Error : ");
			exit(1);
		}

		printf("\n성명 ? (입력종료: end) ");	// kim
		gets(empPtr->name);
		if (!strcmp(empPtr->name, "end"))
			break;

		printf("월급 ? ");	// 1000
		scanf("%d", &empPtr->salary);

		printf("키(신장) ? ");		// 173
		scanf("%f%*c", &empPtr->height);

		printf("회사주소 ? ");		// seoul
		gets(empPtr->comAddr);

		empPtr->before = NULL;
		empPtr->next = NULL;

		if (head == NULL)
			head = tail = empPtr;
		else
		{
			empPtr->before = tail;
			tail->next = empPtr;
			tail = empPtr;
		}

	}	// while(1) end
	free(empPtr);		// 7000번지를 없애겠다

	printf("============ head -> tail =============\n");
	empPtr = head;

	while (empPtr)		// 데이터 출력
	{
		printf("%s, %d, %.2f, %s \n", empPtr->name, empPtr->salary, empPtr->height, empPtr->comAddr);

		empPtr = empPtr->next;
	}

	printf("============ tail-> head =============\n");
	empPtr = tail;

	while (empPtr)		// 데이터 출력
	{
		printf("%s, %d, %.2f, %s \n", empPtr->name, empPtr->salary, empPtr->height, empPtr->comAddr);

		empPtr = empPtr->before;
	}

	printf("=========================\n");
	empPtr = head;

	// 반복문 통해 수많은 노드 삭제함으로써 메모리 해제
	while (empPtr)
	{
		x = empPtr;
		empPtr = empPtr->next;
		free(x);		// empPtr이 가리키는 메모리를 해제 
	}

	empPtr = NULL;

	return 0;
}

특정 멤버 동적 할당

s2_linked.c

//s2_linked.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct EMP {
	char name[20];
	int salary;
	float height;
	char *comAddr;		// 동적할당 멤버
	struct EMP* next;
};

int main()
{
	struct EMP* head, * tail;
	struct EMP* empPtr, * x;
	char tmp[250];

	head = tail = NULL;		// heap에 아무것도 들어있지 않음

	while (1)		// 데이터 입력
	{
		empPtr = (struct EMP*)malloc(sizeof(struct EMP));  // size 바이트의 메모리를 힙에서 할당해 반환 
		// empPtr에는 할당된 메모리의 주소를 담음 

		if (empPtr == NULL)
		{
			perror("Error : ");
			exit(1);
		}

		do {
			printf("\n성명 ? (입력종료: end) ");	// kim
			gets(tmp);

		} while (strlen(tmp) >= sizeof(empPtr->name));		//name 할당량보다 길이가 길 경우

		strcpy(empPtr->name, tmp);		// 문자열 직접 받으면 안 되고 함수를 불러 처리 -> 구조체의 문자열 멤버는 확인하고 넣어야 함

		if (!strcmp(empPtr->name, "end"))
			break;

		printf("월급 ? ");	// 1000
		scanf("%d", &empPtr->salary);

		printf("키(신장) ? ");		// 173
		scanf("%f%*c", &empPtr->height);

		printf("회사주소 ? ");		// seoul
		gets(tmp);
		empPtr->comAddr = (char*)malloc(strlen(tmp) + 1);		// * 붙어있는 경우 주소를 따로 동적할당
		strcpy(empPtr->comAddr, tmp);

		empPtr->next = NULL;

		if (head == NULL)
			head = tail = empPtr;
		else
		{
			tail->next = empPtr;
			tail = empPtr;
		}

	}	// while(1) end
	free(empPtr);		// 7000번지를 없애겠다

	empPtr = head;		// 3000 번지, 4000 번지 두 개의 값 O

	while (empPtr)		// 데이터 출력
	{
		printf("%s, %d, %.2f, %s \n", empPtr->name, empPtr->salary, empPtr->height, empPtr->comAddr);

		empPtr = empPtr->next;
	}

	empPtr = head;
 
	// 반복문 통해 수많은 노드 삭제함으로써 메모리 해제
	while (empPtr)		// 특정 멤버가 동적할당 되면 그거 먼저 지우고 부모를 지워야 함
	{
		x = empPtr;
		empPtr = empPtr->next;
		free(x->comAddr);
		free(x);		// empPtr이 가리키는 메모리를 해제 
	}

	empPtr = NULL;

	return 0;
} 
profile
heejoojeon@daou.co.kr

0개의 댓글

관련 채용 정보