코딩 스터디

[241104] C++ 스터디 2주차

jisu0924 2024. 11. 4. 18:33

Codeup 2문제와 C++ 프로그래밍 강의 복습 3문제를 풀이한다.


1021 : [기초-입출력] 단어 1개 입력받아 그대로 출력하기(설명)

 

문제는 다음과 같다.

더보기
1개의 단어를 입력받아 그대로 출력해보자.
 
입력
한 단어가 입력된다.(단, 단어의 길이는 50자 이하이다.)

문자를 50개 저장하기 위해서는 char data[51] 로 선언하면 된다.

char data[51]="";
scanf("%s", data);

를 실행하면, data[51] 에 한 단어가 저장된다.
 
출력
입력된 단어를 그대로 출력한다.

단어를 입력하기 위해서는 정수형 변수(int)가 아닌 문자형 변수(char)로 변수를 입력받아야 한다. 따라서

#include <iostream>
using namespace std;

int main() {
	char data;
}

우선 char형 변수 data를 선언한다. 문제의 [입력] 부분을 보면 문자의 크기를 정해두기 위해 배열을 사용하는 것을 볼 수 있다. 배열을 사용하면 고정된 크기로 메모리를 할당할 수 있어 불필요한 메모리 사용을 절약할 수 있고 여러 개의 문자를 하나의 변수로 입력 가능하다. [입력]과 같이 문자 50개를 저장하기 위해 char data[51]을 선언한다.

#include <iostream>
using namespace std;

int main() {
	char data[51];
	cin >> data;
	cout << data;
}

 

이렇게 하면 string을 사용하지 않고도 문자열을 하나의 변수로 입력 가능하다.


1024 : [기초-입출력] 단어 1개 입력받아 나누어 출력하기(설명)

문제는 다음과 같다.

더보기

단어를 1개 입력받는다.

입력받은 단어(영어)의 각 문자를

한줄에 한 문자씩 분리해 출력한다.


참고
C언어에서는 문장을 저장하기 위해 특별한 방법을 사용하지 않고,
배열이라는 연속된 공간에 문자들을 연속으로 한 개씩 저장하는 방법을 사용한다.
단, 문자열(문자가 연속적으로 연결된 줄이라는 의미. 즉 문장)의 마지막임을 나타내기 위해
문자열의 마지막에 널(NULL) 문자라는 특별한 의미를 가지는 문자를 삽입해 문장임을 나타낸다.

널(NULL)은 영어 단어 자체의 의미로는, 아무런 가치가 없는 또는 아무것도 없는, 사용되지 않은 등의 의미를 가지는데,

문자로는 '\0', 아스키문자의 정수값은 0을 의미하며 NULL 로도 사용할 수 있다.

단어나 문장을 scanf("%s", ...); 로 입력받게 되면, 그 마지막에 널문자가 자동으로 입력되는 것이다.

그러므로, 본 문제는 그렇게 저장되어있는 영문자들을 한 개씩 순차적으로 검사해서,

문장의 마지막을 나타내는 널문자가 나올 때까지 모양을 만들어 출력하면 되는 것이다.

나중에 조건실행문, 반복문, 배열에 대해서 배우면 명확히 이해할 수 있게 된다.

printf("%s", ...); 는 결국, 저장되어있는 문자의 마지막까지, 즉 널문자가 나올 때까지 형식에 맞추어 출력해 주라는 의미를 가진다.


예시
char d[30]; //최대 30문자를 저장할 수 있는 저장 공간 준비. 마지막에 널문자 고려해야함
scanf("%s", d); //그 공간에 키보드로 입력된 내용을 저장. 단 공백이 있으면 거기까지만 입력됨.
for(i=0; d[i]!='\0'; i++) //저장된 내용을 하나하나씩 검사해서 널문자가 아닐 동안 아래 실행
{
   printf("\'%c\'", d[i]);
}

 

입력

단어(영어) 하나를 입력받는다.
(단, 단어의 길이는 20자 이하이다.)

 

출력

단어의 문자(영어)를 하나씩 나누어 한 줄에 한 개씩
' '로 묶어서 출력한다.

1021번의 연장선 문제라 할 수 있는데, 배열을 사용해서 각 열에 문자를 하나씩 불러오면 된다. 우선 아까와 같은 코드를 불러온다.

#include <iostream>
using namespace std;

int main() {
	char data[51];
}

배열의 각 요소를 순서대로 불러오기 위해서는 for 문을 사용하여 문자열의 길이만큼 반복해야 한다. 이때 문자열의 길이를 알아야 하는데, 문자열 길이를 알려주는 함수로 strlen을 사용할 수 있다. strlen 함수를 사용하려면 코드 맨 앞에 #include <cstring>을 추가하여 C-스트링 관련 함수들을 사용할 수 있는 헤더 파일을 include한다.

#include <iostream>
#include <cstring>
using namespace std;

int main() {
	char data[51];
	cin >> data;
	int a = strlen(data);
}

 

