c

문자열

haniru 2025. 11. 7. 23:52

1. 화이트 스페이스는 화면에 보이지 않지만 공간을 차지하는 문자들인데, scanf 는 화이트 스페이스를 구분하지 않고 그냥 다음 문자를 읽어버려 전부 다 문자로 취급해버린다. 따라서 다음과 같이 해결할 수 있다.

 

1. 공백 추가: scanf(" %c", &ch);

2. 버퍼 비우기: getchar()

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main() {
	char ch1, ch2;

	// 화이트 스페이스 에러
    // 	scanf("%c%c", &ch1, &ch2);
	
	// 공백을 사용해 해결
	scanf(" %c %c", &ch1, &ch2);
	
    // 	fflush(stdin); // Linux/POSIX 에서는 동작 안함

	printf("[%c%c]", ch1, ch2);

	return 0;
}

 

2. getchar와 putchar 함수

getchar()의 반환타입이 int인 이유:

정상적인 문자: 0~255(ASCII 값)

파일 끝(EOF): -1

에러 처리를 위해 -1을 표현할 수 있어야 한다

char은 -1을 제대로 표현 못할 수 있다 (unsinged char인 경우)

화이트스페이스도 읽는

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main() {
	int ch1 = getchar(); // 표준 입력에서 문자 1개 읽기
	printf("입력한 문자 : ");
	putchar(ch1); // 표준 출력으로 문자 1개 출력

	return 0;
}

 

3. 아스키 코드값 출력

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main() {
	char ch;
	printf("문자 입력 : ");
	ch = getchar(); // 또는 scanf("%c", &ch);

	printf("%c 문자의 아스키 코드 값: %d", ch, ch);

	return 0;
}

 

4. while 안 scanf

출력값에 10이 찍히는데, 개행문자(\n)이 들어가기 때문이다 (버퍼에 저장되는 데이터의 끝에는 항상 개행 문자가 있음)

또 Ctrl + Z를 두번 누르면 루프를 빠져나온다

(첫번째 Ctrl + Z: 버퍼에 EOF 신호 전송하지만 버퍼가 비어있지 않으면 먼저 처리

두번째 Ctrl+Z: 버퍼가 완전히 비워진 상태에서 EOF 전송후 루프 종료한다고는 함)

윈도우는 Ctrl + Z, Linux/Mac은 Ctrl + D라고 하는데 이거는 확인해봐야 함

EOF란 파일의 끝이라는 뜻, 파일입력이 끝났음을 알리는 신호이

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main() {
    int res;   // scanf의 반환값 저장
    char ch;
    
    while (1) {
        res = scanf("%c", &ch);
        printf("res값 : %d\n", res);
        
        if (res == -1)  // EOF 체크
            break;
            
        printf("%d\n", ch);  // 문자의 ASCII 값 출력
    }
    
    return 0;
}

 

5. getchar

getchar(): 버퍼에서 1개씩 읽는다

scanf(): 버퍼에서 포맷에 맞게 읽는다

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void my_gets(char* arr, int size);

int main() {
	char str[7];

	my_gets(str, sizeof(str) / sizeof(str[0]));
	printf("입력한 문자열: %s", str);
	return 0;
}

void my_gets(char* str, int size) {
	int i = 0;
	int my_char;

	// my_char = getchar()을 먼저 하는 이유는 
    // while 조건을 검사하려면 미리 값이 있어야하기 때문이다 
    // (이게 없으면 while 조건문에서 쓰레기 값을 비교하게됨)
	my_char = getchar();
	printf("%d\\n", my_char);
	while ((my_char != '\\n') && (i<size - 1)) {
		str[i] = my_char;
		i++;
		my_char = getchar();

		printf("for int %d\\n", my_char);
	}
	str[i] = '\\0';
}

 

6. 학번과 학점을 입력하는 코드

콘솔에서는 화이트 스페이스가 항상 발생할 수 있다. 따라서 입력 후에 버퍼를 항상 비워주어야 한다

getchar()로 버퍼를 비울 수 있지만 사용자가 공백을 여러개 입력하면 ("12345   ") 문제가 된다고 한다

또 문자열을 숫자로 바꾸기 위해 #include <string.h>에 있는 atoi를 사용할 수 있다

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

void clear_input_buffer();

