[TCPL] C++ 포인터, 배열, 구조체

The C++ Programming Language

5장 포인터, 배열, 구조체

5.1 포인터

포인터는 메모리 주소를 담고 있는 타입이다. 예를 들어 어떤 타입 X가 있다고 하면 X*는 ‘X에 대한 포인터’이다. X*에는 X타입의 객체가 저장된 메모리의 주소를 가지고 있다.

    int x = 123;     // x의 값은 123 이다.

    int *p = &x;    // 포인터 p는 변수의 x의 주소를 가진다.(변수 x를 가리킨다)

정리 해보면

  • x == 123 값
  • &x == x의 메모리 주소
  • p == x의 메모리 주소
  • *p == 123 p를 역참조 한값 123

다음 몇 가지를 분석 해 보자.

  • int*p;        //int에 대한 포인터
  • char **p;    //char에 대한 포인터의 포인터 (포인터 역시 메모리를 차지한다)
  • int*p[15];    //int에 대한 포인터가 15개 모인 배열 (배열의 포인터가 아니다!)
  • int(*p)(char*);    //int를 반환하고 char* 인자를 취하는 함수에 대한 포인터
  • int*p(char*);    //int에 대한 포인터를 반환하고 char*인자를 취하는 함수

포인터로 할 수 있는 가장 대표적인 일에 역참조(간접참조)가 있다. 역참조 연산자는 *이다.

5.1.1 영

영(0)이란 것은 타입으로 따지면 int에 속하지만 지정된 규칙에 의해 어떤 수치 타입의 상수로도 쓰인다.

0의 값을 가진 포인터는 아무것도 가리키지 않는 NULL포인터가 된다.

5.2 배열

배열이란 어떤 타입의 원소를 여러 개 가지고 있는 것을 말한다. 배열은 다음과 같이 선언한다.

타입 배열이름[원소갯수]        int x[10];     //int형 배열 10개가 선언 되었다.

int x[10]로 선언 했을때 각 원소의 이름은 x[0],x[1]….. 이렇게 형성된다.

배열을 선언 할 때 [원소갯수]는 반드시 상수 표현식이여야 한다. 경계가 변하는 배열이 필요한 경우에는 vector를 사용 해야 한다.

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream> #include <vector> using namespace std; main( ) { int x=2; // x는 변수다. int y[x]; //에러 : 배열의 크기는 상수형태 이어야 한다. vector<int> z(x); //vector를 사용하면 변수로 크기를 정할 수 있다. } </TEXTAREA>

다차원 배열은 배열들의 배열이라 생각 하고 다음과 같이 나타낸다.

<TEXTAREA class=c name=code row=”10″ col=”60″>int x[5][4]; //int형 배열 x는 5개의 원소들이 각자 4개의 원소를 가지는 배열이다. </TEXTAREA> 

5.2.1 배열 초기치

배열을 선언할 때 값들의 리스트를 함께 주면 초기화가 가능하다.

<TEXTAREA class=c name=code row=”10″ col=”60″>int b[4] = {1,2,3,4,}; int c[] = {5,6,7,8,}; //4개의 원소가 있는 배열 </TEXTAREA>

배열의 크기를 정하지 않고 선언 하였지만 초기치 리스트가 주어 졌다면 초기치의 개수로 크기가 정해 진다.

배열을 선언할 때는 초기치의 개수가 배열 크기를 초과해선 안 된다. 초기치의 개수가 배열의 크기에 못 미치는 경우 나머지 배열 원소에 0이 들어간다.

<TEXTAREA class=c name=code row=”10″ col=”60″>int a[5] = {1,2,3}; // 1,2,3,0,0 을 원소로 하는 배열 </TEXTAREA>

초기화에 썼던 방식으로 그대로 배열에 대입 할 수는 없다.