이렇게 하면 문자열 길이를 a에 저장할 수 있다. 이후 for문을 사용하여 문자열의 각 요소들을 차례대로 불러와 준다.

#include <iostream>
#include <cstring>
using namespace std;

int main() {
	char data[51];
	cin >> data;
	int a = strlen(data);
	for (int i = 0; i < a; i++)
		cout << "'" << data[i] << "'" << endl;
}

각 요소를 한 줄씩 출력하려면 endl을 붙여서 줄바꿈을 해야 한다. endl은 줄을 바꾸어 다음 요소가 새 줄에 출력되도록 해주기 때문에 배열의 각 요소들이 하나씩 한 줄에 출력된다. 결과는 다음과 같다.


동적메모리 할당 및 반환

C++에서 동적 메모리를 할당하고 반환하기 위해서는 new 연산자와 delete 연산자를 사용한다. new 연산자는 객체 할당 시 생성자를 호출하고, delete 연산자는 객체의 동적 소멸을 위해 소멸자를 호출 뒤 객체를 힙에 반환한다.

new와 delete는 C++ 기본 연산자로, 사용 형식은 다음과 같다.

데이터 타입 *포인터변수 = new 데이터타입;
delete 포인터변수;

 

정수형 공간의 동적 할당 및 반환의 예시는 다음과 같다.

#include <iostream>
using namespace std;

int main() {
	int* p; 

	p = new int; //int 타입 1개 할당
	if (!p) {
		cout << "메모리를 할당할 수 없습니다.";
		return 0;
	} //p가 NULL이면 메모리 할당 실패

	*p = 5; //할당 받은 정수 공간에 5 삽입
	int n = *p; //new 없음
	cout << "*p = " << *p << endl;
	cout << "n = " << n << endl;

	delete p; //할당 받은 메모리 반환
    //delete n X
}

 

 

배열의 동적 할당 및 반환을 하기 위해서는 '데이터 타입 *포인터변수 = new 데이터타입'의 형식을 따르되, 데이터타입 뒤에 [배열의 크기]를 정의한다.delete 시에는 포인터 변수 앞에 []를 붙여줌으로써 배열을 반환한다.

데이터 타입 *포인터변수 = new 데이터타입 [배열의 크기];
delete [] 포인터변수;

 

 

문제는 다음과 같다. 

더보기

사용자로부터 입력할 정수의 개수를 입력 받아 배열을 동적 할당 받고, 하나씩 정수를 입력 받은 후 합을 출력하는 프로그램을 작성하라.

우선 사용자로부터 입력할 정수의 개수를 입력 받는다.

#include <iostream>
using namespace std;

int main() {
	cout << "입력할 정수의 개수는?";
	int n;
	cin >> n;
	if (n <= 0) return 0; //0이하면 프로그램 작동 X
}

이후, n개의 정수를 저장하기 위해 배열을 동적 할당 받는다. 이를 위해 우선 포인터 변수 p를 선언하고, new 연산자를 사용하여 n 크기의 배열을 동적 할당한다. new 연산자를 사용하여 n개의 정수를 저장할 수 있는 배열을 동적 할당할 수 있다. 이렇게 할당한 메모리는 프로그램이 끝날 때 자동으로 해제되지 않기 때문에 사용이 끝난 후에는 delete 연산자를 사용하여 메모리를 반환해 주어야 한다.

#include <iostream>
using namespace std;

int main() {
	cout << "입력할 정수의 개수는?: ";
	int n;
	cin >> n;
	if (n <= 0) return 0;
	int* p = new int[n];
	if (!p) {
		cout << "메모리를 할당할 수 없습니다.";
		return 0;
	}

	delete [] p;
}

 

n개만큼 반복하여 정수를 입력 받아야 하니 for문을 사용하여 정수를 각각 입력 받은 후, 이를 sum에 저장하여 결과를 출력하면 다음과 같다.

#include <iostream>
using namespace std;

int main() {
	cout << "입력할 정수의 개수는?: ";
	int n;
	cin >> n;
	if (n <= 0) return 0;
	int* p = new int[n];
	if (!p) {
		cout << "메모리를 할당할 수 없습니다.";
		return 0;
	}

	for (int i = 0; i < n; i++) {
		cout << i + 1 << "번째 정수: "; //배열은 항상 인덱스가 [0]부터 시작하기 때문에 1번째부터 입력받기 위해서는 +1을 해 주어야 한다.
		cin >> p[i];
	}
	
	int sum = 0;
	for (int i = 0; i < n; i++)
		sum += p[i];

	cout << "평균은 " << sum / n << endl;

	delete [] p;
}

출력 결과는 다음과 같다.


객체 배열의 동적 생성 반환

객체를 동적으로 생성하기 위해서는 포인터 변수의 데이터 타입으로 클래스 이름을 사용해야 한다. new 연산자를 사용하여 메모리를 할당할 때 클래스의 생성자를 호출하여 객체를 생성하고 그 주소를 포인터 변수에 저장한다. 예를 들어, Circle* p = new Circle();와 같이 작성하면 Circle 타입의 객체를 동적으로 생성하고, 그 주소를 포인터 변수 p에 저장할 수 있다. 그러나 여기서 주의해야 할 점은 동적 메모리를 할당할 때 기본 생성자를 호출하려면 생성자 뒤에 ()를 꼭 붙여야 한다.

 

