이름공간(namespace)


C언어에서 한 단계 더 발전한 언어, 바로 C++이 C언어와 차이점을 두고 있는 것은 무엇일까요?


우리는 그 차이점 중에서 한가지를 이야기해보려 합니다.

우선 바로 코드를 보면서 어떤 주제에 대해서 설명할 지 감을 잡아보겠습니다.



#include <iostream>

int main() {
        char input[50];
	std::cin >> input;
	std::cout << input << std::endl;
}



이 짧은 코드를 보며 C언어에서 보지 못한 것이 보이시나요?


몇가지 보입니다. 우선 iostream이라는 파일을 include하고, std::in과 std::cout이 보이네요. std::endl라는 것도 보입니다.




하나씩 보도록 하지요.


● #include <iostream>

원래는 iostream.h 헤더 파일 입니다만 c++에서는 .h확장자를 붙이지 않아도 됩니다. 그래서 파일명만 써주도록 하는 거죠. 우리는 이 헤더파일을 include 해주어야 콘솔 출력, 입력을 할 수 있습니다(물론 다른 헤더파일을 써도 할 수는 있습니다..). (아, iostream은 input, output stream이라는 뜻이라는 거~)



● std::cin

cin은 콘솔 입력을 담당하는 객체입니다. >>은 오퍼레이터로 연산자를 의미합니다. cin에서 >>은 콘솔로 입력할때 쓰이는 연산자입니다. 음 그냥 cin>>은 이 후에 데이터에 콘솔로 입력하여라라는 것입니다(in앞에 c는 console의 c입니다.).  C언어에서 scanf와 아주 비슷한 역할을 하는 녀석입니다.

그 앞의 std는 우리가 오늘 이야기할 네임스페이스라고 합니다.


● std::cout

cin을 알았으니 cout도 대충 눈치채셨겠죠? <<역시 연산자를 의미합니다. 

cout은 콘솔출력을 담당하는 객체입니다. 누군가가 cout 보고 카우트라고 하는데 그럼 cin은 킨이냐?


● std::endl

endl은  endline으로 줄을 바꾸어 줍니다. 


namespace

여기서 자주 등장하는 키워드는 무엇일까요? 바로 std입니다. 

std는 네임스페이스의 이름입니다. 네임스페이스는 이름 공간으로 그 변수를 구분해주는 역할을 합니다. 


왜 쓰일까요?

어떤 개발자 A, B 두명이 있습니다. 서로 협동하여 프로젝트를 진행하고 있지요. 

프로젝트를 진행하던 중 개발자A는 함수를 func(int a,int b)해서 함수를 선언했습니다. 개발자 B는 생각없이 func(int a,int b)로 다른 기능을 하지만 함수 이름은 A가 정한것과 같이 정의해버렸습니다.


나중에 프로젝트가 진행될때 같은 함수이름과 같은 매개변수 때문에 오류가 나게 되지요.


그렇게 등장한게 네임스페이스입니다. 개발자 A는 namespace의 이름을 namespaceA, 그리고 개발자 B는 namespaceB로 자신의 함수를 선언합니다. 이렇게 하면 충돌할 확률이 줄어들겠죠??


와 그런데도 namespace이름마저 같으면 답없다 너네 둘은


네임스페이스를 정의하는 것은 너무 쉽습니다. 그저 namespace 키워드를 써주고 namespace의 이름을 정해 중괄호({, })로 묶어주기만 하면 끝입니다.


namespace 네임스페이스 이름{

...

변수나 함수

...

}


어때요? 참 쉽지 않나요? 그래서 네임스페이스에 존재하는 함수와 이름을 사용하려면 네임스페이스이름::함수 또는 변수 로 써주면 됩니다. 


예를 한번 볼까요?





#include <iostream>

namespace myNamespace {
	int a, b;
	int sum(int a, int b) {
		return a + b;
	}
}
int main() {
	myNamespace::a = 30;
	myNamespace::b = 40;
	std::cout << "30+40="<< myNamespace::sum(myNamespace::a, myNamespace::b) << std::endl;
}