int main() {
	char student_number[20];
	int num;
	int grade;

	printf("학번 입력: ");
	scanf("%d", &num);
	getchar(); // 좋은 코드는 아니지만 버퍼 비우기, 화이트 스페이스 없애기 위해서

	//scanf("%s", &student_number);
	//clear_input_buffer();
	//num = atoi(student_number);

	printf("학점 입력: ");
	grade = getchar();

	printf("학번은 %d, 학점은 %c\\n", num, grade);
	return 0;
}

void clear_input_buffer() {
	int c;
	while ((c = getchar()) != '\\n' && c != EOF) {
		// '\n' 만날 때까지 모든 문자 제거
	}
}

 

왜 버퍼를 비워야 하는가?

scanf("%d", &num);  // 숫자만 읽음, '\n'은 버퍼에 남음!
grade = getchar();  // '\n'을 읽어버림!
```

**실행 예시 (getchar() 없을 때):**
```
학번 입력: 12345 (엔터)
            ↓
버퍼: '1' '2' '3' '4' '5' '\n'
      ←---- scanf가 여기까지 읽음
                              ↑
                        이게 버퍼에 남음!

학점 입력: 
(자동으로 '\n' 읽혀버림 - 입력 기회 없음!)
학번은 12345, 학점은 
 (줄바꿈 문자가 grade에 저장됨)

 

7. fgets, fgetc, fputc 비교

함수 역할 (입/출력 단위) 사용 목적 반환 자료형
fgets 문자열 (한 줄) 입력 버퍼 크기만큼의 문자열을 읽음 char * (문자열 시작 주소)
fgetc 문자 하나 입력 스트림에서 문자 하나를 읽음 int (문자 또는 EOF)
fputc 문자 하나 출력 스트림에 문자 하나를 씀 int (작성된 문자 또는 EOF)

 

 

8. C언어 메모리 구조와 문자열 포인터

char* dessert = "apple": dessert는 Data 영역에 있는 변경 불가능한 문자열의 주소를 가리킨다

char arr[] = "apple": "apple" 문자열이 Stack 영역에 있는 배열 arr의 실제 값으로 복사된다

문자열 상수 "apple" 자체는 Data 영역에 저장된다.

포인터 변수 dessert (지역 변수)는 Stack 영역에 저장된다.

dessert에는 "apple" 문자열이 저장된 Data 영역의 주소가 값으로 들어간다.

문자열 상수("apple")는 그 자체가 시작주소를 의미한다. *("apple"+1)은 Data 영역에 있는 문자열 "apple"의 시작 주소에서 1파이트 이동한 곳(p의 주소)의 값을 참조하여 p를 출력한다.

char* dessert = "apple";은 스택에 있는 포인터가 데이터 영역에 있는 읽기전용 문자열 상수를 가리키는 방식이고, malloc은 힙 영역에 읽기/쓰기 가능한 메모리를 동적으로 할당하는 방식이다

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void my_gets(char* arr, int size);

int main() {
	// dessert는 Stack 영역에 있는 주소 저장용 변수
    // "apple"과 "banana"는 Data 영역에 각각 저장된 문자열 상수
    // dessert = "banana"; 구문은 "apple"문자열 자체를 바꾸는 것이 아니라, 
    // dessert가 가리키는 주소를 "apple"이 있는 곳에서 "banana"가 있는 곳으로 변경하는 것이다
    
	char* dessert = "apple"; // 1. dessert(스택)에 "apple" 주소(데이터 영역) 저장
	printf("오늘 후식은 %s 입니다.\n", dessert);
	dessert = "banana";      // 2. dessert(스택)에 "banana" 주소(데이터 영역) 저장 (재할당)
	printf("내일 후식은 %s입니다.\n", dessert);
	return 0;
}

 

C 프로그램이 실행될 때 메모리는 크게 4가지 영역으로 나뉜다

영역 설명 특징 및 필기 연관 내용
Code (Text) 실행할 기계어 코드가 저장됨. 프로그램의 실제 명령어.
Data (데이터) 전역 변수, 정적(static) 변수가 저장됨. 문자열 상수("apple", "banana" 등)가 저장되는 곳.
Stack (스택) 지역 변수, 함수 호출 정보가 저장됨. **크기 제한(예: 64KB)**이 있으며, **int a = 7;**과 같은 지역 변수가 저장됨. main 함수 변수도 여기에 쌓임.
Heap (힙) 프로그래머가 동적으로 할당하는 메모리 영역. malloc() 함수로 할당하며, 크기가 비교적 큼 (필기 예: 16G 중 일부). 자바의 Stream 등으로 객체를 바로 생성할 때도 비슷한 동적 할당 영역을 사용함.

 

