[C 언어] 19_2. 파일 입출력의 이해 2

프로그래밍/C언어|2022. 4. 11. 15:46

2022.04.11 - [프로그래밍/C언어] - [C 언어] 19_1. 파일 입출력의 이해 1

 

[C 언어] 19_1. 파일 입출력의 이해 1

2022.04.11 - [프로그래밍/C언어] - [C 언어] 18. 문자열 입력함수의 종류와 이해 [C 언어] 18. 문자열 입력함수의 종류와 이해 2022.04.11 - [프로그래밍/C언어] - [C 언어] 17. 입력함수(scanf, getchar) 이해 [..

dlwlxo3819.tistory.com

====================== < 전체 소스코드 > ======================

 

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

// ========================================= 함수 선언부 ====================================================================

// 예제1 함수선언 

// ========================================= main 함수 ====================================================================

int main(void)
{
	// 19.2. 다양한 파일 입출력
	printf("\n------------------- < 1) fgets와 fputs로 한줄씩 입출력 > ---------------------------------------------\n\n");

	// 파일에 문자열을 입렫받아 저장하는 함수 fputs는 입력 받을때 매번 줄넘김을 해서 파일에 저장을 합니다.
	FILE* ifp_1, *ofp_1;				// 파일포인터 선언
	char str_1[80];						// 입력한 문자열 저장할 배열
	char* res_1;						// fgets 함수의 반환값을 저장할 변수

	ifp_1 = fopen("Temp\\19_2_1_a.txt", "r");			// 입력파일을 읽기전용으로 개방

	if (ifp_1 == NULL)							// 입력파일 열지 못한 경우 처리
	{
		printf("입력 파일을 열지 못했습니다.\n");
		return 1;
	}

	ofp_1 = fopen("Temp\\19_2_1_b.txt", "w");			// 출력파일 쓰기전용으로 개방

	if (ofp_1 == NULL)							// 출력파일 열지 못한 경우 처리
	{
		printf("출력 파일을 열지 못했습니다.\n");
		return 1;
	}

	while (1)
	{
		res_1 = fgets(str_1, sizeof(str_1), ifp_1);		// 입력 파일로부터 한줄을 읽어와서 str_1에 저장

		if (res_1 == NULL)								// 읽어올 값이 없으면(NULL) 반복문 탈출
		{
			break;
		}

		str_1[strlen(str_1) - 1] = '\0';				// 개행문자 제거(1줄씩 읽어오니까)
		fputs(str_1, ofp_1);							// 처리 마친 str_1의 내용을 출력파일에 쓰기
		fputs(" ", ofp_1);								// 출력파일에 str_1의 내용 모두 쓴뒤 공백(띄어쓰기) 추가 
	}

	fclose(ifp_1);										// 입력파일 닫기
	fclose(ofp_1);										// 출력파일 닫기

	printf("\n------------------- < 2) fscnaf와 fprintf 로 다양한 형태의 입출력 수행  > ---------------------------------------------\n\n");

	FILE* ifp_2, * ofp_2;
	char name_2[20];
	int kor_2, eng_2, math_2;
	int total_2;
	double avg_2;
	int res_2;

	ifp_2 = fopen("Temp\\19_2_2_a.txt", "r");			// 입력파일 읽기전용으로 개방
	if (ifp_2 == NULL)							// 입력파일 열지 못한 경우 처리
	{
		printf("입력 파일을 열지 못했습니다.\n");
		return 1;
	}

	ofp_2 = fopen("Temp\\19_2_2_b.txt", "w");			// 출력파일 쓰기전용으로 개방

	if (ofp_2 == NULL)							// 출력파일 열지 못한 경우 처리
	{
		printf("출력 파일을 열지 못했습니다.\n");
		return 1;
	}

	while (1)
	{
		res_2 = fscanf(ifp_2, "%s%d%d%d", name_2, &kor_2, &eng_2, &math_2);			// 입력파일로 부터 값을 받아와서 저장
		if (res_2 == EOF)															// 읽어올값이 없으면 종료
		{
			break;
		}
		total_2 = kor_2 + eng_2 + math_2;							// 총점계산
		avg_2 = total_2 / 3.0;										// 평균계산
		fprintf(ofp_2, "%s%5d%7.1lf\n", name_2, total_2, avg_2);	// ofp_2파일에 지정한 형식에 맞게 출력
	}

	fclose(ifp_2);						// 입력파일 닫기
	fclose(ofp_2);						// 출력파일 닫기

	// * 참고
	// fscanf는 모든 값을 읽어 왔으면 EOF를 반환한다.

	printf("\n------------------- < 3) 스트림파일의 버퍼공유문제와 fflush 함수 > ---------------------------------------------\n\n");

	printf(" < 문제 해결전 > \n\n");
	FILE* fp_3_1;
	int age_3_1;
	char name_3_1[20];

	fp_3_1 = fopen("Temp\\19_2_3_1_a.txt", "r");

	fscanf(fp_3_1, "%d", &age_3_1);
	fgets(name_3_1, sizeof(name_3_1), fp_3_1);

	printf(" 나이: %d ,이름 : %s", age_3_1, name_3_1);
	fclose(fp_3_1);


	// 이렇게 하면 이름이 들어가지 않는다!
	// 이유는 fscanf가 입력버퍼에 남겨놓은 개행문자를 fgets가 보고 개행문자를 가져가서 name_3에 넣어버린다.
	// 이를 해결하고자 fscaf를 이용하면 "Hong GD" 중에서 공백앞의 "Hong"만 가져가서 name_3 에는 Hong만 저장된다.

	// 이를 해결하는 방법은 fgetc함수와 fflush를 통해서 해결할 수 있다.

	printf("\n < 문제 해결후 >\n\n");
	FILE* fp_3_2;
	int age_3_2;
	char name_3_2[20];

	fp_3_2 = fopen("Temp\\19_2_3_2_a.txt", "r");

	fscanf(fp_3_2, "%d", &age_3_2);
	while (fgetc(fp_3_2) != '\n') {}						// 입력파일의 내용을 한글자씩 들고와서 개행문자 아니면 버리고 개행문자면 반복종료
	fgets(name_3_2, sizeof(name_3_2), fp_3_2);

	printf(" 나이: %d ,이름 : %s", age_3_2, name_3_2);
	fclose(fp_3_2);


	printf("\n------------------- < 4) fread함수와 fwrite함수 > ---------------------------------------------\n\n");

	// fread와 fwrite는 바이너리모드가 기본이라 txt/메모장 으로 확인이 불가능하다.(편집도 불가능)
	FILE* afp_4, * bfp_4;
	int num_4 = 10;
	int res_4;

	afp_4 = fopen("Temp\\19_2_4_1_a.txt", "wt");		// 텍스트모드로 출력파일개방
	fprintf(afp_4, "%d", num_4);				// num_4의 값을 문자로 변환하여 출력

	bfp_4 = fopen("Temp\\19_2_4_1_b.txt", "wb");		// 바이너리 모드로 출력파일 개방
	fwrite(&num_4, sizeof(num_4), 1, bfp_4);	// num_4의 값을 그대로 파일에 출력

	fclose(afp_4);
	fclose(bfp_4);

	bfp_4 = fopen("Temp\\19_2_4_1_b.txt", "rb");			// 바이너리 모드로 입력파일 개방
	fread(&res_4, sizeof(res_4), 1, bfp_4);			// 파일의 데이터를 그대로 변수에 입력
	printf("%d\n", res_4);							// 입력한 데이터 확인

	fclose(bfp_4);

	//	fprintf는 메모리에 저장되어있는 이진수를 아스키코드로 전화하여 txt파일(19_2_4_1_b.txt)에 저장한다
	//	fwrite는 이러한 변환과정 없이 메모리에 있는 데이터(2진수)를 그대로 가져와서 파일에 저장한다.
	//	따라서 이후의 19_2_4_1_b.txt에 저장된 내용은 아스키코드값으로 저장되지 않은 바이너리 파일이 되고 일반 텍스트파일편집기로 읽을수 없다
	//	이를 읽으려면 fread를 통해 읽어야한다.
	//	이러한 형식의 장접은 변환과정이 없어서 매우 빠르게 지행할수 있닫는 것으로 자연스럽게 대용량의 데이터를 파일에 입출력할때 유용하다.
	//	데이터는 바이너리 모드로 항상 개방하는것이 좋다(변환과정에서 크기간의 차이/손실 등이 날수 있다)

	return 0;
}

// ========================================= 함수 정의부 ====================================================================

//	3) getchar 함수를 이용한 문자열 입력 함수정의

댓글()

[C 언어] 19_1. 파일 입출력의 이해 1

프로그래밍/C언어|2022. 4. 11. 15:44

2022.04.11 - [프로그래밍/C언어] - [C 언어] 18. 문자열 입력함수의 종류와 이해

 

[C 언어] 18. 문자열 입력함수의 종류와 이해

2022.04.11 - [프로그래밍/C언어] - [C 언어] 17. 입력함수(scanf, getchar) 이해 [C 언어] 17. 입력함수(scanf, getchar) 이해 2022.04.11 - [프로그래밍/C언어] - [C 언어] 16. 메모리동적할당 [C 언어] 16. 메..

dlwlxo3819.tistory.com

 

====================== < 전체 소스코드 > ======================

 

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


// ========================================= 함수 선언부 ====================================================================

// 예제1 함수선언 

// ========================================= main 함수 ====================================================================

int main(void)
{
	//	19. 파일입출력
	//	파일의 생성 및 수정 그리고 삭제를 다룰수 있다.
	//	* 참고 - 현재디렉토리경로 : D:\\1_IoT\\IoT_start\\IoT_start


	printf("\n------------------- < 1) 파일열고 닫기(fopen함수, fclose 함수) > ---------------------------------------------\n\n");

	//	fopen : 파일을 연다.
	//	fclose : 파일을 닫는다.
	
	FILE* fp_1;

	fp_1 = fopen("Temp\\19_1_a.txt", "r");						// 파일 열기
	if (fp_1 == NULL)								// 해당이름의 파일이 존재하지 않은 경우의 처리
	{
		printf("파일이 열리지 않았습니다.\n");
		return 1;
	}

	printf("파일이 열렸습니다.\n");
	fclose(fp_1);									// 파일 닫기

	//	* 참고1 
	//	fopen의 꼴 : fopen("파일명.확장자","명령어");
	//	여기서 파일의 경로는 '현재 작업디렉토리'로 자동으로 기입되어 생략된것이다. 만약 다른 폴더/위치의 파일의 개방을 원한다면 
	//	절대경로 : "파일경로\\파일명.확장자"
	//	상대경로	: "현재디렉토리로부터의 파일경로\\파일명.확장자"

	//	** 참고2
	//	명령어는 r,w,a의 세가지가 있다.
	//	r = 읽기위해 개방. 파일이 없으면 NULL반환
	//	w = 파일내용을 지우고 쓰기위해 개방. 해당 파일이 없으면 파일을 생성
	//	a = 파일의 끝에 추가하기위해 개방. 해당 파일이 없으면 파일을 생성

	//	!!! 주의사항 !!!

	//	1) 경로설정할때 경로의 모든 역슬래시를 한번씩 더 추가해줘야한다.
	//	ex ) c:\source\19_1_a.txt	->	c:\\source\\19_1_a.txt  

	//	2) 상위경로/폴더로 가야한다면 .. 을 사용
	//	ex) c---ㅁ(목표위치(B))
	//		|----ㅁ(현재위치(A))
	//		이러한 상황에서 B의 19_1_a.txt를 열려면 먼저 c로 간다음 B를 열고 19_1_a.txt를 열어야한다.

	//		fopen("c:\\B\\19_1_a.txt","r");		-	절대경로
	//		또는	
	//		fopen("..\\B\\19_1_a.txt","r");		-	상대경로

	printf("\n------------------- < 2) 파일 입출력(fgetc함수) > ---------------------------------------------\n\n");

	//	fgetc함수 : 파일에 적힌 내용을 읽어서 cmd에 출력
	//				파일내의 내용을 모두 읽었으면 (더이상 읽을 내용이 없으면) EOF를 반환

	FILE* fp_2;
	int ch_2;

	// fp_2 = fopen("Temp\\19_1_a.txt", "r");									// 상대경로
	fp_2 = fopen("D:\\1_IoT\\IoT_start\\IoT_start\\Temp\\19_1_a.txt", "r");	// 절대경로
	if (fp_2 == NULL)
	{
		printf("파일이 열리지 않았습니다.\n");
		return 1;
	}

	while (1)
	{
		ch_2 = fgetc(fp_2);							// 개방한 파일에서 하나의 문자 입력받아온다.
		if (ch_2 == EOF)							// 함수반환값이 EOF(= -1)이면 종료	(fgetc은 모든 데이터를 읽어왔다면 EOF를 반환)
		{
			break;
		}
		putchar(ch_2);								// 입력한 문자 화면출력
	}
	fclose(fp_2);									// 파일 닫음

	printf("\n------------------- < 3) 입력받은 문자열을 파일로 출력(fputs함수) > ---------------------------------------------\n\n");

	//	fputs는 문자열을 파일로 출력할때 사용하는 함수
	//	단 문자열을 버퍼에 모두 저장하고 난 다음 파일로 출력한다.

	FILE* fp_3;
	char str_3[] = "banana";
	int ch_3;

	fp_3 = fopen("Temp\\19_1_b.txt", "w");
	if (fp_3 == NULL)
	{
		printf("파일을 출력하지 못했습니다.\n");
		return 1;
	}


	
	int i = 0;
	while (str_3[i] != 0)					//	널문자가 아니면
	{
		fputc(str_3[i], fp_3);				//	문자를 파일에 출력
		i++;
	}
	fputc('\n', fp_3);						//	파일의 끝에 줄넘김을 추가


	fclose(fp_3);

	// 문자가 잘 입력됬는지 확인하기 위한 파일내용 출력

	printf("문자가 파일에 입력됬는지 확인하기 위한 파일내용 출력\n");
	fp_3 = fopen("D:\\1_IoT\\IoT_start\\IoT_start\\Temp\\19_1_b.txt", "r");	// 절대경로

	while (1)								
	{
		ch_3 = fgetc(fp_3);							// 개방한 파일에서 하나의 문자 입력받아온다.
		if (ch_3 == EOF)							// 함수반환값이 EOF(= -1)이면 종료	(fgetc은 모든 데이터를 읽어왔다면 EOF를 반환)
		{
			break;
		}
		putchar(ch_3);								// 입력한 문자 화면출력
	}

	fclose(fp_3);

	printf("\n------------------- < 4) 표준입출력을 통한 문자열 입력 > ---------------------------------------------\n\n");
	//	표준 입출력 함수들은 자동으로 운영체제가 stdin,stdout,stderr등의 파일스트림을 구성해서 보조한다.
	
	//	(stdin	= 표준입력 스트림 = 키보드와 연결됨) - 키보드로부터 받아옴			(입력)
	//	(stdout = 표준출력 스트림 = 모니터와 연결됨) - 모니터로 내보냄				(출력)
	//	(stderr = 표준에러 스트림 = 모니터와 연결됨) - 모니터로 내보냄(오류코드 등)	(출력)

	//	* 스트림은 기본적으로 포인터임
	//	단, fgetc나 fputs와 같이 기본으로 스트림이 지정되어 있지 않거나 따로 지정해서 사용가능한 함수가 있다는 것을 기억할 것 !
	
	int ch_4;
	printf("입력받은 문자열 저장없이 버퍼에서 다시출력 (종료: ctrl + z)");
	while (1)
	{
		ch_4 = fgetc(stdin);			// 키보드로 문자입력
		if (ch_4 == EOF)				// ctrl + z로 종료
		{
			break;
		}
		fputc(ch_4, stdout);			// 화면에 출력
	}

	//	연결된 키보드로 입력을 받으면 stdin의 버퍼로 입력되고(fgetc(stdin) 그 내용이 다시 ch_4에 입력된다(ch_4 = fgets(stdin))
	//	stdout으로 인해 ch_4의 내용이 stdout의 버퍼로 입력되고 stdout에 연결된 모니터로 정보를 넘긴다. (fputc(ch_4,stdout)) 

	printf("\n------------------- < 5) 개방할 파일의 형식(텍스트(t) 바이너리(b)) > ---------------------------------------------\n\n");

	// 파일을 개방할때 실제로는 r,w,a가 총 6사지의 종류가 있다
	//	텍스트파일 개방		 = rt, wt, at
	//	텍스트이외의 파일 개방 = rb, wb, ab
	//	단, default값으로 앞에 t가 붙어있어서 이전 실습에서의 파일개방에 문제가 없었던 것!

	FILE* fp_5;
	int ary_5[10] = { 13, 10, 13, 13, 10, 26, 13, 10, 13, 10 };
	int res_5;

	fp_5 = fopen("Temp\\19_1_5_a.txt", "wb");
	for (int i = 0; i < 10; i++)
	{
		fputc(ary_5[i], fp_5);
	}
	fclose(fp_5);

	//	텍스트파일에 저장될때 문자로 입력된다 즉, 아스키코드값으로 저장
	//	그런데 아스키코드상으로 13 = '\r', 10 = '\n', 26 = 'ctrl + z', 으므로 
	//	메모장에는 [ \r \n \r \r \n ctrl+z \r \n \r \n ]이 입력이 된다.
	//	(만들어진 텍스트파일을 열어보면 \r와 \n를 명령어로서 수행함을 알수 있다.) 

	fp_5 = fopen("Temp\\19_1_5_a.txt", "rt");
	while (1)
	{
		res_5 = fgetc(fp_5);
		if (res_5 == EOF) break;
		printf("%4d",res_5);
	}
	fclose(fp_5);

	//	!!! 주의 !!!
	//	입력된 파일을 fgetc로 불러오면 3개의 문자만 입력하고 종료됨을 알수 있다.
	//	fgetc는 읽어올때 /r과 /n이 순서대로 붙어서 읽어지면 /r을 버린다! (윈도우 운영체제의 특징)
	//	또한 아스키코드 26번이 'ztrl + z'를 의미하므로 이를 읽으면서 EOF를 반환하고 종료되는 것!

	//	즉, (13 10)		 13		(13 10)		 26   ~
	//		 13버림				 13버림		종료
	//													출력화면	= 10 13 10 (종료)

	printf("\n------------------- < 6) '+'개방모드와 fseek, rewind, feof 함수 > ---------------------------------------------\n\n");

	// '+' 개방모드
	// 기존의 모드뒤에 +를 붙여 기존에서 읽고쓰기능력을 추가하는것!
	
	// r+ = 텍스트파일에 읽고 쓰기위해 개방
	// w+ = 텍스트파일의 내용을 지우거나 읽고 쓰기위해 개방
	// b+ = 텍스트파일을 읽거나 파일내용의 끝에 추가하기위해 개방
	
	// rb+ = 바이너리파일에 읽고 쓰기위해 개방
	// wb+ = 바이너리파일의 내용을 지우거나 읽고 쓰기위해 개방
	// ab+ = 바이너리파일을 읽거나 파일내용의 끝에 추가하기위해 개방


	FILE* fp_6;
	char str_6[20];

	fp_6 = fopen("Temp\\19_1_6_a.txt", "a+");				// 읽기 가능한 추가보드로 개방
	if (fp_6 == NULL)
	{
		printf("파일을 만들지 못했습니다.\n");
		return 1;
	}

	while (1)
	{
		printf("과일이름 : ");
		scanf("%s", str_6);							// 키보드로 과일이름입력

		if (strcmp(str_6, "end") == 0)				// end를 입력받으면 종료
		{
			break;
		}

		else if (strcmp(str_6, "list") == 0)		// list를 입력받으면 파일내용 출력
		{
			fseek(fp_6, 0, SEEK_SET);				// fseek함수를 이용해 위치지시자를 문서의 맨앞으로 옮김 (*참고1 항목참고)
			while (1)
			{
				fgets(str_6, sizeof(str_6), fp_6);
				if (feof(fp_6))						// 파일을 끝까지 다읽었는지 체크
				{
					break;
				}
				printf("%s", str_6);				// 파일내용 출력(a+ - 읽기)
			}
			
		}
		else
		{
			fprintf(fp_6, "%s\n", str_6);			// list,end가 아니면 str_6에 입력받은 값을 fp_6의 끝에 추가(a+ -  추가)
		}

	}
	fclose(fp_6);

	//	* 참고1
	//	fseek함수 꼴: fseek(파일 포인터,오프셋값, 기준점)
	//	fseek함수는 현재 어디에 입력되고 있는지를 가리키는 '위치지시자'의 위치를 변화시키는 함수이다.
	//	포인터가 가리키는 파일내부의 위치지시자를 기준점으로 옮기고 오프셋값만큼 커서를 움직인다.
	//	기준점은 파일의 처음(SEEK_SET), 현 위치지시자 위치(SEEK_CUR), 파일의 끝(SEEK_END) 3가지이다.
		
	//	** 사용하는 이유
	//	입력을 진행중일때 한줄을 입력하면 자동으로 위치지시자는 다음의 빈줄을 가리키게 되는데
	//	이를 fseek을 이용해 문서의 맨앞으로 돌리고 한줄씩 출력하여 순서대로 전체 내용을 출력할수 있다.

	//	*** 참고2
	//	간단하게 rewind를 써서 문서의 처음을 가리키게 할수도 있다.
	//	fseek(fp_6,0,SEEK_SET) == rewind(fp_6) 이다.

	//	**** 참고3
	//	feof(fp_6)는 fp_6파일의 데이터를 모두 읽었을때(EOF상태) 참값을 반환한다.



	return 0;
}


	
// ========================================= 함수 정의부 ====================================================================

//	3) getchar 함수를 이용한 문자열 입력 함수정의

댓글()

[C 언어] 18. 문자열 입력함수의 종류와 이해

프로그래밍/C언어|2022. 4. 11. 15:40

2022.04.11 - [프로그래밍/C언어] - [C 언어] 17. 입력함수(scanf, getchar) 이해

 

[C 언어] 17. 입력함수(scanf, getchar) 이해

2022.04.11 - [프로그래밍/C언어] - [C 언어] 16. 메모리동적할당 [C 언어] 16. 메모리동적할당 2022.04.11 - [프로그래밍/C언어] - [C 언어] 15. 함수포인터 [C 언어] 15. 함수포인터 2022.04.11 - [프로그래밍/C..

dlwlxo3819.tistory.com

 

====================== < 전체 소스코드 > ======================

 

#include <stdio.h>



// ========================================= main 함수 ====================================================================


int main(void)
{
	// 18. 문자열에 대한 이해


	printf("\n------------------- < 1) 문자열 상수는 주소다! > ---------------------------------------------\n\n");

	printf("apple이 저장된 시작 주소 값 : \t%p\n", "apple");						// 주소값 출력
	printf("두 번째 문자의 주소 값 : \t%p\n", "apple" + 1);						// 주소값 출력 (위와 딱 1 차이난다)

	printf("첫번째 문자 : \t\t\t\t%c\n", *"apple");								// 간접 참조 연산
	printf("두번째 문자 : \t\t\t\t%c\n", *("apple" + 1));						// 포인터 연산식
	printf("배열로 표현한 세 번째 문자 : \t%c\n", "apple"[2]);					// 배열 표현식
	
	// !!! 주소로 접근해서 문자열을 바꾸면 안됨 !!!
	// Ex) *"apple" = 't'
	// 와 같은 주소의 첫 문자를 't'로 바꾸는 시도는 실행은 되지만 운영체제가 강제종료할수도, 버전에 따라 실행되지만 시스템의 손상/오류를 일으킬수 있다
	// 호환성/안전성 등을 모두 헤치는 행위!

	printf("\n------------------- < 2) char 포인터로 문자열 사용 > ---------------------------------------------\n\n");

	char* dessert_2 = "apple";

	printf("오늘 후식은 %s 입니다.\n", dessert_2);
	dessert_2 = "banana";
	printf("내일 후식은 %s 입니다.\n", dessert_2);

	// 포인터 dessert에 저장된것은 문자열이 아니다.
	// 문자열 "apple" 과  "banana"는 상수로 메모리상수공간속 어딘가에 지정되었고
	// dessert에는 그 공간(배열)의 첫 시작좌표가 저장되어 있는 것!

	printf("\n------------------- < 3) scanf와 문자열 사용 및 심화 > ---------------------------------------------\n\n");

	char str_3[80];

	printf("문자열 입력 : ");
	scanf("%s", str_3);
	printf("첫 번째 단어 : %s \n", str_3);
	scanf("%s", str_3);
	printf("버퍼에 남아 있는 두 번째 단어 : %s\n", str_3);


	//	실행후 "apple jam"을 입력하고 ENTER를 누르면 버퍼에는 'a' 'p' 'p' 'l' 'e' '(공백)' 'j' 'a' 'm' 'ENTER' 가 저장된다.
	//	scanf에 의해 공백(스페이스)전까지의 내용인 "apple"이 str_3에 저장되고 뒤에 널문자를 추가 저장한다. (첫 번째 단어)
	//	그런데 다음 scanf는 버퍼에 내용이 있기때문에 키보드입력을 생략하고 공백,탭,개행문자를 제외한 버퍼내용을 그대로 str_3에 넘겨줍니다.(두번째 단어)
	//	그 결과 str_3은 "apple000...000"에서 덮어씌워져 "jam0e000...000" 이 저장되어 있고 e가 중간에 끼어있지만 printf 등의 함수사용 출력에서 분자열 끝의 판단은
	//	처음 만나는 널문자이므로 배열/주소 등을 이용해 억지로 출력하지 않는 한 jam까지 출력된다.


	printf("\n------------------- < 4) gets함수의 이해 > ---------------------------------------------\n\n");

	//	gets함수는 개행문자 입력전까지의 모든 것을 입력받아 넘기는 함수로 마지막에 입력되는 개행문자까지 모두 들고와서 변수에 저장
	//	대신, 마지막에 가지고온 개행문자를 널문자로 변환!
	//	즉, 버퍼에 개행문자가 남지 않는다는 장점!

	fgetc(stdin);				// 버퍼에서 하나의 문자를 읽어서 반환, 반환문자는 버림 ( !!! fgets가 아니라 fgetc임을 주의 !!!! )

	char str_4[80];

	printf("공백이 포함된 문자열 입력 : ");
	gets(str_4);									// gets 함수 사용 (입력받은 문자열을 저장할 공간지정)
	printf("입력한 문자열은  %s 입니다.", str_4);

	printf("\n------------------- < 5) fgets함수의 이해 > ---------------------------------------------\n\n");
	// scanf와 gets는 지정된 배열 넘어서는 범위까지 입력받은 문자등을 그대로 메모리에 저장하는 메모리 침범의 우려가 있다!!!
	// 이를 방지하기 위해 미리 최대배열크기를 계산해서 집어넣는 fgets함수를 사용하면 안정성을 높일수 있다.
	// fgets는 또한 개행문자까지 모두 변수에 넘기기때문에 출력하면 자동으로 개행된다!

	char str_5[80];

	printf("공백이 포함된 문자열 입력 : ");
	fgets(str_5,sizeof(str_5),stdin);			// fgets(입력받은 문자열을 저장할 공간의 주소(&+변수명 or 배열명), 배열의 크기(보통 sizeof로 사용), stdin(표준입력을 의미))
	printf("입력한 문자열은  %s 입니다.", str_5);

	// * 키보드로 입력받을때는 항상 stdin을 적어야한다
	// ** fgets는 버퍼의 개행문자도 모두 지정한 변수공간에 집어넣고 그 끝에 널문자를 붙인다
	//		(scanf = 개행문자 버퍼에 그대로둠, 변수에 입력X)
	//		(gets  = 개행문자까지 버퍼에서 빼서 변수에 저장, 저장후 입력된 개행문자를 널문자로 교체)
	//		(fgets = 개행문자까지 버퍼에서 빼서 변수에 저장, 저장후 지정된 배열의 크기 + 1 위치에 널문자 삽입 (그냥 배열크기 넣으면 개행문자뒤에 널문자가 추가되는 것)


	printf("\n------------------- < 6) 버퍼 비우는 방법 > ---------------------------------------------\n\n");
	// 버퍼를 비우지 않으면 입력받아야할때 문제가 생긴다.

	getchar();					// 버퍼에서 하나의 문자를 읽어서 반환, 반환문자는 버림
	scanf("%*c");				// 버퍼에서 하나의 문자를 읽어서 버림, 변수는 필요없음 (scnaf("%*c") 는 버퍼를 비우는 약속(일종의 아스키코드종류)이다) 
	fgetc(stdin);				// 버퍼에서 하나의 문자를 읽어서 반환, 반환문자는 버림 ( !!! fgets가 아니라 fgetc임을 주의 !!!! )

	// 주의할점은 버퍼가 원래 비어있을경우 커서만 깜빡여서 ENTER를 눌러줘야 넘어간다

	// Ex)
	printf(" < 버퍼비우기 수행 안하고 값 입/출력 수행 > \n\n");
	int age_6_1;
	char name_6_1[20];

	printf("나이입력 : ");
	scanf("%d", &age_6_1);

	printf("이름 입력 : ");
	gets(name_6_1);
	printf("나이 : %d, 이름 : %s\n", age_6_1, name_6_1);

	printf("\n\n < 버퍼비우기 수행 안하고 값 입/출력 수행 > \n\n");
	int age_6_2;
	char name_6_2[20];

	printf("나이입력 : ");
	scanf("%d", &age_6_2);
	fgetc(stdin);				// 버퍼에서 하나의 문자를 읽어서 반환, 반환문자는 버림 ( !!! fgets가 아니라 fgetc임을 주의 !!!! )

	printf("이름 입력 : ");
	gets(name_6_2);
	printf("나이 : %d, 이름 : %s\n", age_6_2, name_6_2);


	 
	printf("\n------------------- < 7) puts와 fputs함수의 이해 > ---------------------------------------------\n\n");
	// puts 와 fputs 모두 문자열을 출력하는 함수이지만 puts는 출력하고난뒤 맨끝에 자동으로 개행문자를 통해 줄바꿈을 수행한다.



	char str_7[80] = "apple juice";
	char* ps_7 = "banana";

	puts(str_7);
	fputs(ps_7, stdout);		// stdout = 표준출력버퍼 (현시점에서 신경X)
	puts("milk");


	return 0;
}

댓글()

[C 언어] 17. 입력함수(scanf, getchar) 이해

프로그래밍/C언어|2022. 4. 11. 15:38

2022.04.11 - [프로그래밍/C언어] - [C 언어] 16. 메모리동적할당

 

[C 언어] 16. 메모리동적할당

2022.04.11 - [프로그래밍/C언어] - [C 언어] 15. 함수포인터 [C 언어] 15. 함수포인터 2022.04.11 - [프로그래밍/C언어] - [C 언어] 14. 응용포인터와 주소연산자 [C 언어] 14. 응용포인터와 주소연산자 2022.04...

dlwlxo3819.tistory.com

 

====================== < 전체 소스코드 > ======================

 

#include<stdio.h>

// ========================================= 함수 선언부 ====================================================================

void my_gets(char* str, int size);	//	[ 입력받은 문자열을 저장하는 함수 ]

// ========================================= main 함수 ====================================================================

int main(void)
{
	printf("\n------------------- < 1) scanf를 통한 버퍼 이해 > ---------------------------------------------\n\n");
	// 모든 키보드로 받는 값은 일단 버퍼에 저장되나음 이 저장된 값이 함수등에 의해 변수에 입력되는 형태를 띈다.

	char ch_1_1, ch_1_2;

	//for (int i = 0; i < 3; i++)				// 3회 반복
	//{
	//	scanf("%c", & ch_1_1);				// 문자입력
	//	printf("%c", ch_1_1);				// 입력된 문자
	//}
	// !!! scanf의 원리는 버퍼가 비어있는 경우 키보드의 입력을 받아 입력받은값을 버퍼에 저장하고 저장한 값을 자료형에 맞게(크기[byte] 기준) 들고와서(복사아님!) 변수의 주소에 저장

	// !!! 위와 같은경우 실행후, 입력창에 'tiger'을 입력하면 버퍼에 tiger가 입력되고 이 버퍼에서 for문에 따라 ch_1_1에 한글자씩 3번버퍼에서 뽑아내서 저장한다.
	//	즉, printf문에 의해 tig가 출력되고 버퍼에는 여전히 er이 남아있다.
	

	while (1)							// 개행문자 입력받을때까지 입력받고 출력하기를 반복
	{
		scanf("%c", &ch_1_2);
		if (ch_1_2 == '\n') break;
		printf("%c", ch_1_2);
	}

	// 맨위의 3회반복때 키보드 입력의 끝을 표시하기위해 ENTER(개행문자)를 입력했고 이게 버퍼에 남아있기 때문에 버퍼는 비어있지 않다!
	// 즉, while문내의 if문에 의해 바로 break된다.

	// 주석처리등을 통해 위의 while문 만을 단독 실행할 경우 ENTER,\n을 입력받기 전까지 공백,탭을 포함해 모든 키보드 입력이 버퍼에 저장(마지막에 입력한 ENTER도 저장됨)된 다음
	// scanf에 의해 한글자씩 옮겨져(복사아님) ch_1_2에 저장되고 출력되고 다음글자가 입력되고(덮어씌움)를 반복
	// 

	printf("\n------------------- < 2) scanf함수 반환값 활용 > ---------------------------------------------\n\n");

	//	운영체제나 시스템 버전등에 따라 scnaf의 반환값을 -1로 만드는 방법이 다른데 
	//	'윈도우 10'인 경우	:	'ctrl + z' 누르고 'ENTER'를 누른다음 다시 'ctrl + z'를 눌러야 된다
	//	'리눅스'의 경우		:	'ctrl + d' 를 루르면 된다
	//	-1을 반환하는 이유는 프로그램 종료를 위함이다.

	int res_2;
	char ch_2;

	while (1)
	{
		res_2 = scanf("%c", &ch_2);			// 문자입력 ( 마지막의 ENTER 까지 모두 입력된다 )
		if (res_2 == EOF) break;			// stdio.h에는 EOF가 -1 로 상수가 이미 입력되어있어 쓸수 있다.
		printf("%d ", ch_2);					// 입력된 문자의 아스키코드값 출력 ( 마지막에 입력된 ENTER( = 10 ) 도 전부 출력된다. )
	}

	printf("\n------------------- < 3) getchar 함수를 이용한 문자열 입력 > ---------------------------------------------\n\n");

	char str_3[7];

	my_gets(str_3, sizeof(str_3));
	printf("입력한 문자열 : %s\n", str_3);


	printf("\n------------------- < 4) 입력버퍼 지우기 > ---------------------------------------------\n\n");

	// 조건등에 의해 scanf가 입력받은 값을 다 가져가지 못할경우( 보통 enter가 맨뒤에 남아있다) 다음 scanf가 엔터를 바로 가져가서 오류발생(프로그램상의 오류)
	// 이를 대비하여 버퍼를 초기화 해야한다.

	int num_4, grade_4;

	printf("학번 입력 : ");
	scanf("%d", &num_4);
	getchar();										// !! 버퍼초기화 !!

	printf("학점입력 : ");
	grade_4 = getchar();
	printf("학번 : %d, 학점 : %c", num_4, grade_4);

	//	버퍼초기화 없이 위의 내용을 실행하면 한번입력하여 num_4에 입력한 학점이 저장된 다음 마지막에 입력한 ENTER가 버퍼에 남아있고
	//	이를 grade_4가 getchar()를 이용해 가져가 바로 끝나버린다.

	// * 다양한 버퍼 초기화 방법 *  - 자세한 내용은  18번을 볼것
	//getchar();					// 버퍼에서 하나의 문자를 읽어서 반환, 반환문자는 버림
	//scanf("%*c");				// 버퍼에서 하나의 문자를 읽어서 버림, 변수는 필요없음 (scnaf("%*c") 는 버퍼를 비우는 약속(일종의 아스키코드종류)이다) 
	//fgetc(stdin);				// 버퍼에서 하나의 문자를 읽어서 반환, 반환문자는 버림 ( !!! fgets가 아니라 fgetc임을 주의 !!!! )

	return 0;
}



// ========================================= 함수 정의부 ====================================================================

//	3) getchar 함수를 이용한 문자열 입력 함수정의
void my_gets(char* str, int size)	//	[ 입력받은 문자열을 저장하는 함수 ]
{
	int ch;
	int i = 0;

	ch = getchar();								// 첫번째 문자 입력
	while ((ch != '\n') && (i < size - 1))		// 배열의 크기만큼 입력
	{
		str[i] = ch;			// 입력한 문자를 배열에 저장
		i++;					// 첨자증가
		ch = getchar();			// 새로운 문자입력
	}
	str[i] = '\0';				// 입력된 문자열의 끝에 널문자 삽입
}

댓글()

[C 언어] 16. 메모리동적할당

프로그래밍/C언어|2022. 4. 11. 15:34

2022.04.11 - [프로그래밍/C언어] - [C 언어] 15. 함수포인터

 

[C 언어] 15. 함수포인터

2022.04.11 - [프로그래밍/C언어] - [C 언어] 14. 응용포인터와 주소연산자 [C 언어] 14. 응용포인터와 주소연산자 2022.04.11 - [프로그래밍/C언어] - [C 언어] 13. 자기참조구조체 [C 언어] 13. 자기참조구조체.

dlwlxo3819.tistory.com

====================== < 전체 소스코드 > ======================

 

#include <stdio.h>
#include <stdlib.h>		// !!! malloc, free 함수 사용을 위한 헤더 파일 포함 !!!
#include <string.h>

// ========================================= 함수 선언부 ====================================================================

//	예제 2
void print_str_ex2(char** ps_ex2);		//  [ 동적할당영역의 문자열을 출력하는 함수 ] 


// ========================================= main 함수 ====================================================================



int main(void)
{
	// 16. 메모리 동적할당

	printf("\n------------------- < 1) Malloc, Free > ---------------------------------------------\n\n");
	// 동적할당하고 끝나면 반드시 free를 통해 할당한 메모리를 반환해야 메모리 누수를 막을 수 있다.

	int* pi_1;									//	동적할당영역을 연결할 포인터 선언
	double* pd_1;

	pi_1 = (int*)malloc(sizeof(int));			//	메모리 동적할당후 포인터 연결
	if (pi_1 == NULL)							//	동적할당 실패시 NULL 반환되는것을 이용해 예외상황 처리
	{
		printf("# 메모리가 부족합니다.\n");		// 예외상황 처리부분
		exit(1);								// 프로그램 종료
	}

	pd_1 = (double*)malloc(sizeof(double));

	*pi_1 = 10;									//	포인터로 동적할당 영역사용		(값 저장)
	*pd_1 = 3.4;

	printf("정수형으로 사용 : %d\n", *pi_1);			// 동적할당영역(정수부 = pi_1) 에 저장된 값 출력	
	printf("실수형으로 사용 : %.1lf\n", *pd_1);		// 동적할당영역(실수부 = pi_1) 에 저장된 값 출력	

	// !!! 반드시 해야한다 !!!
	free(pi_1);										// 동적할당영역 반환 
	free(pd_1);

	printf("\n------------------- < 2) 동적할당영역을 배열처럼 사용하기 > ---------------------------------------------\n\n");

	int* pi_2;												// 동적 할당 영역을 연결할 포인터
	int sum_2 = 0;

	pi_2 = (int*)malloc(5 * sizeof(int));					// 저장공간 20바이트 할당

	if (pi_2 == NULL)
	{
		printf("메모리가 부족합니다!\n");						// 예외상황 처리부분
		exit(1);
	}

	printf("다섯명의 나이를 입력하세요 : ");
	for (int i = 0; i < 5; i++)
	{
		scanf("%d", &pi_2[i]);									// 배열요소에 입력
		sum_2 += pi_2[i];										// 배열 요소에 값 누적
	}

	printf("다섯 명의 평균 나이 : %.1lf\n", (sum_2 / 5.0));		// 평균나이 출력
	free(pi_2);		// 할당된 메모리 영역 반환

	printf("\n------------------- < 3) 기타 동적할당 함수 > ---------------------------------------------\n\n");

	//  1. calloc 함수	= 할당된 저장공간을 0으로 초기화함	
	//		원형 : void *calloc(unsigned int, unsigned int); 

	//	2. realloc 함수	= 할당괸 저장공간 크기를 조절	
	//		원형 : void *realloc(vodi *, unsigned int); 

	int* pi_3;					// 할당된 저장공간을 연결할 포인터
	int size_3 = 5;				// 한번에 할당할 저장공간의 크기 , int형 변수 5개씩
	int count_3 = 0;			// 현재 입력된 양수 개수

	int num_3 = 0;					// 양수 입력할 변수

	pi_3 = (int*)calloc(size_3, sizeof(int));		// 먼저 5개의 저장공간 할당
	while(1)
	{
		printf("양수만 입력하세요 : ");
		scanf("%d", &num_3);						// 데이터 입력
		if (num_3 <= 0) break;						// 양수가 아니면 ( 0 or 음수 ) 종료

		if (count_3 == size_3)						// 저장공간 모두 사용시 처리
		{
			size_3 += 5;							// 크기 늘려서 재할당
			pi_3 = (int*)realloc(pi_3, size_3 * sizeof(int));
		}
		pi_3[count_3++] = num_3;
	}

	for (int i = 0; i < count_3; i++)
	{
		printf("%5d", pi_3[i]);
	}
	free(pi_3);

	printf("\n------------------- < 동적할당 활용 예제1 > ---------------------------------------------\n\n");

	char temp_ex1[80];
	char* str_ex1[3];

	for (int i = 0; i < 3; i++)									// 3개의 문자열을 입력받는다 
	{
		printf("문자열을 입력하세요 : ");
		gets(temp_ex1);
		str_ex1[i] = (char*)malloc(strlen(temp_ex1) + 1);		// 동적할당을 선언 [ 영역크기 = 문자열 길이구하는 함수를 통한 문자열길이 + 1(NULL문자를 위한공간) ]
		strcpy(str_ex1[i], temp_ex1);							// 동적할당을 통해 공간을 확보하고 그 공간에 입력받아 임시저장한 문자열을 복사
	}

	for (int i = 0; i < 3; i++)
	{
		printf("\n%s\n", str_ex1[i]);		// 입력받은 문자열 출력
	}

	for ( int i = 0; i < 3; i++)		//  동적할당영역 반환 
	{
		free(str_ex1[i]);
	}

	printf("\n------------------- < 동적할당 활용 예제2 > ---------------------------------------------\n\n");

	// 예제1을 포인터를 이용해 함수로 만들어는 예제

	char temp_ex2[80];					// 임시 char 배열
	char* str_ex2[21] = { 0 };			// 문자열을 연결할 포인터 배열, 널값으로 초기화
	int i_ex2 = 0;

	while (i_ex2 < 20)												// 최대 20개의 문자열을 입력받아 동적할당후 저장 
	{
		printf("문자열을 입력하세요 : ");
		gets(temp_ex2);
		if (strcmp(temp_ex2, "end") == 0) break;					// 'end' 를 입력받으면 종료

		str_ex2[i_ex2] = (char*)malloc(strlen(temp_ex2) + 1);		// 동적할당 [ 영역크기 = 입력받은 문자열 길이 + 1(NULL 값을 위한) ]
		strcpy(str_ex2[i_ex2], temp_ex2);							// 할당된 영역에 입력받은 문자열 복사
		i_ex2++;
	}

	print_str_ex2(str_ex2);

	for (int i = 0; str_ex2[i] != NULL; i++)						// str_ex2에 연결된 문자열에 없을떄까지 반복
	{
		free(str_ex2[i]);											// 동적할당영역 반환
	}


	return 0;
}

// ========================================= 함수 정의부 ====================================================================

//	예제 2
void print_str_ex2(char** ps_ex2)		// [ 동적할당영역의 문자열을 출력하는 함수 ]
{
	// * 참고 *
	// main에서 print_str_ex2(str_ex2)로 호출받음
	// 생략되어 보이지 않지만 실제로는 아래의 내용이 있는 것!
	// --------------------------------------------
	// char **ps_ex2 = str_ex2;
	// --------------------------------------------

	while (*ps_ex2 != NULL)				// 포인터 배열의 값이 널이 아닌동안 반복
	{
		printf("%s\n", *ps_ex2);		// ps_ex2가 가리키는 것은 포인터 배열의 요소리고 이를 출력한다.
		ps_ex2++;						// ps_ex2가 다음 배열요소를 가리킨다.
	}

	//	* 참고1 
	//	main에서 print_str_ex2(str_ex2)로 호출받음
	//	**ps_ex2 = str_ex2 = *(*(&str_ex2[0])) = *(str_ex2[0]) [ 이때, str_ex2는 포인터 배열이므로 str_ex2[0]또한 포인터 ]
	//	즉, str_ex[0]의 값(주소값)에 해당하는 주소를 가진 메모리영역의 공간에 저장되어있는 값(입력받은 문자열)에 접근

	//	** 참고2
	//	'ps_ex2++' 는 ps_ex2 = ps_ex2 + 1 이므로 다음 반복에서
	//	printf("%s\n", *(ps_ex2 + 1)); 로 계산되고 
	//	여기서 *(ps_ex2 + 1) = *(*(&str_ex2[0]) + 1 ) = *(str_ex2[0] + 1) = *(str_wx2[1]) 이다.
	//	즉, 다음 포인터 배열에 들어가있는 주소값이 가리키고있는 문자열로 다음번째 입력받은 문자열값이다.
	//	이를 다음 문자열에 저장된 값이 NULL일때까지 반복
 
		
	
}

댓글()

[C 언어] 15. 함수포인터

프로그래밍/C언어|2022. 4. 11. 15:32

2022.04.11 - [프로그래밍/C언어] - [C 언어] 14. 응용포인터와 주소연산자

 

[C 언어] 14. 응용포인터와 주소연산자

2022.04.11 - [프로그래밍/C언어] - [C 언어] 13. 자기참조구조체 [C 언어] 13. 자기참조구조체 2022.04.11 - [프로그래밍/C언어] - [C 언어] 12. 포인터배열 [C 언어] 12. 포인터배열 2022.04.11 - [프로그래밍/C..

dlwlxo3819.tistory.com

 

====================== < 전체 소스코드 > ======================

 

#include <stdio.h>

// ========================================= 함수 선언부 ====================================================================

// 1) 함수 포인터 함수선언
int sum_1(int, int);							// [ 함수포인터가 가리킬 대상함수 ]

// 2) 함수포인터의 활용 함수 선언
void func_2(int(*fp)(int, int));			// [ 함수 포인터를 매개변수로 갖는 함수 ]
int sum_2(int a, int b);
int mul_2(int a, int b);
int max_2(int a, int b);



// ========================================= main 함수 ====================================================================

int main(void)
{
	//	15.함수 포인터는 함수의 사양을 100%정하지 못했을때,
	//	일단 만들면서 시간남으면 추가하는 등의 확장성 및 생산성의 향상을 노리고 작성한다.

	printf("\n------------------- < 1) 함수포인터 > ---------------------------------------------\n\n");

	int (*fp_1)(int, int);					// 함수 포인터 선언

	int res_1;

	fp_1 = sum_1;								// 함수 포인터에 대상 함수를 지정
	res_1 = fp_1(10, 20);						// 함수포인터를 이용해 함수 호출
	printf(" result : %d\n", res_1);



	printf("\n------------------- < 2) 함수포인터의 활용 > ---------------------------------------------\n\n");

	int sel_2;

	printf(" 1) 두 정수의 합\n");
	printf(" 2) 두 정수의 곱\n");
	printf(" 3) 두 정수에서 큰 값 출력\n\n");
	printf(" 원하는 연산을 선택하세요 : ");
	scanf("%d", &sel_2);

	switch (sel_2)
	{
	case 1: func_2(sum_2); break;
	case 2: func_2(mul_2); break;
	case 3: func_2(max_2); break;
	}

	printf("\n------------------- < 3)  보이드 포인터 > ---------------------------------------------\n\n");
	//	void 포인터는 가리키는 자료형이 정해지지않은 포인터다.
	//	즉, 가리키는 자료형이 가른 주소를 저장하는 경우 사용

	int a_3 = 10;
	double b_3 = 3.5;
	void* vp;				// void포인터 선언	= 가리키는 자료형이 정해지지않은 포인터

	vp = &a_3;
	printf("a_3 : %d\n", *(int*)vp);			//	사용할때마다 자료형을 지정해준다. [ (int*) = int형 포인터로 변환 ]

	vp = &b_3;
	printf("b_3 : %.1lf\n", *(double*)vp);		//	사용할때마다 자료형을 지정해준다. [ (double*) = double형 포인터로 변환 ]

	return 0;

}


// ========================================= 함수 정의부 ====================================================================

// 1) 함수 포인터 함수정의
int sum_1(int a, int b)					// [ 함수포인터가 가리킬 대상함수 ]
{
	return a + b;
}

// 2) 함수포인터의 활용 함수정의
void func_2(int(*fp)(int, int))			// [ 함수 포인터를 매개변수로 갖는 함수 ]
{
	int a, b;
	int res;

	printf("\n두 정수의 값을 입력하세요 : ");
	scanf("%d %d", &a, &b);

	res = fp(a, b);

	printf("\n결과값은 : %d\n", res);

}

int sum_2(int a, int b)
{
	return (a + b);
}

int mul_2(int a, int b)
{
	return (a * b);
}

int max_2(int a, int b)
{
	if (a > b) return a;
	else return b;
}

댓글()

[C 언어] 14. 응용포인터와 주소연산자

프로그래밍/C언어|2022. 4. 11. 15:30

2022.04.11 - [프로그래밍/C언어] - [C 언어] 13. 자기참조구조체

 

[C 언어] 13. 자기참조구조체

2022.04.11 - [프로그래밍/C언어] - [C 언어] 12. 포인터배열 [C 언어] 12. 포인터배열 2022.04.11 - [프로그래밍/C언어] - [C 언어] 11. 포인터 배열 [C 언어] 11. 포인터 와 배열 2022.04.11 - [프로그래밍/C언..

dlwlxo3819.tistory.com

 

====================== < 전체 소스코드 > ======================

 

#include <stdio.h>

// ========================================= 함수 선언부 ====================================================================

// 예제1 함수선언 
void swap_double_ptr(char** ppa, char** ppb);	// [ 이중포인터를 이용한 포인터교환 ]

// 예제2 함수선언 
void swap_ptr(char** ppa, char** ppb);			// [ 포인터를 이용한 포인터 교환 ]

// 2) 포인터배열 함수선언
void print_str(char** pps, int cnt);

// 예제3 함수선언
void print_ary_ex3(int(*)[4]);					// [ 배열포인터 출력함수 ]



// ========================================= main 함수 ====================================================================

int main(void)
{
	// 14. 응용포인터와 배열포인터

	printf("\n------------------- < 1) 이중포인터 개념 및 사용 > ---------------------------------------------\n\n");
	// 1) 이중포인터 개념 및 사용
	int a = 10;
	int* pi;
	int** ppi;

	pi = &a;
	ppi = &pi;

	printf("변수 \t 변숫값 \t&연산 \t\t*연산 \t **연산 \t\n\n");
	printf("a   %10d \t %10u\n",a, &a);												//	a값			a주소값
	printf("pi  %10u \t %10u \t %10d\n", pi, &pi, *pi);								//	a주소값		pi주소값		a값
	printf("ppi %10u \t %10u \t %10u \t %10u\n", ppi, &ppi, *ppi, **ppi);			//	pi주소값		ppi주소값	a주소값	a값
		
	//	결국 포인터는 값으로 다른 변수의 ㅈ소값을 가지는 것으로 그 자신도 주소를 가지고 있다
	//	이중 포인터는 또하나의 포인터를 선언해 기존의 포인터의 주소를 저장하는 것으로
	//	포인터ppi -> 포인터pi -> 변수a 를 가지게 하는것
	//	
	//	즉, ppi를 출력하면
	// 
	//	ppi = pi의 주소값		( = &pi) 
	// 
	//	&ppi = ppi의 주소값		( = &ppi )
	// 
	//	*ppi = ppi의 값에 해당하는 주소의 메모리공간에 저장된 값 = pi에 저장된 값 = pi값
	//				( = *(&pi) = pi )
	// 
	//	**ppi = ppi의 값에 해당하는 주소의 메모리공간에 저장된 값을 다시 주소로서 해당 주소에 해당하는 메모리공간속의 값
	//				= pi의 값에 해당하는 주소의 메모리공간에 저장된 값 = a에 저장된 값 = a값
	//					( = **ppi = *(*(&pi)) = *pi = *(&a)) = a )	

	printf("\n------------------- < 예제1 > ---------------------------------------------\n\n");
	
	// 

	char* pa_ex1 = "success";
	char* pb_ex1 = "failure";

	printf(" 변경전 : pa -> %s, pb -> %s\n", pa_ex1, pb_ex1);
	swap_double_ptr(&pa_ex1, &pb_ex1);
	printf(" 변경후 : pa -> %s, pb -> %s\n", pa_ex1, pb_ex1);

	printf("\n------------------- < 예제2 > ---------------------------------------------\n\n");
	char* pa_ex2 = "success";
	char* pb_ex2 = "failure";

	printf(" 변경전 : pa -> %s, pb -> %s\n", pa_ex2, pb_ex2);
	swap_ptr(&pa_ex2, &pb_ex2);
	printf(" 변경후 : pa -> %s, pb -> %s\n", pa_ex2, pb_ex2);

	printf("\n------------------- < 2) 포인터배열 > ---------------------------------------------\n\n");
	// 포인터를 배열로 선언 

	char* ptr_ary[] = { "eagle","tiger","lion", "squirrel" };		// 포인터배열 초기화
	int count;			// 배열요소갯수 저장변수

	count = sizeof(ptr_ary) / sizeof(ptr_ary[0]);	// 배열요소수 계산
	print_str(ptr_ary, count);						// 배열명과 배열요소수 주고 호출

	//	배열명 자체가 주소다
	//	즉, prt_ary = &ptr_ary[0]
	
	//	게다가 포인터 배열의 경우 각 요소(문자열)은 각각 실제 문자열처럼 주소를 가진다
	//	즉, "eagel" "tiger"등의 각 배열요소는 주소를 가지고 있다.

	printf("\n------------------- < 3) 포인터배열의 주소와 요소번호의 상관관계 > ---------------------------------------------\n\n");

	int ary_3[5];

	printf(" ary_3의 값 : \t\t%u\t\n", ary_3);				
	printf(" ary_3의 주소값 : \t%u\t\n", &ary_3);			// 주소로서의 배열명의 값	( 위와 값이 같다 =  < ary_3 = &ary_3 = &ary_3[0] > )
	printf(" ary_3 + 1의값 : \t%u\t\n", ary_3 + 1);		// 배열의 주소
	printf(" &ary_3 + 1의 값 : \t%u\t\n", &ary_3 + 1);

	// "%u", ary_3 + 1 = ary_3[0 + 1] 이므로 ary_3[1]의 주소를 의미하고 자료형이 int이므로 4만큼의 크기차이가 난다.
	// "%u", &ary_3 + 1 = ary_3 배열 자체의 주소 + 1 을 의미하고 이는 배열 전체의 다음번지를 의미하므로 
	//		= { &ary_3[0] + (요소수 * 자료형의 크기) } = { &ary_3[0] + (4 * 5) } = { &ary_3[0] + 20 } 

	printf("\n------------------- < 4) 배열 포인터 > ---------------------------------------------\n\n");

	// 배열형태의 포인터 - 2차원 배열저장가능
	int ary_4[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
	int(*pa_4)[4];				// int형 변수 4개의 배열을 가리키는 배열 포인터 ( !!! 괄호 반드시 해야함 !!! )
	pa_4 = ary_4;				

	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%5d", pa_4[i][j]);			// pa_4를 2차원 배열처럼 사용
		}
		printf("\n");
	}

	printf("\n------------------- < 예제 3 > ---------------------------------------------\n\n");

	int ary_ex3[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };

	print_ary_ex3(ary_ex3);

	printf("\n------------------- < 5) 2차원 배열 요소참조 원리 > ---------------------------------------------\n\n");

	int ary_5[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };

	// 둘은 같다 ( ary_5 전체 배열의 주소값 )
	printf(" < ary_5 전체 배열의 주소값 >\n");
	printf(" ary_5의 값 : \t\t\t\t\t%u\t\n", ary_5);
	printf(" ary_5의 2차원 배열 전체의 주소값 : \t\t%u\t\n\n", &ary_5);		

	// 모두 같다 ( ary_5[1]의 주소값 )
	printf(" < ary_5[1]의 주소값 >\n");
	printf(" ary_5 + 1의값 : \t\t\t\t\t%u\t\n", ary_5 + 1);		
	printf(" &ary_5[0] + ( 1 * sizeof(ary_5[0]))의 값 : \t\t%u\t\n", ((int)(&ary_5[0])) + ( 1 * sizeof(ary_5[0]))); // (int)를 추가한 이유는 ary_5 + num 은 ary_5[num]으로 자동 계산되서 
	printf(" *(ary_5 + 1)의 값 : \t\t\t\t\t%u\t\n", *(ary_5 + 1));
	printf(" ary_5[1]의 주소값 : \t\t\t\t\t%u\t\n\n", ary_5[1]);

	//둘은 같다 ( ary_5[1][2]의 주소값 )
	printf(" < ary_5[1][2]의 주소값 >\n");
	printf(" *(ary_5 + 1) + 2의값 :\t\t\t\t\t%u\t\n", *(ary_5 + 1) + 2);
	printf(" *(ary_5 + 1) + ( 2 * sizeof(ary_5[1][0]) )의 값 : \t%u\t\n\n", ((int)*(ary_5 + 1)) + (2 * sizeof(ary_5[1][0]))); // (int)를 추가한 이유는 ary_5 + num 은 ary_5[num]으로 자동 계산되서 

	// 둘은 같다 ( ary_5[1][2]의 값 )
	printf(" < ary_5[1][2]의 값 >\n");
	printf(" *(*(ary_5 + 1) + 2)의값 : \t\t%u\t\n", *(*(ary_5 + 1) + 2));
	printf(" ary_5[1][2]의 값 : \t\t\t%u\t\n\n", ary_5[1][2]);

	//	!!! &ary_5 = 100 으로 가정 !!!
	
	//	ary_5 + 1 = &ary_5[0] + ( 1 * sizeof(ary_5[0]) ) = 100 + (1 * 16 ) = 116
	//	*(ary_5 + 1) = ary_5[1]
	
	//	*(ary_5 + 1) + 2 = *(ary_5 + 1) + ( 2 * sizeof(ary_5[1][0]) ) = 116 + (2 * 4) = 124
	//	*(*(ary_5 + 1) + 2) = ary_5[1][2]

	//	* 정리 * 
	//	&ary_5			= 2차원 배열 전체의 주소
	//	ary_5			= 첫 번째 부분배열의 주소
	//	&ary_5[0]		= 첫 번째 부분배열의 주소
	//	ary_5[0]		= 첫 번째 부분배열의 첫 번째 배열 요소의 주소
	//	&ary_5[0][0]	= 첫 번째 부분배열의 첫 번째 배열 요소의 주소

	//	단,
	//	sizeof(ary_5)			= 배열전체의 크기			= 48	바이트
	//	sizeof(&ary_5[0])		= 주소의 크기			= 4		바이트
	//	sizeof(ary_5[0])		= 부분배열 전체의 크기	= 16	바이트
	//	sizeof(&ary_5[0][0])	= 주소의 크기			= 4		바이트






	return 0;
}

// ========================================= 함수 정의부 ====================================================================

// 예제1 함수정의
void swap_double_ptr(char** ppa, char** ppb)	// [ 이중포인터를 이용한 포인터교환 ]
{
	//	포인터의 주소를 매개변수로 이중포인터에 저장하고 포인터의 주소값을 값으로 가진 이중포인터간에 값을 교환
	// 
	//	즉, char **ppa = *(*(&pa_ex1)) = *("succece")
	//		char **ppb = *(*(&pb_ex2)) = *("failure")
	// 
	//	이때, "succec", "failure" 은 문자열이므로 자체적인 주소값을 가지고있다.
	//	즉, **ppa 는 "suceece" 배열의 주소값내의 값을 가리키게 되고,
	//		**ppb 는 "failure" 배열의 주소값내의 값을 가리킨다.

	char* pt;

	pt = *ppa;
	*ppa = *ppb;
	*ppb = pt;
}

// 예제2 함수정의
void swap_ptr(char* ppa, char* ppb)			// [ 포인터를 이용한 포인터 교환 ]
{
	//	main에서의 호출꼴 = swap_ptr(&pa_ex2, &pb_ex2);
	//	생략되었지만 실제로는 아래의 내용이 추가 되어있는 것
	//	--------------------------
	//	char* ppa, ppb;
	//	ppa = &pa_ex2;
	//	ppb = &pb_ex2;
	//	--------------------------

	char pt ;

	pt = *ppa;				// *ppa = ppa가 가지고 있는 값을 주소로 하는 메모리공간속 값에 접근
	*ppa = *ppb;
	*ppb = pt;

	//	위의 이중포인터와 같지만, 받을때 이중이 아닌 일반 포인터로 받음
	//	그래서 임시 저장하는 temp를 수준을 맞추기 위해 포인터가 아닌 일반 변수로 선언하였고 교환함

	// * 참고 *
	// ppa = &pa_ex2 = pa_ex2의 주소값
	// ppb = &pb_ex2 = pb_ex2의 주소값

}

// 2) 포인터배열 함수정의
void print_str(char** pps, int cnt)
{
	//	main에서의 호출꼴 = print_str(ptr_ary, count);
	//	생략되었지만 실제로는 아래의 내용이 추가 되어있는 것
	//	--------------------------
	//	char** pps;
	//  int cnt;
	//	pps = ptr_ary;		// **pps = *(*(&ptr_ary[0])) = *("eagle" ~ "squirrel)
	//	cnt = count;
	//	--------------------------

	int i;

	for (i = 0; i < cnt; i++)
	{
		printf("%s \n", pps[i]);	// pps[i] = (**pps[i]) = *(*(&ptr_ary[0]) + i) = *( ptr_ary[i] )
	}


}

// 예제3 함수정의
void print_ary_ex3(int(*pa)[4])			// [ 배열포인터 출력함수 ]
{
	int i, j;
	

	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%5d", pa[i][j]);			// pa_4를 2차원 배열처럼 사용
		}
		printf("\n");
	}
}

 

'프로그래밍 > C언어' 카테고리의 다른 글

[C 언어] 16. 메모리동적할당  (0) 2022.04.11
[C 언어] 15. 함수포인터  (0) 2022.04.11
[C 언어] 13. 자기참조구조체  (0) 2022.04.11
[C 언어] 12. 포인터배열  (0) 2022.04.11
[C 언어] 11. 포인터 와 배열  (0) 2022.04.11

댓글()

[C 언어] 13. 자기참조구조체

프로그래밍/C언어|2022. 4. 11. 15:25

2022.04.11 - [프로그래밍/C언어] - [C 언어] 12. 포인터배열

 

[C 언어] 12. 포인터배열

2022.04.11 - [프로그래밍/C언어] - [C 언어] 11. 포인터 배열 [C 언어] 11. 포인터 와 배열 2022.04.11 - [프로그래밍/C언어] - [C 언어] 10. 포인터 기초 [C 언어] 10. 포인터 기초 2022.04.11 - [프로그래밍/C..

dlwlxo3819.tistory.com

====================== < 전체 소스코드 > ======================

 

#include <stdio.h>
#pragma warning(disable:6031)

struct list					// 자기참조구조체
{
	int num;				// 데이터를 저장하는 멤버
	struct list* next;		// 구조체 자신을 가리키는 포인터멤버
};

#define Num_ary 20

int main(void)
{
	/*
	
	// 1. 자기참조 구조체
	struct list a = { 10,0 }, b = { 20,0 }, c = { 30,0 };	// 구조체변수 초기화
	struct list* head = &a, * current;						// 헤드 포인터 초기화

	a.next = &b;		// a의 포인터 멤버(next)가 b를 가리킴
	b.next = &c;		// b의 포인터 멤버(next)가 c를 가리킴

	printf("head -> num : %d\n", head->num);				// head가 가리키는 a의 num멤버 사용
	printf("head->next->num : %d\n", head->next->num);		// head로 b의 num멤버 사용


	printf("list all : ");
	current = head;						// 최초 temp포인터(current)가 a를 가리킴


	while (current != NULL)				// 마지막 구조체변수까지 출력 후, 종료
	{
		printf("%d  ", current->num);	// temp가 가리키는 구조체변수의 num 출력
		current = current->next;		// temp가 다음 구조체변수를 가리킴
	}

	printf("\n");

	*/


	// 2. 자기참조구조체 배열 

	struct list list_ary[Num_ary] = { {10,0}, };						// 구조체 배열 초기화
	struct list* head_ary = &list_ary[0], * current_ary;
	int i = 0;

	for (int i = 0; i < Num_ary-1; i++)
	{
		list_ary[i+1].num = 10 + ((i + 1) * 10);
		list_ary[i].next = &list_ary[i + 1];
	}


	printf(" head_ary -> num : %d\n", head_ary->num);				
	printf(" head_ary->next->num : %d\n", head_ary->next->num);		


	printf("\n list all : \n\n");
	current_ary = head_ary;						// 최초 temp포인터(current)가 a를 가리킴


	while (current_ary != NULL)				// 마지막 구조체변수까지 출력 후, 종료
	{
		
		printf(" 요소번호: %2d   %5d\n",i, current_ary->num);	// temp가 가리키는 구조체변수의 num 출력
		current_ary = current_ary->next;		// temp가 다음 구조체변수를 가리킴
		++i;
	}

	printf("\n");


	return 0;
}

댓글()