myNamespace라는 네임스페이스에서는 a,b라는 변수, sum이라는 함수가 있네요. 얘네들을 써먹어서 a+b를 sum함수로 값을 도출해냅니다. 그러니 myNamespace::가 앞에 붙어있죠.



원하는 결과가 나오죠?


네임스페이스안에 네임스페이스를 지정할 수도 있습니다. 그렇게 되면 충돌확률은 현저히 줄어들겠네요. 하지만 쓸 일이 거의 없습니다. 이름도 길어지고.


namespace 네임스페이스 이름{

namespace 네임스페이스 이름1{

...

변수나 함수

...

}

namespace 네임스페이스 이름2{

...

변수나 함수

...

}

namespace 네임스페이스 이름3{

...

변수나 함수

...

}

}



접근하는 방법도 역시 똑같습니다.

네임스페이스::네임스페이스이름n::함수나 변수명



좋은것 같은 데 귀찮아

근데요. 사실 우리는 공부하는데 일일이 std::붙어주는 게 여간 귀찮은게 아니죠.


그런 경우에는 using namespace 네임스페이스이름 을 써주기만 하면 namespace의 이름으로 구분할 것 없이 바로 변수, 함수 등의 데이터를 사용할 수 있습니다.


이렇게 말입니다.


#include <iostream>

using namespace std;
namespace myNamespace {
	int a, b;
	int sum(int a, int b) {
		return a + b;
	}
}

using namespace myNamespace;
int main() {
	a = 30;
	b = 40;
	cout << "30+40="<< sum(a, b) << endl;
}


바로 위의 코드를 네임스페이스를 사용하지 않고 구현했습니다. 코드가 확 줄고 편해졌지요?


이제까지 네임스페이스를 알아보았습니다.


다음에 봐요~



반응형
블로그 이미지

REAKWON

와나진짜

,

구조체와 포인터


지난 번에 구조체에 대한 이야기를 쬐~~~끔 했었죠? 이번에도 구조체를 가지고 놀아봐요.


구조체를 통해서 ,이야기했다 시피 여러가지 자료형을 통합적이고 효율적으로 작업을 할 수 있는 것이 장점입니다.


자료형이라...

우리는 이제까지 int, char, double, float 같은 자료형을 많이 봐왔지요. 하지만 구조체 자체도 자료형이 될 수 있습니다. 즉, 변수로 선언이 가능하다는 것이죠.




한번 되짚어 봅시다. int 자료형 변수 a를 선언하고 10이라는 값을 집어 넣어 보아라 한다면 우리는 식은 죽 먹기로 해낼 수 있습니다.


int a;

a = 10;


이렇게 자료형을 통한 변수는 값을 대입하는 것 외에도 


1. 매개변수로 쓰일 수 있다.

2. 포인터로 참조할 수 있다.

3. 배열로 쓰일 수 있다.

4. 구조체의 변수로 쓰일 수 있다.


뭐 이밖에도 여러분이 더 잘 알거에요.


그래서 무슨 말이 하고 싶은 거냐 넌?


구조체도 자료형이 될 수 있다고 했습니다. 그러니까 위와 같이 쓰일 수도 있다는 이야깁니다. 똑같습니다. 위의 순서대로 구조체를 갖고 놀아 봅시다.


일단 매개변수로 쓰이는 경우를 보도록 하지요.


#include <stdio.h>

typedef struct student{
	char *name;
	int math;
	int kor;
	int eng;
} student;

float avg(student person);
student getHonorStudent(student me, student you);

int main() {
	student reakwon = { "REAKWON",40,50,40 };
	student seonmi = { "선미",90,95,100 };

	student honorStudent = getHonorStudent(reakwon, seonmi);
	printf("우등생은 %s입니다.\n", honorStudent.name);
	return 0;
}

float avg(student person) {
	return (person.math + person.kor + person.eng) / 3.0;
}
student getHonorStudent(student me, student you) {
	if (avg(me) > avg(you))
		return me;
	if (avg(me) < avg(you))
		return you;
}

전교생이 두 명인 학교에서 우열을 참 가리기 쉬운 학생 두명이 있습니다(그러니 평균이 같은 학생은 없다는 가정을 하겠습니다). 누가 우등생인가를 구하는 코드입니다.