malloc과의 차이점

char* dessert = "apple"; char* ptr = malloc(10);
문자열 상수는 Data 영역에 저장됩니다. 메모리는 Heap 영역에서 할당받습니다.
메모리 할당 및 해제 관리가 필요 없습니다. 사용 후 반드시 **free()**로 메모리를 해제해야 합니다.
읽기 전용(Read-Only)이므로 dessert[0] = 'a';와 같은 수정은 런타임 오류를 발생시킵니다. 할당된 메모리는 읽기/쓰기 모두 가능합니다.

 

8. 스코프(Scope)와 변수의 메모리 저장 위치

스코프: 변수가 프로그램 코드 내에서 유효하고 접근 가능한 영역

C 프로그램은 실행될 때 메모리의 특정 영역(Code, Data, Stack, Heap) 에 할당되는데 전역변수와 함수 코드 등은 프로그램이 시작될 때 Data 영역에나 Code 영역에 미리 할당되어 메모리에 로드된다

 

전역변수: 함수 바깥에 선언된 함수이고 선언된 지점부터 프로그램 끝까지가 스코프이다. 주로 데이터 영역에 저장되고 (메모리 위치) 프로그램 실행 내내 존재한다

 

지역변수: 함수 안 또는 특정 블록 안에 선언된 변수이고 스코프는 변수가 선언된 함수나 블록 내부로 한정한다. 함수 호출이 끝나면 소멸한다. 스택 영역에 저장된다. 함수가 호출될 때 스택에 해당 함수의 스택 프레임이라는 영역이 만들어지고, 그 안에 지역변수가 존재한다. 함수가 끝나면 이 영역이 해제된다.

 

C나 C++는 성능 최적화를 위하 많은 변수와 메모리 할당이 프로그램이 실행되기 전(컴파일 시점)에 결졍된다. 또 전역변수나 함수 내에 선언된 큰 크기의 지역 배열등은 프로그램 실행 내내 사용할 최대 크기를 컴파일 시점에 미리 확보한다 (주로 Data 영역이나 Stack 영역 등) 만약 어떤 전역변수가 프로그램 전체 실행 시간 중 잠깐 동안만 사용되어도 프로그램이 끝날 때까지 메모리를 계속 점유한다. 또 함수 내에 int data[1000] 처럼 큰 배열을 선언하면 이 함수가 호출되는 동안에는 1000개의 정수 공간이 무조건 스택에 할당된다. 하지만 자바나 파이썬은 객체와 변수를 힙 영역에 저장하며 메모리 관리를 가비지 컬렉터 라는 도구가 실행시점에 전담한다 (동적 메모리 할당). 메모리 관리가 유연하고 편리하지만 가비지 컬렉션 과정 자체가 추가적인 성능 오버헤드를 발생시킬 수 있다.

 

9. scanf 함수를 사용한 문자열 입력

scanf의 문제점: 화이트 스페이스(스페이스, 탭, 엔터)를 구분자로 사용한다. 대안으로 fgets를 사용한다

gets의 문제점: 길이 제한이 없어 할당된 배열 크기보다 긴 문자열을 입력하면 버퍼 오버플로우가 발생하여 프로그램의 다른 영역을 침범할 수 있다 (C11부터 완전히 폐지됨)

fgets: 입력 길이를 제한해서 버퍼 오버플로우를 방지한다. 또 엔터까지 입력으로 간주하여 배열에 저장한다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void my_gets(char* arr, int size);

int main() {
	char str[80];

	printf("스페이스가 포함된 문자열 입력: ");
	//scanf("%s", str); // 스페이스바, 엔터 등을 구분자로 사용한다
	//gets(str);

	int len = sizeof(str); // 길이 변수화

	fgets(str, len, stdin); // gcc에서 사용 가능

	printf("%s\\n", str);
	printf("버퍼에 남아있는 단어 : %s\\n", str);
	return 0;
}

 

 

10. 표준 입력 함수의 버퍼 공유 문제

scanf("%d", &age) 실행 후: 사용자가 25를 입력하고 Enter를 누르면 버퍼에는 2, 5, \n 이 저장된다. scanf는 숫자만 읽어가고 버퍼에 \n를 남긴다