Circle 객체의 동적 생성과 반환을 응용한 문제는 다음과 같다.

더보기

정수 반지름을 입력받고 Circle 객체를 동적 생성하여 면적을 출력하라. 음수가 입력되면 프로그램은 종료한다.

 

우선 기본적으로 Circle 클래스를 생성하고, 반지름을 저장하기 위한 생성자 두 개를 정의한다. 이렇게 생성자를 통해 객체가 생성될 때 반지름 값이 초기화되도록 한다. 이후, **면적을 계산하는 멤버 함수 getArea()**를 선언한다. delete 연산자를 사용하면 소멸자가 자동으로 호출되기 때문에, 동적 할당된 메모리를 반환하려면 소멸자를 정의해 주어야 한다 (동적 메모리 문제이기에 객체와 클래스의 기본 개념에 대한 설명은 생략한다.)

#include <iostream>
using namespace std;

class Circle {
	int radius;
public:
	Circle();
	Circle(int r);
	~Circle();
	void setRadius(int r) { radius = r; }
	double getArea() { return 3.14 * radius * radius; }
};

Circle::Circle()
{
	radius = 1;
	cout << "생성자 실행 raius = " << radius << endl;
}

Circle::Circle(int r)
{
	radius = r;
	cout << "생성자 실행 radius = " << radius << endl;
}

Circle::~Circle()
{
	cout << "소멸자 실행 radius = " << radius << endl;
}


int main() {

}

이후, 각 원소 객체의 기본 생성자 Circle()을 실행하기 위해 객체 배열을 생성한 후 delete로 메모리를 반환한다. (틀을 미리 만든다.) 그리고 각 배열 원소의 반지름을 설정하기 위해 setRadius() 함수를 사용하여 반지름 값을 지정해 준다. (main() 함수로 넘어온다.)

int main() {
	Circle* pArray = new Circle[3];

	pArray[0].setRadius(10);
	pArray[1].setRadius(20);
	pArray[2].setRadius(30);
}

이후, 각 원소의 면적을 출력하기 위해 for문을 사용하여 입력을 받아야 한다. 여기서 배열의 주소값을 담은 포인터 p를 선언한 후, p->getArea()를 호출하여 각 원소의 면적을 출력한다. 포인터 p는 pArray 배열을 가리키며 p++로 다음 배열 원소로 이동하면서 반복한다.

int main() {
	Circle* pArray = new Circle[3];

	pArray[0].setRadius(10);
	pArray[1].setRadius(20);
	pArray[2].setRadius(30);

	for (int i = 0; i < 3; i++) {
		cout << pArray[i].getArea() << endl;
	} //방법1

	Circle* p = pArray;
	for (int i = 0; i < 3; i++) {
		cout << p->getArea() << endl;
		p++;
	} //방법2
	
	delete [] pArray;
}

 

출력 결과는 다음과 같다.

 


 string 클래스를 이용한 문자열 사용

문제는 다음과 같다.

더보기

5개의 string 배열을 선언하고 getline()을 이용하여 문자열을 입력 받아 사전순으로 가장 뒤에 나오는 문자열을 출력하라. 문자열 비교는 <, > 연산자를 간단히 이용하면 된다.

 

우선 string 클래스를 이용해야 하기 때문에 string을 헤더 파일을 include한다. 이후 5개의 string 배열을 선언해 준다.

#include <iostream>
#include <string>
using namespace std;

int main() {
	string name[5];
}

이제 이름 5개를 입력받기 위해 getline() 함수를 사용하여 문자열을 입력받는다. getline() 함수는 cin과 달리 공백을 포함한 문자열 입력이 가능하므로, 이름을 입력받을 때 유용하다.

 

int main() {
	string name[5];

	for (int i = 0; i < 5; i++) {
		cout << "이름 >> ";
		getline(cin, name[i], '\n');
	}
}

 

이후, latter라는 string형 변수에 names 배열의 0번째 인덱스를 저장해 둔다. 이후 for문을 사용하여 나머지 인덱스 [1, 2, 3, 4]의 문자열과 latter를 사전 순으로 비교해 나간다. 만약 현재 비교 중인 문자열이 latter보다 사전 순으로 뒤에 있다면 latter를 해당 문자열로 변경하여 가장 뒤에 오는 문자열을 찾는다. 결과는 다음과 같다.

 

'코딩 스터디' 카테고리의 다른 글

[250128] C 스터디 2주차  (0) 2025.01.29
[241125] C++ 스터디 5주차  (0) 2024.11.25
[241119] C++ 스터디 4주차  (0) 2024.11.20
[241112] C++ 스터디 3주차  (1) 2024.11.12
[241006] C++ 스터디 1주차  (10) 2024.10.08