위 코드에서 reakwon이라는 학생 성적은 제 고등학교 시절과 정확히 똑같군요.


우리는 avg나 getHonorStudent함수에서 매개변수로 쓰인 student 구조체를 주목해야합니다. 변수가 매개변수로 함수로 전달하는 것과 일치하죠. 그 매개변수 앞에는 student라는 자료형이 있는 것과 같은 겁니다.


결과를 보죠.



예상했다 싶이 선미라는 아이가 우등생이네요.

오~ 매개변수로 쓰일 수 있구나!


두번째, 포인터로 참조할 수도 있습니다.

우리는 포인터를 통해 그 주소에 접근할때 *를 이용해서 접근했었죠.

포인터도 역시 똑같습니다.


(*구조체 변수).변수이름


구조체는 이와 같은 접근 방법외에도 다른 방법으로도 포인터를 통해 참조할 수 있습니다. "->" 이와 같은 표시로 말이죠.


구조체 변수->변수이름


마치 화살표 같은게 포인터 티가 나죠?


이제 코드로 한번 확인해보도록 합시다.


#include <stdio.h>

typedef struct student{
	char *name;
	int math;
	int kor;
	int eng;
} student;


int main() {
	student reakwon = { "REAKWON",40,50,40 };
	student *me = &reakwon;

	printf("me의 크기:%d\n", sizeof(me));
	printf("reakwon의 주소:%p, me가 가리키는 주소:%p\n", &reakwon, me);
	printf("\n");
	printf("포인터를 통해서 값을 읽어오는 방법 1");
	printf("수학:%d, 국어:%d, 영어:%d\n", (*me).math,(*me).kor,(*me).eng);

	printf("\n");
	printf("포인터를 통해서 값을 읽어오는 방법 2");
	printf("수학:%d, 국어:%d, 영어:%d\n", me->math, me->kor, me->eng);
	
      return 0;
}



다음 결과 사진을 보고서 다시 이야기해 보도록 합시다.




포인터를 배울때와 같이 변수 me는 reakwon의 주소를 값으로 갖고 있습니다. 그리고 접근하는 방법 두가지 역시 같은 값을 나타내고 있습니다.


하지만 크기를 보세요. 구조체 포인터는 역시 포인터 크기(4바이트)와 같은 크기입니다. 주소만 갖고 있으면 되기 때문이죠.


그림으로 그려보면 이런 그림이겠군요.





우리는 한가지 생각해볼 점이 있습니다. 

구조체의 크기가 크고 함수 매개변수로 쓰일 경우 어떻게 넘겨주는 것이 더 효율적일까요?

값을 복사하는 normal한 매개변수로 쓴다면 구조체의 크기만큼 복사해야합니다.

그러나 포인터를 사용한다면 단지 주소값만 넘겨주면 되기 때문에 시간 면에서나 효율 면에서 유리할 수 있습니다.


물론 포인터는 매개변수의 변형을 일으킬 수도 있지만, 그런 원치않는 조작을 막기위해서 const라는 키워드가 존재하는 겁니다.



이제 구조체를 배열로 관리해보겠습니다. 역시 쉽습니다. 바로 코드로 봅시다.


#include <stdio.h>


typedef struct student{
	char name[30];
	int math;
	int kor;
	int eng;

} student;

float avg(student who) {
	return (who.math + who.kor + who.eng) / 3.0;
}

int main() {
	
	student students[3];

	for (int i = 0; i < 3; i++) {
		printf("이름:");
		scanf("%s", students[i].name);

		printf("수학 점수:");
		scanf("%d", &students[i].math);

		printf("국어 점수:");
		scanf("%d", &students[i].kor);

		printf("영어 점수:");
		scanf("%d", &students[i].eng);
		printf("\n");
		
	}


	for (int i = 0; i < 3; i++) {
		printf("%s의 점수\n",students[i].name);
		printf("수학 %d, 국어 %d, 영어 %d\n",
			students[i].math, students[i].kor, students[i].eng);
		printf("평균 %.1lf\n",avg(students[i]));
		printf("\n");
	}

	return 0;
}