get(name) 실행시: gets() 함수는 문자열 입력을 위해 버퍼를 확인한다. 버퍼에는 이전에 남겨진 \n이 있어서 종료 신호로 인식하고 새로운 문자열을 입력할 기회 없이 바로 종료된다. 결과적으로 name 변수에는 아무것도 입력되지 않는다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void my_gets(char* arr, int size);

int main() {
	int age;
	char name[20];

	printf("나이 입력 : ");
	scanf("%d", &age);
	//getchar(); // 나중에 문제가 될 수 있다. (멈춰버릴 수 있음)
	printf("이름 입력 : ");
	gets(name);
	printf("나이 : %d, 이름 : %s\\n", age, name);
	return 0;
}

 

표준 입력 함수의 버퍼 공유 문제 수정

fgets를 사용해 버퍼 공유 문제를 근본적으로 해결한 방법 (숫자도 문자열로 입력받고 나중에 atoi 숫자로 변경한다)

atoit 함수 대신 strtol 함수를 사용할 수 있다고 한다

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

int main() {
	char strAge[10];
	char name[20];

	printf("나이 입력 : ");
	fgets(strAge, sizeof(strAge), stdin); // 17 -> "17"
	int age = atoi(strAge); // "17" -> 17

	printf("이름 입력 : ");
	fgets(name, sizeof(name), stdin);
	//printf("나이 : %s\\n", strAge);
	printf("나이 : %d\\n", age);
	printf("이름 : %s\\n", name);

	return 0;
}

 

11. 문자열을 출력하는 puts, fputs 함수

puts: 개행문자를 추가

fputs: 개행문자를 추가하지 않음

ps: 문자열 상수 "choco banana"가 저장된 데이터 영역의 시작 주소를 가리키는 포인터 변수. puts나 fputs는 이 주소를 따라가서 널 문자가 나올때까지 문자를 출력.

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

int main() {
	char str[80] = "apple juice";
	char* ps = "choco banana";

	puts(str); // apple juice 출력하고 줄 바꿈
	fputs(ps, stdout); // choco banana만 출력
	puts("milk"); // choco banana에 이어 milk 출력 

	return 0;
}

 

12. 지역변수

#include <stdio.h>

int main() {
	int a = 10, b = 20;
	printf("%d %d\\n", a, b);
	{
		int temp;
		int b = 90; // 지역변수 선언

		temp = a;
		a = b; // b: 지역변수 사용
		b = temp;
	}
	printf("%d %d\\n", a, b);
	return 0;
}

 

auto int a = 0; 에서 auto는 지역 변수의 기본 저장 클래스이며 생략 가능하다

지역 변수는 선언된 함수가 호출될 때 생성되고, 함수 실행이 끝나면 메모리에서 사라진다

main 함수의 a와 assign 함수의 a는 이름만 같고 완전히 다른 변수

#include <stdio.h>

void assign(void);

int main() {
	auto int a = 0;

	assign();
	printf("main 함수 안에서의 a 값: %d\\n", a);
}

// 지역변수는 선언한 곳에서 사용할 수 있고, 선언이 끝나면 메모리 공간에서 사라짐
void assign(void) {
	int a;
	a = 10;
	printf("assign 함수 a : %d\\n", a);
}

 

13. 문자열을 대입하는 strcpy 함수

strcpy는 배열이 아닌 문자열을 다루기 때문에 = 로 할수 없는 문자열의 내용 복사를 수행한다

strcpy 는 복사할 문자열의 길이를 확인하지 않는

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

int main() {
	char str1[80] = "strawberry";
	char str2[80] = "apple";
	char* ps1 = "banana";
	char* ps2 = str2;

	printf("최초 문자열 : %s\\n", str1);
	strcpy(str1, str2);
	printf("바뀐 문자열 : %s\\n", str1);
	strcpy(str1, ps1);
	printf("바뀐 문자열 : %s\\n", str1);
	strcpy(str1, ps2);
	printf("바뀐 문자열 : %s\\n", str2);

	return 0;
}

 

14. 문자열 복사 함수 strncpy

C언어에서 "apple-pie"나 "mango tree" 등은 프로그램 실행 시 데이터 영역 또는 읽기 전용 데이터 영역에 저장된다. 이 영역에 있는 문자열은 변경이 불가능하다.

 char str[20] = "mango tree"; 이 경우에는 문자열 상수 "mango tree"의 내용이 스택 영역에 할당된 배열 str에 복사되어 들어간다. 따라서 str 배열의 내용은 나중에 변경 가능하다 (strncpy)