<TEXTAREA class=c name=code row=”10″ col=”60″>void main( ) { int a[3]; a = {1,2,3,}; //에러! a[0] = 1; a[1] = 2; a[2] = 3; //원소 하나씩 대입한다. } </TEXTAREA>

5.2.2 문자열 리터럴

문자열 리터럴은 큰 따옴표로 둘러싼 연속적인 문자들을 뜻한다.

문자열 리터럴의 크기는 눈으로 보이는 개수보다 하나가 더 큰데 이것은 문자열의 끝을 나타내는 Null문자인 ‘\0‘이 붙어 있는 것이다. (sizeof를 이용 하면 쉽게 알수 있다. sizeof(“nclab”)==6;

문자열 리터럴의 타입을 정확하게 말하면 const char의 배열이라고 할 수 있다. 즉 “nclab”의 타입은 const char[6]이 된다.

문자열 리터럴은 char*에 대입 할 수 있다. 그러나 char*를 이용해서 리터럴의 내용을 바꾸는 것은 잘못이다. 내용을 바꿀수 있는 문자열을 쓰고 싶으면 문자 배열을 끌어와야 한다.

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream.h> using namespace std; const char* caution_message( ); int main() { const char a[] = “apple”; //6개의 상수 char타입(배열)에 대입 char* b = “apple”; //리터럴을 char* 에 대입 했다 char c[] = “apple”; //6개의 char타입(배열)에 대입 했다 const char* d = “apple”; //상수를 가리키는 포인터 char* e = (char*)d; //d와 같은 값을 가리키는 포인터 //a[0] = ‘b’; //Error : 상수를 바꾸려고 했다. b[0] = ‘b’; //Error : 상수를 바꾸려고 했다. c[0] = ‘b’; //OK : 배열의 첫번째 원소를 바꿈 //d[0] = ‘b’; //Error : 상수를 바꾸려고 했다. e[0] = ‘b’; //OK : e가 가리키는 값의 첫번째 원소를 바꿈 cout<<“a : “<<a<<endl<<“b : “<<b<<endl<<“c : “<<c<<endl; cout<<“d : “<<d<<endl<<“e : “<<e<<endl; /*둘다 bpple 이 출력 된다. e를 이용해 d가 가리키는 값이바뀜*/ cout<<“&d : “<<&d<<endl<<“&e : “<<&e<<endl; /*포인터의 주소값을 본다. d와 e는 서로 다르다.*/ printf(“%x, %x”, &(*d), &(*e)); //d와 e가 가리키는 값의 주소를 본다. (같다.) const char* x = “banana”; const char* y = “banana”; cout<<(x==y); //False : 포인터가 지고 있는 주소를 비교 했다. cout<<(*x==*y); //True : 포인터가 가리키는 값을 비교 했다. out<<“\n”<<caution_message( ); return 0; } /* 문자열 리터럴은 함수에서 반환하더라도 안전하다. */ const char* caution_message( ) { return “Caution!!”; } </TEXTAREA>

빈 문자열은 큰따옴표 두 개를 바로 붙여서(“”)만든다. (const char[1]타입이다.)

글자가 아닌 문자를 나타낼때는 \표현을 쓴다. 줄바꿈을 할때는 \n 을 사용 한다( 진짜 줄바꿈을 하는 것은 소용 없다.)

긴 문자열은 공백을 두어 구분 할 수 있다. 텍스터를 읽기 편하게 하기 위해서이고 다른 의미는 없다.

“good”

“apple”

컴파일러는 인접한 두 문자를 합쳐 버린다. 결국 위의 것은 “goodapple”이 된다.

0 를 이용하여 Null문자를 넣을 수도 있다.

5.3 배열에 대한 포인터

포인터와 배열의 관계는 매우 밀접하다.

배열의 이름이 곧 그 배열의 첫째 원소의 포인터이다

<TEXTAREA class=c name=code row=”10″ col=”60″> int a[ ] = {1,2,3,4}; int* p1 = a; //첫째 원소 (1)에 대한 포인터 int* p2 = &v[0]; //첫째 원소 (1)에 대한 포인터 int* p3 = &v[4]; //마지막 원소 바로 다음에 대한 포인터 </TEXTAREA>

이것을 그림으로 이렇게 나타낼 수 있다.

1

2

3

4

 

포인터가 가리키는 위치는 배열의 끝 바로 다음 위치까지 문제 없지만 실제로 어떤 원소를 가리키고 있는 것은 아니기 때문에, 그 포인터를 이용하여 데이터를 읽거나 쓰는 것은 불가능하다.

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream> #include <string.h> using namespace std; void main( ) { char a[]= “apple”; char* b = a; //배열 a의 첫번째 주소값을 넘겨 받는다. cout<<a<<endl; cout<<b<<endl; //둘다 apple출력 cout<<strlen(a)<<endl; cout<<strlen(b)<<endl; //strlen은 0이 나올때까지 카운트 하므로 5 b = a; //OK a = b; //Error : 배열에 대입할수 없다. } </TEXTAREA>

호출된 함수에서는 배열의 정보가 없어진다. 즉, 배열의 크기 정보가 없어진다. 그래서 문자열의 끝 표시 문자인 0을 기준으로 동작 한다 strlen( ) 은 0 이 나올 때 까지의 문자의 수를 세어 값을 반환 하는 함수이다.

5.3.1 배열 순회하기

배열의 접근 수단에는 크게 두 가지가 있다.

첫째로 배열에 대한 포인터에 색인번호를 더하는 방법.

<TEXTAREA class=c name=code row=”10″ col=”60″> void fi (char a[]) { for (int i = 0; a[i]!=0; i++) // 색인번호를 증가 시킨다. /*a[i]를 사용*/ } </TEXTAREA>

둘째로 원소자체에 대한 포인터를 쓰는 방법.

<TEXTAREA class=c name=code row=”10″ col=”60″> void fp (char a[]) { for (char*i = v; *i!=0; i++) // 포인터 값을 증가 시킨다. /*a[i]를 사용*/ } </TEXTAREA>

X* 타입의 포인터 p에 산술 연산을 적용하면 p는 X타입 객체가 모인 배열의 원소를 가리킨다. 즉, X타입의 크기 만큼(sizeof(X)만큼) 연산이 이루어져 다른 원소를 정확히 가리킨다.

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream> using namespace std; int main( ) { int a[4]; short int b[4]; cout<<sizeof(int)<<endl; //int 의 size는 4 cout<<sizeof(short int)<<endl; //short int의 size는 2 cout<<&a[0]<<” “<<&a[1]<<” “<<&a[0]+1<<endl; //a[0], a[1]은 4만큼 차이남 /*a[0] +1 은 a[1]와 주소값이 같다*/ cout<<&b[0]<<” “<<&b[1]<<” “<<&b[0]+1<<endl; //a[0], a[1]은 4만큼 차이남 /*b[0] +1 은 b[1]와 주소값이 같다*/ } </TEXTAREA>

5.4 상수

값을 수정 할 수 없는 객체임을 지정해 줄 때 const라는 키워드를 사용 한다. const 키워드는 보통 선언문 앞에 붙여 주는데 상수엔 대입 연산이 허용 되지 않으므로 선언과 동시에 초기화해 주어야 한다.

const 키워드를 포인터 선언문 앞에 붙일 경우, 상수가 되는 쪽은 포인터가 아니라 실제 객체이다. 실제 객체가 아닌 포인터 자체를 상수로 선언 하려면 선언자 연산자로 *const를 써야 하며 그냥 *는 안 된다.

<TEXTAREA class=c name=code row=”10″ col=”60″> char a[] = “alpha”; char* b = “apple”; const char* pa = a; //상수 문자에 대한 포인터 pa[3] = ‘z’; //Error : pa는 상수를 가리킨다. pa = b; //OK : 상수를 가리키지만 포인터 자체는 상수가 아니다. char *const pb = a; //문자를 가리키는 포인터 자체가 상수이다. pb[3] = ‘z’; //OK : 문자는 상수가 아니므로 바꿀 수 있다. pb = b; //Error : 포인터 자체가 상수이므로 바꿀 수 없다. const char *const pc = a; //상수 문자를 가리키는 상수 포인터 pc[3] = ‘z’; //Error : 상수를 가리킨다. pc = b; //Error : 포인터 자체도 상수다. </TEXTAREA>

이렇게 어떤 포인터를 상수로 만들고 싶을 때 사용 하는 것이 *const이다.

<TEXTAREA class=c name=code row=”10″ col=”60″> char *const a; //char 에 대한 상수 포인터. char const *a; //const char에 대한 포인터 const char *a; //const char에 대한 포인터. </TEXTAREA>

(*를 기준으로 const가 왼쪽이면 가리키는 대상이 상수, 오른쪽이면 포인터 자체가 상수)

상수에 대한 포인터에는 언제든지 주소값을 대입할 수 있다. 그러나 아무 제약이 없는 포인터에 상수의 주소를 대입하는 것은 안 된다. (객체의 값이 변경될 수 있다는 여지를 만들기 때문에)

<TEXTAREA class=c name=code row=”10″ col=”60″> void main( ) { int a = 1; const int b = 2; const int* p1 = &a; //OK const int* p2 = &b; //OK : 상수를 가리키는 포인터가 상수를 가리킨다. int* p3 = &b; //Error : 이것이 가능하다면 *p3=3 이 가능해진다 } </TEXTAREA>

5.5 참조자

참조자(reference)는 객체에 대한 다른 이름. 참조자는 함수의 인자와 반환값을 정의하는 데 일반적으로 쓰인다. X에 대한 참조자를 나타내는 꼴은 X&이다.

어떤 참조자를 객체의 이름으로 만들어 주려면 선언과 동시에 초기화 해주어야 한다.

<TEXTAREA class=c name=code row=”10″ col=”60″>void main( ) { int a = 1; int&b = a; //이제 b는 a와 똑같은 1에 붙은 이름이다. int c = b; //c = 1 b = 2; //a = 2 } </TEXTAREA>

참조자의 초기화는 참조자에 대입하는 것과 비교할 때 몇 가지 상당히 다른 면모를 가지고 있다. 참조자에는 보통의 연산자가 기대하는 대로 작동하지 않는다.

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream> using namespace std; int main( ) { int a = 1; int&b = a; //b는 a의 참조자 int* c = &b; //c는 b의 데이터를 가리키는 포인터 cout<<a<<endl; //a의 값 1 cout<<b<<endl; //a를 참조하는 b 값 1 b++; //b에 더하는 것이 아니라 결국 a의 값이 더해짐 cout<<a<<endl; //a가 1만큼 증가 했음 값 2 cout<<b<<endl; //b는 a를 참조하므로 값은 2 cout<<&a<<endl; //a의 주소값 cout<<&b<<endl; //a를 참조 하는 b의 값은 결국 a와 같다. cout<<c<<endl; //결국 참조자가 아닌 a의 주소값을 받아서 a와 같다. } </TEXTAREA>

int* c = &b; 에서 &b는 참조자의 주소값이 아닌 b가 나타내는 객체 a의 주소값을 받아 온다. 결국 참조자의 포인터를 받아 오는 것은 불가능하다. 뿐만아니라. 참조자의 배열을 정의 하는 것도 안된다. 이런 의미를 가지고 정리해 볼 때, 참조자는 객체가 아니다.

참조자의 초기화는 초기치가 좌변값인 경우엔 지극히 평범하게 이루어진다. 아무런 제약이 없는 int&에 대한 초기치는 int 타입의 좌변값이어야 한다.

그런데 const int&에 대한 초기치는 좌변값일 필요가 없으며 int타입이 아니어도 된다. 이런 경우엔 다음과 같은 일이 진행된다.

  1. 우선 int타입으로의 암시적 타입 변환이 적절한 판단에 따라 일어나고
  2. 그 다음, 그 결과값이 int타입 임시 변수에 저장되며
  3. 마지막으로 그 임시 변수가 초기치로 사용 된다.

<TEXTAREA class=c name=code row=”10″ col=”60″> int&a = 1; //좌변값이 필요하다! const int&b = 1; //OK : 이것은 아래와 같은 과정으로 해석할 수 있다. int temp = int(1); //우변의 값을 가지는 임시 변수를 만들고 const int&b = temp; //그 임시 변수를 b의 초기치로 이용한다. </TEXTAREA>

어떤 참조자의 초기치를 담기 위해 생성된 임시 객체는 그 참조자의 유효 범위가 끝날 때까지 없어지지 않는다. 참조자는 어떤 객체를 받아 그 정보를 바꾸는 함수의 인자를 지정하는 데 사용할 수 있다.

<TEXTAREA class=c name=code row=”10″ col=”60″>void increment1(int&a) {a++;} //참조자 a로 받는 값의 또다른 이름이 된다. int increment2(int a) {return a+1;} //새로운 변수에 값을 받아 증가시킨 다음 반환. void increment3(int*a) {(*a)++;} //받는값의 주소를 포인터로 가리키고 증가. int main() { int b = 1; increment1(b); //b = 2 b = increment2(b); //b = 3 increment3(&b); //b = 4 } </TEXTAREA>


5.6 void에 대한 포인터

  1. 가리키는 객체의 타입에 상관 없이 모든 포인터는 void*타입 변수에 대입 할 수 있다.
  2. void*는 또 다른 void*에 대입 할 수 있다.
  3. void*끼리 같은지 안 같은지 상등 비교를 할 수 있다.
  4. void*는 다른 타입으로 변환할 수 있다.
  5. 1~4번을 제외한 다른 연산의 경우에는 안전하지 않다.

<TEXTAREA class=c name=code row=”10″ col=”60″>void f(int*a) { void*pa = a; //OK : int*를 void*에 대입할 때 암시적인 변환이 일어난다 *pa; //Error : void*는 역참조할 수 없다. pv++; //Error : void*는 증가시킬 수 없다.(가리키는 객체의 크기를 모른다) int*pb = static_cast<int*>(pa); //int*로 명시적 변환이 일어난다. double*x = pa; //Error double*y = a; //Error double*z = static_cast<double*>(pa); //컴파일은 되지만 안전하지 않음. } </TEXTAREA>

void* 의 주된 용도는 두 군데이다.

  1. 포인터가 가리키는 객체의 타입을 예측할 수 없는 함수에 대해 지정하는 인자이다.
  2. 타입이 지정되지 않은 객체를 반환하고 싶을 때 지정하는 반환 타입이다.

5.7 구조체

구조체(struct)는 거의 모든 타입의 원소들이 임의로 구성된 것이다.

<TEXTAREA class=c name=code row=”10″ col=”60″> struct profile { char* id; //아이디 char* password; //비밀번호 char* name; //이름 int number; //번호 char code[2]; //코드 (‘a’ ‘b’) }; </TEXTAREA>

위의 코드는 profile란 이름의 타입이다. 중괄호 안에 선언되어 있는 것 들을 “맴버”라 한다. 중괄호 } 뒤에 세미콜론이 있음에 유의하자.

profile 타입 변수의 선언방법은 다른 변수를 선언하는 방법과 같고, 각각 구조체 맴버는 .(점) 연산자를 써서 접근한다.

<TEXTAREA class=c name=code row=”10″ col=”60″> int main( ) { profile khj; khj.id = “talsu”; khj.password = “gudwh”; khj.name = “Hyung jo”; khj.number = 20033000; } </TEXTAREA>

구조체 타입의 변수를 초기화할 때도 역시 배열 초기화에 사용한 표기 방법을 사용할 수 있다.

<TEXTAREA class=c name=code row=”10″ col=”60″> address khj = { “talsu”, “gudwh”, “Hyoung jo”, 2033000, {‘a’,’b’}}; </TEXTAREA>

여기서 khj.code는 “ab“로 초기화하지 않고 {‘a’,’b’}로 초기화 했음에 주목하자. 문자열에는 마지막에 \0이 들어가므로 선언한 크기보다 더 크기 때문에 맞지 않다.

구조체 타입의 객체를 포인터로 접근할 경우에는 -> (구조체 포인터 역참조) 연산자를 쓰는 것이 보통이다.

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream> using namespace::std; struct profile //profile이라는 구조체를 만듬 { char* id; char* password; }; void print_pro(profile* p) //구조체 포인터 역참조 -> 를 이용해 profile의 원소를 출력 { cout<<p->id<<endl<<p->password<<endl; } int main( ) { profile khj = { “talsu”, “1234” }; cout<<khj.id<<endl<<khj.password<<endl; //.(점) 연산자로 접근여 출력 print_pro(&khj); //포인터를 이용해 출력 } </TEXTAREA>

p가 포인터일때 p->m(*p)m과 같다.

구조체 타입의 객체는 대입 연산, 함수 인자로서 전달, 함수에서 반환하기가 모두 가능하다.

구조체 타입의 이름을 쓸 수 있는 시점은 그 이름이 등장한 이후이면 아무 곳이든 상관없다. 선언이 완전하게 끝나지 않아도 된다는 말이다. 하지만 구조체 타입의 객체를 선언하는 일은 구조체 타입의 선언이 완전히 끝난 다음에나 가능하다. 즉 그 이름으로 인해 구조체의 크기를 알아야 하는 상황이 만들어지면 안 된다는 사실을 주의 해야 한다.

<TEXTAREA class=c name=code row=”10″ col=”60″> struct abc { abc* p; //OK abc q; //Error : abc의 크기를 결정할 수 없는 상태 }; </TEXTAREA>

두 개 이상의 구조체 타입이 서로를 참조할 수 있게 하려면 한쪽 구조체 타입의 이름을 미리 선언해 두면 된다.

<TEXTAREA class=c name=code row=”10″ col=”60″> struct a; //실제 정의는 뒤에 있다 struct b { b* name; a* id; //정의 되기도 전에 사용 할 수 있다 }; struct a { //정의 b* profile; }; </TEXTAREA>

구조체는 클래스의 일종이다.

한 유효 범위 안에 똑같은 이름의 구조체와 구조체가 아닌 것을 정의해도 아무런 문제가 없다.

<TEXTAREA class=c name=code row=”10″ col=”60″> struct profile {/*……*/}; int profile(char* name, struct profile* id); //profile앞에 struct 접두어 사용 </TEXTAREA>

이런 경우 그냥 이름을 쓰면(profile이라고) 구조체가 아닌 것을 지칭하지만 앞에 struct란 말을 접두어로 붙여주면 구조체를 지칭하므로 같은 이름을 쓸 수 있다. (그러나 이렇게 할 필요가 없도록 이름을 중복하지 않는 것이 최선이다.)

5.7.1 타입 동등 관계

똑같은 맴버를 가졌다 하더라도 이름이 다른 두 구조체는 서로 다른 타입이다 그러므로 상호간의 대입이나 연산은 불가능 하다.

<TEXTAREA class=c name=code row=”10″ col=”60″> struct a { int x; }; struct b { int x; }; //멤버는 같지만 이름이 다르므로 다른 타입이다. int main ( ) { a num; b num2 = num; //Error : 타입 불일치 } </TEXTAREA>

모든 구조체는 프로그램 안에서 자신만의 정의를 가져야 한다.

5.8 바른 프로그래밍을 위한 고수의 조언

  1. 허용된 것 이외의 포인터 연산은 금물.
  2. 배열의 경계를 넘어선 데이터 기록은 하지 않도록 각별한 주의를 기울일 것.
  3. NULL보다 0을 사용할 것.
  4. 기본제공 (C 방식) 배열을 쓰려거든 string을 사용할 것.
  5. 0으로 끝나는 문자 배열을 쓰려거든 string을 사용할 것.
  6. 상수 제약이 없는 참조자 인자를 사용하는 일은 최대한 줄일 것.
  7. 하부 수준의 코드 이외에는 void*를 피할 것.
  8. 코드 중 의미를 부여해야 하는 곳에 리터럴(매직 넘버)을 쓰지 말자. 그대신 기호 형태의 상수를 정의 하여 사용하는 것을 습관화할 것.

5.9 연습문제

1. 두 정수를 스왑(swap)하는 함수를 하나 작성해 보자. 인자 타입으로는 int*를 사용하자. 다 만들었으면 이번엔 인자 타입으로 int&를 사용하는 swap 함수를 만들어 보자

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream> using namespace::std; void swap1 (int* a, int* b) //int*를 이용한 swap { int temp = *a; //임시 변수에 a의 값 저장 *a = *b; //a 에 b의 값 저장 *b = temp; //b에 임시 변수 값 저장 } void swap2 (int&a, int&b) //int&를 이용한 swap { int temp = a; a = b; b = temp; } int main() { int x = 1; int y = 2; cout<<x<<” “<<y<<endl; swap1(&x,&y); cout<<x<<” “<<y<<endl; swap2(x,y); cout<<x<<” “<<y<<endl; } </TEXTAREA>

2. 1년에 속한 각 달의 이름과 각 달의 날수를 담은 테이블을 하나 정의하고, 이 테이블을 출력 해보자. 달 이름과 날수를 묶은 구조체의 배열을 사용하자.

<TEXTAREA class=c name=code row=”10″ col=”60″>#include <iostream> using namespace::std; struct md // 달과 날짜를 담은 구조체 { char* mounth; // 달 int lastday; // 날짜 }; int main() { md m[12] ={ // 구조체 자체를 배열로 선언 한 다음 초기화 {“January”,31},{“February”,30}, {“March”,31} ,{“April”,30} , {“May”,31} ,{“June”,30} , {“July”,31} ,{“August”,31} , {“September”,30} ,{“October”,31} , {“November”,30} ,{“December”,31} }; for (int i = 0; i<12; ++i) { cout<<m[i].mounth<<” : “<<m[i].lastday<<endl; // 출력 } }</TEXTAREA>

hk67.pdf

One thought on “[TCPL] C++ 포인터, 배열, 구조체

  1. 호식자

    잘 봤습니다. Textarea 태그가 그대로 보이네요.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url=""> 

This site uses Akismet to reduce spam. Learn how your comment data is processed.