세명의 학생의 이름과 점수를 입력받고 점수와 평균을 출력해주는 코드입니다. 


배열을 포인터 연산으로 나타낼 수 있듯이 구조체 배열 역시 포인터 연산으로 나타낼 수 있습니다. 위의 코드를 (*(students+i)).kor 과 같이 코드를 한 번 바꾸어 실행해보세요.


위 코드의 결과가 아래의 캡처화면입니다.



구조체는 이렇게 편리합니다. 




이제 마지막, 구조체는 구조체를 포함할 수 있습니다. 이것도 역시 변수와 같은 성격이죠. 구조체를 변수로 쓰는 방법은 아래와 같습니다.


typedef struct person {

char name[30];

int age;

char sex[10];

struct person friends[3];

} person;



구조체 안에 같은 구조체 타입의 변수가 배열로 들어가 있습니다. 구조체는 자료형이라고 했기 때문에 뭐 놀랍지도 않군요.


물론 다른 구조체 타입의 변수까지 멤버로 가질 수도 있습니다. 이런 엉뚱한 구조체는 어디서 쓰일까요?


나중에 자료구조에서나 알고리즘에서 트리의 노드와 같은 것으로 쓰일 수가 있습니다.



이제까지 구조체에 대해서 공부해봤습니다. 바이바이~

반응형
블로그 이미지

REAKWON

와나진짜

,

구초제(Structure)

 

우리는 같은 자료형을 여러개 쓸 때는 배열이라는 것을 썼었죠. 아주 유용합니다. 배열과 반복문을 통해서 조금 더 쉽게 프로그래밍을 할 수 있었습니다.

 

하지만 같은 자료형이 아닌, 다른 자료형을 하나로 통합해서 관리해야할때는 어떻게 할까요??

 

통합적이게 데이터를 묶고 쉽게 접근할 수 있는 기법이 바로 구조체라고 합니다.

 

만약 구조체가 없이 사람들의 데이터를 관리한다고 칩시다.

 

char name[MAX_N][30];

int age[MAX_N];

char phone[MAX_N][30];

 

자료형이 서로 다르니, 서로 같아도 쓰임새가 다르니, 여러개의 변수를 관리할 수 밖에 없습니다. 프로그래밍의 가독성과 효율성을 떨어뜨리게 되는 것이죠.

 

 

하지만 구조체는 우리의 작은 소원을 들어줍니다. 하나로 묶는 것이죠. 

구조체를 정의하는 방법은 상당히 간단합니다. 이렇게요.

 

struct User{

char name[30];

int age;

char phone[30];

}

 

User가 바로 구조체의 이름이며, 그 안의 변수들은 멤버변수라고 합니다.

 

이렇게 데이터를 묶어서 배열로 정의하게 되면 저 위의 코드를 조금 더 편하게 관리하고 접근할 수 있습니다.

 

struct User user[MAX_N];

 

대충 이런 느낌입니다. 쉽죠?

 

그럼 오늘 포스팅을 이걸로 마치겠습니다.

 

이제 본격적으로 구조체를 어떻게 사용하는 지 알아보도록 하겠습니다.

구조체 정의는 위에서 본것과 같이 함수밖에서(전역) 구조체 안의 멤버 변수들을 선언해줍니다. 

struct 구조체이름{

...

변수

...

};

 

그리고 함수안에서는 다음과 같이 사용할 수 있습니다.

 

struct 구조체이름 구조체변수명={ ..., 멤버 변수 값, ...};

 

이렇게 한꺼번에 구조체에 선언된 변수 순서대로 값을 초기화 할 수도 있고

또는

 

struct 구조체이름 구조체변수명;

구조체변수명.멤버변수명=변수값

...

 

하나하나 명시적으로 변수에 값을 할당할 수 있습니다.

 

변수에 접근하려면 어떻게 할까요??

그것도 역시 간단합니다. 구조체변수에 .(dot)을 찍어서 멤버변수명을 입력해서 접근할 수 있습니다.

 

구조체변수명.멤버변수명

 

 

이제까지 설명했던 내용을 코드로 간단하게 사용해보도록 합시다.