strncpy(복사될 배열, 원본 문자열, 최대 몇 개의 문자를 복사할지 지정) 하므로 apple만 str의 처음 5칸으로 복사된

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

int main() {
	char str[20] = "mango tree";
	strncpy(str, "apple-pie", 5);
	printf("%s\\n", str);
	return 0;
}

 

15.문자열 비교 함수 strcmp

0: 두 문자열이 완전히 같을 때

양수: 첫번째 문자열이 두번째 문자열보다 사전순으로 뒤에 올 때

음수: 첫번재 문자열이 두번째 문자열보다 사전순으로 앞으로 올 때

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

int main() {
	char str1[80] = "pear";
	char str2[90] = "peach";

	//int result = strcmp(str1, str2);
	//printf("리턴값 : %d\\n", result); // 같으면 0, 다르면: str1이 크면 1, str2가 크면 -1
	
	printf("사전에 나중에 나오는 과일 이름 : ");
	if (strcmp(str1, str2) > 0) {
		printf("%s\\n", str1);
	}
	else {
		printf("%s\\n", str2);
	}

	return 0;
}

 

참고:

복사 strcpy

붙이기 strcat

길이 strlen

같은지 strcmp (0이면 같은거)

 

16. global 전역변수

프로그램의 모든 함수에서 접근 가능하고 프로그램이 시작될 때 생성되어 종료될 때까지 메모리에 남아있다

assign10 함수는 전역 변수 a를 변경하여 10이 되고, assign20 함수는 자신만의 지역 변수 a를 선언하고 20을 대입했으므로 전역변수 a에는 영향을 주지 않는다

전역변수는 프로그램 어디서든 접근하고 수정할 수 있기 때문에 코드가 복잡해지면 어떤 함수가 언제 이 변수를 수정했는지 추적하기 어려워져 디버깅이 매우 힘들어진다 (상태추적의 어려움) 권장 대안은 정적 지역 변수이다 (변수의 스코프는 지역변수처럼 해당 함수 내로 제한되지만 생존 주기는 전역변수처럼 프로그램 전체로 유지된다)

#include <stdio.h>

void assign10(void);
void assign20(void);

// 전역변수는 코드 양이 많아지면, 다른 함수에 영향을 미치므로
// 자제하는게 좋다
int a;

int main() {
	printf("함수 호출 전 a의 값 : %d\\n", a);

	assign10();
	assign20();

	printf("함수 호출 후 a값 : %d\\n", a);

	return 0;
}

void assign10(void) {
	a = 10;
}
void assign20(void) {
	int a;
	a = 20;
}

 

17. 정적 지역 변수

global 전역변수처럼 값을 유지하면서도 다른 함수가 이 변수에 접근하거나 변경하는 것을 막을 수 있다 (스코프 제한)

정적 지역 변수도 프로그램이 시작될 때 생성되어 종료될 때까지 메모리에 존재한다. 하지만 일반 지역 변수는 함수가 호출될 때 생성되고, 함수가 끝나면 소멸된다. 정적 지역 변수는 데이터 영역에 저장되고, 일반 지역 변수는 스택 영역에 저장된다. 정적 지역변수는 값을 지정하지 않아도 자동으로 0으로 초기화 되지만 일반 지역변수는 초기화 하지 않으면 쓰레기 값을 가진다. 또 정적 지역 변수는 함수가 다시 호출되어도 이전 값이 유지되지만 일반 지역 변수는 함수가 다시 호출될 때마다 새로 0부터 시작한다. 스코프는 둘다 선언된 함수 내부로 한정된다.

#include <stdio.h>

void auto_func();
void static_func();

int main() {
	printf("일반 지역 변수(auto)를 사용한 함수 : \\n");

	int a = 0;
	for (int i = 0; i < 3; i++) {
		auto_func();
	}

	printf("정적 지역 변수(static)를 사용한 함수 : \\n");
	for (int i = 0; i < 3; i++) {
		static_func();
	}
	return 0;
}

void auto_func() {
	int a = 0;
	a++;
	printf("auto function a %d\\n", a);
}
void static_func() {
	// 프로그램 실행과 동시에 변수가 살아있고, 프로그램이 사라질 때 같이 사라짐. 
	// 따라서 global 변수처럼 동작한다
	// 에러처리 하기도 좋고, 프로그램 실행과 동시에 계속 사용이 된다
	static int a; // 전역 변수는 초기 값이 0
	a++;
	printf("static function a %d\\n", a);
}

 

 

문제

 

문자열을 뒤집어 출력해 주세요

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

int main() {
	char str[100];

	puts("문자열을 입력해주세요 : ");
	fgets(str, sizeof(str), stdin); // scanf("%s", str);

	int len = strlen(str) - 1; // len은 \n 을 포함한 길이이므로 len-1 인덱스가 \n의 위치이다

	for (int i = len; i >= 0; i--) {
		printf("%c", str[i]);
	}

	return 0;
}

 

모음 갯수 세기

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

typedef struct {
	char vowel; // 모음문자
	int count;
} VowelCount;

int main() {
	char str[100];

	VowelCount vowel_counts[] = {
		{'a', 0},
		{'e', 0},
		{'i', 0},
		{'o', 0},
		{'u', 0}
	};

	int size_vowel_counts = sizeof(vowel_counts);
	int size_VowelCount = sizeof(VowelCount);
	int num_vowels = size_vowel_counts / size_VowelCount;

	int i = 0;
	int total_count = 0;

	fputs("영어 소문자 문자열을 입력해주세요 : ", stdout);
	fgets(str, sizeof(str), stdin);

	while (str[i] != '\\0') {
		char current_char = str[i];

		for (int j = 0; j < num_vowels; j++) {
			VowelCount *current_vowel_ptr = &vowel_counts[j];
			if (current_char == current_vowel_ptr->vowel) {
				current_vowel_ptr->count++;
				break;
			}
		}

		//switch (str[i]) {
		//case 'a':
		//case 'e':
		//case 'i':
		//case 'o':
		//case 'u': {
		//	total_count++;
		//}
		//}

		i++;
	}

	//int len = strlen(str);
	//for (int i = 0; i < len; i++) {
	//	switch (str[i]) {
	//	case 'a':
	//	case 'e':
	//	case 'i':
	//	case 'o':
	//	case 'u': {
	//		total_count++;
	//	}
	//	}
	//}

	printf("모음 분석 결과\\n");

	for (int j = 0; j < num_vowels; j++) {
		int current_count = vowel_counts[j].count;
		printf("%c : %d개\\n", vowel_counts[j].vowel, current_count);
		total_count += current_count;
	}

	printf("a, e, i, o, u 의 총 개수는 %d 입니다", total_count);

	return 0;
}

 

swap 문제

주소의 의한 호출(call by reference)를 이용해 변수 값 교환 문제를 푸는 것이다

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void Swap(int* a, int* b);

int main() {
	int a, b;  // (1) main의 지역 변수 a, b 선언
	scanf("%d %d", &a, &b); // &a, &b는 main 함수에 있는 정수 변수 a와 b의 메모리 주소
	// 예시: a=10, b=20 이라고 가정

	Swap(&a, &b); // (2) a와 b의 '주소'를 Swap 함수에 전달
	
	printf("%d %d\n", a, b); // (5) Swap 함수에 의해 변경된 최종 a, b 값 출력
}

void Swap(int* a, int* b) { // int *a, int *b: Swap 함수의 매개변수는 정수형을 가리키는 포인터
	// (3) Swap 함수의 지역 포인터 변수 a, b는 main의 a, b 주소를 저장함.
	int temp = *a; // *a: main의 a가 가리키는 곳 (값 10)을 읽어 temp에 저장
	*a = *b;       // *a: main의 a가 가리키는 곳에 main의 b의 값(20)을 덮어씀
	*b = temp;     // *b: main의 b가 가리키는 곳에 temp의 값(10)을 덮어씀
	// (4) main의 a는 20, main의 b는 10으로 변경됨
	printf("inner %d %d\n", *a, *b); 
}

 

Call by Value vs. Call by Reference

방식 설명 함수 내 변경의 영향 Swap 문제 해결 여부
Call by Value 변수의 을 복사하여 전달. 함수 내에서 값을 변경해도 원래 변수(main의 변수)에는 영향 없음. 불가능. (Swap이 일어나지 않음)
Call by Reference 변수의 주소를 전달 (포인터 사용). 함수 내에서 주소를 역참조하여 변경하면 원래 변수의 내용이 직접 변경됨. 가능. (제시된 코드 방식)

'c' 카테고리의 다른 글

배열, 포인터와 구조  (0) 2025.11.11
이중포인터 N차원 배열  (1) 2025.11.10
포인터  (0) 2025.11.06
배열  (0) 2025.11.05
함수와 배열 일부  (0) 2025.11.04