#include <stdio.h>
struct User {
        char name[30];
        int age;
        char phone[30];
};
int main() {
        struct User user = { "REAKWON",2018,"010-????-????" };
        printf("이름:%s\n", user.name);
        printf("나이:%d\n", user.age);
        printf("전화번호:%s\n", user.phone);
}

 

원하는 대로 결과가 나오죠?

 

그런데 우리는 구조체를 typedef로 조금 더 간단하게 선언할 수 있습니다. 

 

typedef은 자료형을 조금 더 편하게 관리하기 위해서 쓰입니다.

typedef 원래의 자료형 내가 정의한 자료형;

이렇게 하면 변수를 원하는 대로 정의해줄 수 있습니다.

아래와 같이요.

typedef unsigned int u_int;u_int val=30;

 

typedef struct 구조체명{

구조체 정의부

} 정의할 구조체명;

 

그러면 선언에서 struct를 빼고

 

정의한 구조체명 구조체변수

 

이렇게 조금 더 편하게 쓸 수도 있습니다.

 

아래의 코드는 바로 위의 코드를 typedef를 사용해서 바꾼 코드랍니다.

 

 

#include <stdio.h>
typedef struct User {
        char name[30];
        int age;
        char phone[30];
} _User;
int main() {
        _User user = { "REAKWON",2018,"010-????-????" };
        printf("이름:%s\n", user.name);
        printf("나이:%d\n", user.age);
        printf("전화번호:%s\n", user.phone);
}

 

구조체 크기

구조체의 크기를 한번 알아보도록 할까요?

아까와 같은 구조체를 한번 변경해서 보도록하겠습니다.

struct User { 
    char name[10]; 
    int age; 
    char phone[10]; 
}

char자료형은 1바이트이고 10개의 배열을 가졌으니, 10바이트

int자료형은 4바이트입니다. 

또 char배열 10개로 10바이트해서 구조체의 크기는 총 24바이트겠네요.

 

코드로 확인해보도록 하지요.

 

#include <stdio.h>

struct User {
    char name[10];
    int age;
    char phone[10];
};

int main() {
    printf("User 구조체 사이즈:%d\n",sizeof(struct User));
}

 

 

 

 

??????

제가 틀렸군요. 28바이트가 나오네요.

 

왜일까요??

구조체의 사이즈는 그 구조체 멤버중에서 가장 큰 자료형에 따라 결정이 됩니다. 가장 큰 자료형은 int로 4바이트가 되죠? 그래서 4의 배수로 크기가 결정됩니다.

이해를 돕기 위해 그림을 첨부합니다. 

 

 

사각형 한 칸의 크기는 1바이트를 나타냅니다. name과 phone은 10바이트를 차지할 것 같지만, char형은 1바이트이고, int형인 age는 4바이트니까 큰 자료형에 의해서 4의 배수로 메모리가 할당이 됩니다.

name과 같이 12바이트를 맞추기 위해 여분의 바이트를 붙여주는 것을 우리는 패딩(padding)이라고 합니다.

 

아~ 그래서 28바이트가 나오는 구나. 

그렇다면 메모리가 낭비될텐데 조금 더 아낄수 있는 방법은 없을까요?

 

우리는 변수명을 단지 바꾸어 주어 이런 메모리 낭비를 막을 수 있습니다. 이렇게요.

 

#include <stdio.h>

struct User {
    char name[10];
    char phone[10];
    int age;
};

int main() {
    printf("User 구조체 사이즈:%d\n",sizeof(struct User));
}

 

이때의 메모리를 보면 아래의 그림과 같습니다.

 

이렇게 4바이트를 아낄 수 있지요.

 

왜 이렇게 메모리를 큰 자료형에 맞출까요?? 네트워크 통신에서 관련이 있습니다. 우리는 아직 그 크기에 그렇게 신경써도 되지 않아도 됩니다.

 

이렇게 구조체가 무엇인지, 어떻게 정의하는지 알아보았는데요. 다음 시간에는 구조체에 대해서 조금 더 세세하게 알아보고 구조체 포인터에 대해서도 이야기해보도록 하겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,