반응형
728x90
반응형

-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#include <iostream>
#include <string>
#include <cstring>
 
namespace mystd
{
    using std::cout;
    using std::cin;
    using std::endl;
    using std::printf;
    using std::ostream;
    using std::istream;
 
    class string
    {
    private:
        char * str;
        int str_len;
    public:
        string();
        string(const string& r_string);
        string(const char * str);
        ~string();
 
        string& operator=(const string& r_string);
        string& operator=(const char * str);
        const string operator+(const char * str);
        const string operator+(const string& r_string);
        string& operator+=(const char * str);
        string& operator+=(const string& r_string);
        bool operator== (const string& r_string);
        bool operator== (const char * str);
 
        friend ostream& operator<<(ostream& os, const string& r_string);
        friend const string operator+(const char * str, const string& r_string);
        friend char * operator+=(char * str, const string& r_string);
        friend bool operator==(const char * str, const string& r_string);
    };
    istream& operator>> (istream& is, string& r_string);
    ostream& operator<< (ostream& os, const string& r_string);
    const string operator+(const char * str, const string& r_string);
    char * operator+=(char * str, const string& r_string);
    bool operator==(const char * str, const string& r_string);
};
 
using namespace mystd;
 
int main(void)
{
    string str1 = "I like "// 복사 생성자
    string str2 = "string class"// 복사 생성자
    string str3 = str1 + str2; // 복사 생성자, + 연산자 오버로딩
 
    cout<<"------ str1 ------"<<endl;
    cout<<str1<<endl// << 연산자 오버로딩 전역
    cout<<"------ str2 ------"<<endl;
    cout<<str2<<endl// << 연산자 오버로딩 전역
    cout<<"------ str3 ------"<<endl;
    cout<<str3<<endl// << 연산자 오버로딩 전역
 
    string str4;
    str4 = "HELLO";
    cout<<"------ str4 ------"<<endl;
    cout<<str4<<endl;
 
    string str5;
    str5 = str3 + str4 ;
    cout<<"------ str5 ------"<<endl;
    cout<<str5<<endl;
 
    string str6;
    str6 = str4 + " AAA" ;
    cout<<"------ str6 ------"<<endl;
    cout<<str6<<endl;
 
    string str7;
    str7 =  "AAA " + str4 ;
    cout<<"------ str7 ------"<<endl;
    cout<<str7<<endl;
 
    string str8 = "asdf";
    string str9 = "fdsa";
 
    str8 += str9;
    str9 += "asdf";
    cout<<"------ str8 ------"<<endl;
    cout<<str8<<endl;
    cout<<"------ str9 ------"<<endl;
    cout<<str9<<endl;
 
    cout<<"------ str11 ------"<<endl;
    string str10="222222222";
    char str11[30= "fdjskalfjklsaf";
    str11+=str10;
    printf("%s\n",str11+=str10);
 
    string str12 = "ZXC1";
    string str13 = "ZXC";
    if(str12 == str13)
        cout<<"동일한 문자열이다1"<<endl;
    else
        cout<<"동일하지 않은 문자열이다1"<<endl;
 
    char str14[10= "ZXC";
    if(str12 == str14)
        cout<<"동일한 문자열이다2"<<endl;
    else
        cout<<"동일하지 않은 문자열이다2"<<endl;
 
    if(str14 == str12)
        cout<<"동일한 문자열이다3"<<endl;
    else
        cout<<"동일하지 않은 문자열이다3"<<endl;
 
    cout<<"문자열 입력 : ";
    cin>>str4; // >> 연산자 오버로딩 전역
    cout<<"입력한 문자열 : "<<str4<<endl// << 연산자 오버로딩 전역
 
    return 0;
}
 
 
mystd::string::string() : str(NULL), str_len(0) { }
mystd::string::string(const string& r_string) : str_len(r_string.str_len)
{
    this->str = new char[r_string.str_len+1];
    strcpy(this->str, r_string.str);
}
mystd::string::string(const char * str) : str_len(strlen(str))
{
    this->str = new char[strlen(str)+1];
    strcpy(this->str, str);
}
mystd::string::~string()
{
    delete []this->str;
}
 
string& mystd::string::operator=(const string& r_string)
{
    this->str_len = r_string.str_len;
 
    if(this->str == NULL)
    {
        this->str = new char[this->str_len+1];
    }
    else
    {
        delete []this->str;
        this->str = new char[this->str_len+1];
    }
    strcpy(this->str, r_string.str);
    return *this;
}
 
string& mystd::string::operator=(const char * str)
{
    this->str_len = strlen(str);
 
    if(this->str == NULL)
    {
        this->str = new char[this->str_len+1];
    }
    else
    {
        delete []this->str;
        this->str = new char[this->str_len+1];
    }
 
    strcpy(this->str, str);
    return *this;
}
const string mystd::string::operator+(const char * str)
{
    char * tmp_str = new char[this->str_len + strlen(str) + 1];
    string tmp;
 
    strcpy(tmp_str, this->str);
    strcpy(tmp_str + this->str_len, str);
 
    tmp = tmp_str; // 대입 연산자 오버로딩
    delete []tmp_str;
    return tmp;
}
const string mystd::string::operator+(const string& r_string)
{
    char * tmp_str = new char[this->str_len + r_string.str_len + 1];
    string tmp;
 
    strcpy(tmp_str, this->str);
    strcpy(tmp_str + this->str_len, r_string.str);
 
    tmp = tmp_str; // 대입 연산자 오버로딩
    delete []tmp_str;
    return tmp;
}
string& mystd::string::operator+=(const char * str)
{
    int tmp_strlen = this->str_len + strlen(str);
    char * tmp_str = new char[tmp_strlen + 1];
 
    strcpy(tmp_str, this->str);
    strcpy(tmp_str+this->str_len, str);
 
    if(this->str == NULL)
        this->str = new char[tmp_strlen + 1];
    else
    {
        delete []this->str;
        this->str = new char[tmp_strlen + 1];
    }
    strcpy(this->str, tmp_str);
    this->str_len = tmp_strlen;
 
    delete []tmp_str;
    return *this;
}
string& mystd::string::operator+=(const string& r_string)
{
    int tmp_strlen = this->str_len + r_string.str_len;
    char * tmp_str = new char[tmp_strlen + 1];
 
    strcpy(tmp_str, this->str);
    strcpy(tmp_str+this->str_len, r_string.str);
 
    if(this->str == NULL)
        this->str = new char[tmp_strlen + 1];
    else
    {
        delete []this->str;
        this->str = new char[tmp_strlen + 1];
    }
    strcpy(this->str, tmp_str);
    this->str_len = tmp_strlen;
 
    delete []tmp_str;
    return *this;
}
bool mystd::string::operator== (const string& r_string)
{
    if(!strcmp(this->str, r_string.str))
        return true;
    else
        return false;
}
bool mystd::string::operator== (const char * str)
{
    if(!strcmp(this->str, str))
        return true;
    else
        return false;
}
 
istream& mystd::operator>> (istream& is, string& r_string)
{
    char str[100];
    is>>str;
    r_string = string(str);
    return is;
}
ostream& mystd::operator<< (ostream& os, const string& r_string)
{
    os<<r_string.str;
    return os;
}
const string mystd::operator+(const char * str, const string& r_string)
{
    char * tmp_str = new char[r_string.str_len + strlen(str) + 1];
    string tmp;
 
    strcpy(tmp_str, str);
    strcpy(tmp_str + strlen(str), r_string.str);
 
    tmp = tmp_str; // 대입 연산자 오버로딩
    delete []tmp_str;
    return tmp;
}
char * mystd::operator+=(char * str, const string& r_string)
{
    int i = 0;
    int tmp_strlen = strlen(str) + r_string.str_len;
    char * tmp_str = new char[tmp_strlen + 1];
 
    strcpy(tmp_str, str);
    strcpy(tmp_str + strlen(str), r_string.str);
 
    while(str[i++]!=0); // str의 사이즈를 구한다.(i-2)
    strncpy(str,tmp_str,i-2); // str의 기존 사이즈만큼 복사한다. 짤리겠지.
    delete []tmp_str;
    return str;
}
bool mystd::operator==(const char * str, const string& r_string)
{
    if(!strcmp(str, r_string.str))
        return true;
    else
        return false;
}
cs

-

 

직접 구현해보았으나 설명 및 풀이를 시간이 나면 진행을 해보겠다.

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

24 예외 처리  (0) 2020.10.15
23 템플릿  (0) 2020.10.13
21 연산자 오버로딩  (0) 2020.09.23
20 다중 상속  (0) 2020.09.22
19 멤버함수 가상함수의 동작 원리  (0) 2020.09.15
728x90
반응형

구조체

  • 구조체

구조체란 하나 이상의 변수를 묶어서 새로운 자료형을 정의하는 도구이다. 예를 들어, 기존의 자료형과 별개로 3바이트짜리 자료형을 만들어내는 것, 이러한 예처럼 새로운 자료형을 기본 제공 자료형으로 추가하는 것은 불가능 하지만 기존의 자료형들을 이용하여 생성하는 변수들을 묶어서 새로운 자료형을 정의하는 것은 가능하다.

다음은 이름과 학번을 저장하는 새로운 자료형을 정의한 예를 나타낸 것이다.

학생을 관리하는 시스템에서는 학번과 이름은 함께 처리되어야 의미가 있는 자료들이다. 그렇기 때문에 위와 같이 학생의 이름을 나타내는 name 변수와 해당 이름의 학생의 번호를 의미하는 stdNum을 한데 묶어서 관리할 수 있도록 새로운 자료형을 정의한 것이다.

 

  • 구조체의 정의

구조체의 정의는 다음과 같다

왼쪽은 구조체의 정의를 나타낸 것이다. 오른쪽의 예는 구조체를 정의한 예인데, 20 바이트 문자형 배열과, 4바이트 정수를 저장할 수 있는 변수들로 구성되어 있는 PersonalInfo라는 자료형을 하나 새로 만든 것이다. PerosonalInfo라는 자료형은 int, float, double 등처럼 자료형의 이름이 될 수 있다. 이 이름을 통해서 사용자 정의 자료형 변수를 만들게 되는 것이다.

  • 구조체 변수의 선언과 접근

구조체 변수의 선언

 

정의한 사용자 정의 자료형(구조체)을 통해 변수를 선언하는 방법은 왼쪽과 같다. 그리고 오른쪽 13, 14번 라인은 구조체 변수를 선언한 예를 나타내고 있다.

 

구조체 변수를 통한 멤버 변수 직접 접근과 간접 접근

선언된 구조체 변수를 통해 멤버 변수에 접근할 수 있다. 아니, 해야만 한다. 그러기 위해 만든 구조체이기 때문이다.

왼쪽과 같이 구조체 변수 이름과 ‘ . ‘ (직접 접근 연산자)와 멤버 변수의 이름을 명시함으로써 구조체의 멤버 변수에 직접 접근하여 값을 넣거나 값을 출력 혹은 수정까지 할 수 있다.

위의 예에서는 16번 라인과 17번 라인을 통해 멤버 변수에 접근하고, 대입 및 다른 곳으로의 참조를 시키는 등의 작업이 이루어진다. 중요한 것은 구조체_변수_이름 . 구조체의 멤버 변수 형식을 갖추어 직접 접근한다는 것.

구조체(사용자 정의 자료형) 또한 일반 자료형과 같은 취급을 하기 때문에 포인터로 선언하고 주소 값을 받는 모든 행위들은 허용된다. 다음의 예가 가능하다.

위의 예에서 13번 라인에서 일반 구조체 변수를 선언하고, 15번 라인에서는 구조체 포인터 변수를 선언한다. 그리고 나서 16, 17번 라인을 통해서 직접 접근 연산자 ‘.’을 이용하여 구조체 멤버에 직접 접근을 통해서 값을 대입한다. 그리고 19번 라인에서 구조체 변수에 기존에 선언했던 일반 구조체 변수의 주소 값을 대입한다. 중요한 것은 21번 라인의 참조 방식인데, ‘->’(간접 접근 연산자)를 통해서 접근하는 형태를 볼 수 있다. 이렇듯 구조체 포인터 변수를 통해서 가리키고 있는 변수의 멤버에 접근할 때에는 간접 접근 연산자(->)를 통해서 접근한다. 이를 간접 접근 이라고 한다.

참조를 통한 직접 접근 또한 가능하다.

22번 라인과 같이 구조체 포인터가 가리키는 대상에 대해서 접근 연산이므로 직접 접근 연산이 맞다. 포인터를 이용해서 참조한 후 직접 접근 연산자를 통해 멤버에 접근할 수 있는 방법이 제시된 예이다.

 

 

  • 구조체 변수의 초기화

생성과 동시에 초기화

배열의 초기화와 매우 유사하다.

5번 라인에 해당하는 구조체 정의를 두고, 13번 라인에서 구조체 변수를 선언하면서 생성과 동시에 초기화를 진행하고 있는데 그 초기화 방식은 다음과 같다.

중괄호로 감싼 후, 초기화할 값들을 콤마 연산자를 통해 구분하며 나열하는데 구조체 정의 시 멤버 변수의 선언 순서대로 나열을 한다.

 

일반 초기화(값의 대입)

다음의 예와 같이 선언 이후에 값의 대입을 통하여 초기화를 진행할 수 있다.

  • 구조체 배열

구조체 배열 또한 일반 변수의 배열과 마찬가지로 다를 바가 없다. 다음은 이전의 예제를 조금 변형한 구조체 배열에 대한 예제이다.

먼저, 15번 라인에서 구조체 배열을 선언한다. 두 명의 학적 정보를 저장할 수 있도록 배열로 선언한다. 그런데 배열을 선언과 동시에 초기화를 이루고 있다. 이 경우 2차원 배열의 초기화와 크게 다를 바가 없다. 구조체 정의 시 나열한 멤버 변수의 순서대로 값들을 나열하고, 그러한 나열을 행 별로 다시 구분 지어 나열한다. 그리고 20번 라인에서는 선언만 이루어졌고, 22~ 26번 라인에서 대입을 통한 초기화가 이루어지고 있다. 그리고 29번 라인부터는 인덱스를 이용하여 구조체 배열의 각 요소들을 출력하는 것을 볼 수 있다.

 

  • 구조체 포인터 및 구조체 첫 멤버 변수의 주소 / 구조체 변수의 주소 / 구조체 이름

구조체 포인터 또한 일반 변수의 포인터와 다를 바가 없다. 다만 다른 부분은 아래에 제시한다.

위의 결과로 미루어 보았을 때 구조체의 첫 멤버 변수의 주소와 구조체 변수의 주소는 같지만 구조체 이름은 다른 것임을 알 수 있다. (배열 이름과는 다름)

 

l  구조체와 typedef 연산을 통한 자료형 재정의

Typedef 는 자료형을 다른 이름으로 재정의하는 연산자이다. 다음과 같은 형식으로 재정의를 진행한다.

typedef 기존_자료형 새로운_자료형_이름;

다음은 기존 자료형을 통한 자료형 재정의의 예이다.

11, 12번 라인에서 int 자료형과 char 자료형에 대해서 새로운 이름으로 재정의가 일어났다.

다음으로는 사용자 정의 자료형에 대해서 typedef 연산을 통해 재정의가 일어난 몇 가지 경우를 표현했다. 아래에서 제시하는 모든 경우에 대한 결과는 다음과 같다.

14번 라인과 같이, 일반 자료형을 통한 변수 선언 하듯, struct 키워드를 포함하지 않더라도 구조체 변수의 선언이 가능하다. 밑에서 11번 라인에 들어가는 다양한 경우의 typedef 선언을 제시.

 

  • 함수 구조체 인자 전달

 

  • 구조체를 대상으로 가능한 연산

구조체 변수를 대상으로 하는 연산은 제한적으로 대입(=), sizeof, 주소(&) 연산만이 허용된다. 특히 구조체의 변수 간 대입 연산을 진행하게 되면 멤버 대 멤버의 복사가 이루어진다.

15번 라인에서 대입 연산을 진행하고 있고 그 결과, pi1pi2의 멤버들이 지니는 값은 모두 같은 것으로 미루어 보았을 때, 값의 복사가 이루어지고 배열의 경우에도 모조리 값의 복사가 일어나는 것을 확인할 수 있다.

  •  구조체의 정의 이유와 중첩된 구조체의 선언

구조체의 정의가 필요한 이유

구조체를 통해서 연관 있는 데이터를 하나로 묶을 수 있는 자료형을 정의하면, 데이터의 표현 및 관리가 용이해지고, 합리적인 코드를 작성할 수 있다.

구조체를 정의하지 않고 구현한 경우 문제가 되는 점 : 인자 전달 시 매개 변수가 늘어남, 묶어서 하나의 인덱스에 대해서 관리가 불편해짐. 높은 차원의 배열이 계속 생성됨.

 

중첩된 구조체의 정의와 변수 선언

구조체 내에 구조체 변수를 선언할 수 있다. 이때 초기화 할 경우에는 순서에 주의하고, 중첩된 구조체 변수에 대한 초기화 시 중괄호를 통해 구분한다.

  • 공용체(union)

공용체는 구조체와 유사한 개념이긴 하나, 다른 점은 멤버 변수들이 하나의 공간을 공유하는지의 차이가 있다.

위의 예제는 구조체와 공용체의 차이를 나타내는 예제이다. 공용체의 정의나 선언 방법은 구조체와 완전히 똑같다고 보면 된다. 그러나 차이는 24~ 28번 라인에서 볼 수 있다.

24번 라인에서는 구조체의 멤버의 각 주소 값을 출력하는데 결과를 보면 자료형 별 차이만큼 주소 값이 잘 할당되어 있는 것을 확인할 수 있는데 25번 라인에서 공용체의 멤버의 각 주소를 출력하는데 결과를 보니 모두 같은 주소(공용체의 시작 주소) 값을 가지고 있었다. 게다가 이상한 것은 이 뿐만이 아니다. 27번 라인에서 sData의 크기는 멤버 변수들의 메모리 공간 할당 크기들을 합한 결과 값이 나오는데, uData의 크기는 멤버 변수 중 가장 할당 크기가 큰 자료형의 길이 값이 나왔다.

< 구조체 멤버 변수의 할당 구성 >
< 공용체 멤버 변수의 할당 구성 >

위의 그림은 구조체와 공용체의 멤버 변수들의 할당 구성을 나타낸 것이다. 굵은 검정 칸이 실제로 할당된 메모리 공간이 된다. 구조체 할당 구성에서는 각 멤버 변수 별로 크기만큼 할당이 되어 있는 것을 볼 수 있는데 공용체 할당 구성에서는 가장 큰 double 자료형의 크기만큼만 공간을 할당하고 모든 멤버 변수가 이 공간을 공유하는 형태로 할당 구성이 이루어진다.

이러한 공용체는 다음과 같은 예제에서 유용하게 사용될 수 있다.

 

정수 하나를 입력 받고, 입력 받은 정수의 상위 2바이트와 하위 2바이트의 값을 양의 정수로 출력하고, 상위 1바이트와 하위 1바이트에 저장된 아스키 문자 출력하기

 

  • 열거형

구조체나 공용체는 자료형을 명시함으로써 멤버에 저장할 값의 유형(type)을 결정하였다면, 열거형의 경우는 저장이 가능한 값 자체를 정수의 형태로 결정한다.

0, 1, 2, 3, 4, 5, 6 값들을 저장 가능한 color형 자료형

Color 자료형으로 선언되는 모든 변수들은 enum Color 정의에서 정의해 둔 상수들을 값으로 갖을 수 있다. 또한 정의된 상수들은 어디서든 지역 범위를 고려하여 사용할 수 있다. enum 정의 및 변수 선언은 구조체, 공용체와 완전히 똑같다. 그리고 상수 정의 시, 어떤 값도 넣지 않으면 순서대로 0 값부터 값이 1씩 올라가는 형태로 값의 배정이 이루어진다.

다음의 예에서 특이한 점을 발견할 수 있다.

위에서는 정의된 상수들에서 RED에 값을 지정하지 않았으므로 0부터 시작하는데 YELLOW10이라는 값을 지정했다. 그리고 그 결과 YELLOW 이후 부터는 10부터 1씩 증가한 값들로 배정된다.

열거형은 위와 같이 활용될 수 있다.

열거형의 유용함은 이름있는 상수의 정의를 통한 의미의 부여에 있다. 둘 이상의 연관이 있는 이름을 상수로 함께 같이 묶어서 선언함으로써 프로그램의 가독성을 높인다.

728x90
반응형

'컴퓨터 언어 정리 > C 언어' 카테고리의 다른 글

18 동적 메모리 할당  (0) 2020.10.15
17 파일 입출력  (0) 2020.10.13
15 문자와 문자열  (0) 2020.09.29
14 함수 포인터와 void 포인터  (0) 2020.09.23
13 다차원배열 및 포인터의 관계  (0) 2020.09.21
728x90
반응형

오늘은 오랜만에 기록이다.

요즘 프리 바람의나라 서버를 개발하고 있는 탓에 DB가 필요하다. 웹에서는 DB가 거의 기본이다 싶었지만, 예전에 DB 개념이 거의 전무하다 싶을 때에는 파일 입/출력을 통해서 유저, 아이템 데이터 등을 관리했다. 

그 결과, 텍스트화 되어 있는 게임 데이터를 모두 메모리에 올려다 놓고 거기에 항목에 대한 엑세스까지 행해야하므로 메모리는 메모리 대로 씨피유는 씨피유 대로 박살났던 기억이 있다... ㅠㅠ

아무튼 웹 개발 일을 하면서, 아! 이래서 DB를 썼어야했구나! 아주 절실하게 느끼곤 하는데, 바로 그 DB를 설치해볼 것이다.

그것도 우분투 20.04에서 Maria DB 를 설치해보았다.
우분투 22.04에서 Maria DB를 다시 설치해 보았고, 대체로 동작하지만 몇몇 부분은 누락되는 부분들을 수정했다.

 

1. 간단하게 바로 설치

sudo apt install mariadb-server

매우 간단하다.

 

2. 설정

설정이 좀 중요하다.

sudo mysql_secure_installation

위의 명령어를 작성하면 여러 설정을 위한 질문들이 나온다

Change the root password? [Y/n] n
Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] n
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y

읽어보면 바로 알 수 있는 내용들이다. 굵은 글씨는 내가 설정한 내용들이다.

22.04 ) 윗 부분에 없는 질문들은 모두 Default 값으로 엔터만 눌러 넘어가도 문제 없다.

maria db 설정에 성공한 모습이다

3. Character Set 설정

문자 설정 매우 중요하다. 한글이 파괴되면 얼마나 슬프단 말인가. 영어만 입력을 해야하니.. 다음과 같이 하면 된다.

sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf

위 명령어를 쳐서 general -> unicode로 변경한다. 혹시 모르니까 기존 줄(106)은 주석을 쳐뒀다.

4. Port 변경

remote 가 붙는 경우가 대부분이므로, 포트 설정을 중요하다. 로컬이어도 포트는 사용되니 중요하다.

sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf

마찬가지로 위 명령어를 통해 port 항목의 값을 원하는 포트 번호로 수정한다.

설정 파일 구조가 조금 바뀌었지만 자신 있게 변경하면 문제없다.

5. 외부 접속 허용 설정

remote 가 아닌 local 에서 다음과 같이 실행한다.

sudo mysql -u root  # sudo 계정으로 접속

이 아래 부터는 maria db 명령을 치는 곳에다가 쳐야한다.(위 명령을 통해 접속했으므로)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
설치 당시(2023-05-09) Maria DB 10.6.12 버전에서는 mysql_native_password 가 기본으로 설정 되어 있는 것 같다.... 그래서 점선 사이 부분은 사실 상 커맨드 치지 않아도 된다.
----------------------------------------------------------------------------------------------------------------------------------------------------------------

use mysql; 
SELECT User, Host, plugin FROM mysql.user; 
update user set plugin='mysql_native_password' where user='root';
flush privileges; 
select user, host, plugin from user; 

위 명령들을 치면 A 사진이 B 사진 처럼 바뀌어야한다. ( Plugin 부분 )

A사진
B사진

----------------------------------------------------------------------------------------------------------------------------------------------------------------

그리고! 다음 명령어로 유저 별로 외부 접속을 허용시켜주자! 

GRANT ALL PRIVILEGES ON *.* TO '아이디'@'%' IDENTIFIED BY '패스워드';   
FLUSH PRIVILEGES; 

갑자기 뜬금 없이 아이디가 shyoo 가 나와서 헤깔렸을 수 있는데.. 새로운 아이디 생성은 다음과 같이 CREATE USER 갈겨주면 해결된다.
CREATE USER 'USERID'; // 지정한 이름으로 아이디 생성
CREATE USER 'USERID'@'%' identified by 'PASSWORD';
// 지정한 이름으로 아이디 생성 및 외부 접근 허용(%), 비밀번호 설정까지 함께 진행한다.
FLUSH PRIVILEGES; 

그러니까 기존에 아이디가 존재했다면 GRANT 명령을, 아이디를 새로 생성하는 경우에는 CREATE USER 명령을 갈기면 된다.

6. 서버 허용 아이피 변경

접속을 시도했는데 이런 오류가 뜬다면 문제가 있는 것이다.

그럴 때에는 다음과 같이 한다.

sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf

위 명령을 작성 후 그림과 같이 한다.

 

7. 서비스 재시작

sudo /etc/init.d/mysql stop
sudo /etc/init.d/mysql start
--------------------------------------------------------------------
Maria DB 10.06.12 버전에서는 명령 스크립트 이름이 mariadb로 변경됐다.
sudo /etc/init.d/mariadb stop
sudo /etc/init.d/mariadb start

위 명령어를 통해 종료와 시작이 잘 이루어지는지 확인을 한다.

성공한 모습이다.

8. 확인

 

728x90
반응형
728x90
반응형

문자와 문자열

  • 입력과 출력

  • 스트림

프로그램과 입력 및 출력 장치를 연결해주는 소프트웨어적으로 구현되어 있는 도구, 입출력 장치와 프로그램을 연결해주는 가상의 다리이다. 운영체제가 이를 제공한다.

  • 문자 단위 입/출력 함수

EOF End Of File의 약자로 파일의 끝을 표현하기 위해 정의된 상수이다. 위의 함수가 파일이나 콘솔의 출력으로 EOF를 반환하게 되면 파일이나 콘솔로부터 더 이상 입력 받을 것이 없다 라는 의미를 갖는다. 그렇다면 EOF를 반환하는 시기는 다음과 같다.

함수 호출의 실패할 경우
컨트롤 + z / 컨트롤 + d 키가 입력될 경우

문자를 입력 받는 함수인데 반환 형이 정수 int 형인 이유는? 컴파일러에 따라 char unsigned char로 처리하는 경우가 존재한다. 그런데 EOF 라는 값은 -1로 정의되어 있는데 charunsigned char로 처리하는 컴파일러는 EOF에 대한 처리가 불가능하다. 그렇기 때문에 같은 정수 타입인 int로 반환한다.

 

  • 문자열 단위 입/출력 함수

기존의 scanf 함수 등은 공백을 포함한 문자열을 입력 받기에 있어서 크게 제한이 되었다. (공백을 기준으로 입력을 나누기 때문)

 

  • 표준 입/출력 버퍼

표준 입/출력 함수를 통해서 데이터를 입/출력하는 경우, 해당 데이터들은 운영체제가 제공하는 메모리 버퍼를 꼭 통과하게 되어 있다. 메모리 버퍼는 데이터를 임시로 저장해 놓는 메모리 공간인데, 이런 저장하는 작업을 버퍼링 이라고 한다.

키보드로 데이터를 입력하면(문자열 입력 후 엔터를 칠 경우) 해당 데이터들은 입력 버퍼로 전송되어 저장되고(버퍼링), 그 이후에 프로그램으로 데이터들이 전송이 된다.

 

버퍼링을 하는 이유?

프로그램 연산(CPU 연산)에 비했을 때 I/O 장치들 과의 I/O 작업은 상당히 시간이 걸리는 작업이다. , 데이터를 읽어오는데 걸리는 시간이 CPU의 연산 처리 속도보다 훨씬 느리다. 그렇기 때문에 CPU는 그러한 느린 데이터 입출력 처리를 기다리고 있어야 하는데 데이터를 읽을 때마다 조금씩 전송을 하는 구조로 진행되면 데이터 전송의 효율성이 떨어진다. 그렇기 때문에 중간에 메모리 버퍼를 두고 데이터를 한데 묶어서 보내면 빠르고 효율적인 데이터 전송이 될 수 있다.

 

  • 출력 버퍼와 입력 버퍼 비우기!

출력 버퍼를 비운다는 것은 출력 버퍼에 저장된 데이터가 버퍼를 떠나서 목적지로 이동되는 것을 이야기하는데, 이러한 역할을 하는 함수가 존재하는데 다음과 같다.

위 함수는 파일을 대상으로도 버퍼 비우기가 가능하고, 출력 버퍼를 비우는 일은 많이 존재하지는 않는다.

반면에, 입력 버퍼를 비운다는 것은 출력 버퍼와는 다르게 데이터의 소멸을 의미한다. fflush 함수를 이용하여fflush(stdin); 과 같은 형태로 호출하면 입력 버퍼는 비워지거나/비워지지 않거나 보장할 수 없는 결과를 나타낸다. (Windows 계열의 컴파일러의 경우는 입력 버퍼를 지워 준다고 한다.)

입력 버퍼를 비우기 이전에 입력 버퍼를 비워야 하는 경우에 대한 예제이다.

먼저, 6번 라인에서 str1의 크기를 10 바이트로 지정해주었고, 10번 라인에서 str1에 문자열을 대입하는데 9자리의 문자를 대입한다. 9자리라고 함은 문자열 9자리와 NULL 문자 1바이트를 고려한 것이다. 그러나 입력될 때에는 “abcdefghi\0\n” 이 입력되고 입력 버퍼에 들어가게 된다. (fgets함수의 ‘\n’이 입력될 때까지 읽어 들이는 특성 상) 그래서 10번 라인에서는 입력한 “abcdefghi” 문자열에 ‘\0’ 문자가 합쳐져서 입력으로 str1에 저장된다. 그리고 13번 라인에서 fgets 함수가 호출되는데 이 때 입력 버퍼에 남은 ‘\n’ 문자를 읽어버리고 바로 호출이 끝나게 된다. 그렇기 때문에 실행 결과에서는 str2‘\n’만 저장된 결과가 나타난 것이다. 이러한 경우 str1의 저장 이후 입력 버퍼를 비웠더라면 다음 문자열 입력을 받을 수 있었다. 이것이 입력 버퍼 비우기의 필요성이다.

입력 버퍼를 비운다는 것은 문자들을 읽어 들이면 지워진다, 즉 비워진다. 다음과 같은 함수로 입력 버퍼를 지울 수 있다.

23번 라인에서 한 문자씩 읽는데 그 읽은 것이 ‘\n’ 개행 문자일 경우 탈출하게 된다. , 개행 문자까지 버퍼로부터 문자를 읽음으로써 버퍼가 비워진다.

 

  • 문자열의 길이를 반환 : strlen

일단, size_ttypedef unsigned int size_t; 로 자료형 재정의가 되어 있다.

 

  • 문자열의 복사 : strcpy

strcpysrc의 문자열을 dest에 복사한다. strncpy의 경우에는 src의 문자열을 dest에 복사하되, n 크기만큼 복사한다. , strncpy의 경우 마지막 문자가 NULL인지에 대한 검사는 하지 않고 주어진 n만큼만 복사를 한다. , 복사된 문자열의 끝에서 NULL에 대한 처리를 진행하지 않기 때문에 직접 NULL 처리를 해줘야 한다. 이후에 기재되는 모든 n이 들어가는 함수는 NULL처리가 진행되지 않는 공통점을 가지고 있다.

 

  • 문자열 연결 : strcat

Dest 문자열 뒤에 src 문자열을 붙이고 그 값을 dest에 저장한다. 앞 문자열의 NULL 문자를 없애고 그 자리부터 뒷 문자열이 시작된다.

 

  • 문자열 비교 : strcmp

  • 이외의 문자열/수 변환 함수

728x90
반응형

'컴퓨터 언어 정리 > C 언어' 카테고리의 다른 글

17 파일 입출력  (0) 2020.10.13
16 구조체  (0) 2020.10.12
14 함수 포인터와 void 포인터  (0) 2020.09.23
13 다차원배열 및 포인터의 관계  (0) 2020.09.21
12 포인터의 포인터  (0) 2020.09.15
728x90
반응형

연산자 오버로딩

  • 연산자 오버로딩

C++언어에서는 연산자의 오버로딩을 통해서 기존에 존재하던 연산자의 기본 기능 이외에 다른 기능을 추가할 수 있다.

먼저 15번 라인을 통해서 operator+ 라는 함수를 제공한다. 이 함수를 정의함으로써 연산자를 오버로딩한다. 29번 라인을 보면 operator+ 멤버 함수를 호출함으로써 a1의 멤버 변수 value의 값과 a2의 멤버 변수 value의 값을 더한 결과를 a3로 객체 복사를 통해서 전달하고 있음을 확인할 수 있다. 그런데 이러한 과정이 30번 라인에서도 똑같이 일어나고 있음을 알 수 있다. , 정리해보자면 다음과 같음을 알 수 있다.

이는 객체 또한 기본 자료형 변수처럼 취급하고 각종 연산이 가능하도록 하려는 C++의 기본 철학이다. operator 키워드와 연산자를 묶어서 함수를 정의하면 함수의 이름(operator’연산자’)을 이용한 함수 호출은 물론 연산자만을 이용해도 operator’연산자함수 호출로 바꾸어 준다.

  • 연산자 오버로딩의 두 가지 방법

멤버 함수에 의한 연산자 오버로딩

위에서 제시한 예의 경우이다.

전역 함수에 의한 연산자 오버로딩

다음의 예를 통해 설명.

20번 라인에서 private 영역에 접근을 위해서 전역 함수를 friend 선언을 해 주었다. 그리고 22번 라인에서 + 연산자에 대해서 전역 함수 형태로 오버로딩 하였다. 이 함수를 이용해서 30번 라인에서 a1+a2operator+(a1,a2) 형태로 해석이 된다.

 

l  오버로딩이 가능한 / 불가능한 연산자의 종류

오버로딩 불가능 연산자

오버로딩 가능 연산자

 

  • 연산자 오버로딩 주의사항

1.     본래의 의도를 벗어난 형태의 연산자 오버로딩은 좋지 않다.

2.     연산자의 우선순위와 결합성은 바뀌지 않는다.

3.     매개변수의 디폴트 값 설정이 불가능하다

4.     연산자의 순수 기능까지 빼앗을 수 없다.

 

  • 단항 증감 연산자(선 증감 후 연산)의 오버로딩

operator++( ) 처럼 인자에 아무 것도 명시하지 않은 경우에는 선 증감 후 연산의 증감 연산자가 오버로딩 된다.

  • 단항 증감 연산자(선 연산 후 증감)의 오버로딩

operator++(int)처럼 인자에 int 키워드를 넣어 명시하는 경우에는 선 연산 후 증감의 증감 연산자가 오버로딩 된다. 인자 전달의 int 의미는 아니라는 것. 이 때 int 키워드는 매개 변수의 가장 뒤에 넣는다.

21번 라인이나 30번 라인과 같이 Const를 이용하여 객체를 반환한 이유는 단순히 const A 타입은 returnObj 반환하기 때문에 const를 기재한 것이 아니라, 반환함으로써 생기는 임시 객체를 const 객체로 생성한다는 의미에서 const를 기재한 것임.

선 연산 후 증감에 대한 연산자의 경우 해당 연산이 존재하는 라인에서는 증가되지 않은 값을 출력해야 한다. 그리고 또한 그 값을 변경할 수도 없기 때문에 참조 값을 반환하지 않을 뿐 더러, 임시 객체를 const로 반환한다.

 

(a1++)++ 이 불가능한 이유

(a1++)++ -> (a1.operator++( ))++ -> (A const 임시객체)++ -> (A const 임시객체).operator++( )

이 과정에서 마지막 operator++( ) 호출이 불가능하다. 왜냐하면 const로 선언된 함수 내에서는 const 함수의 호출만을 허용하기 때문이다.

 

  • 교환 법칙이 필요한 연산자의 오버로딩 ( * )

멤버 함수의 * 연산자 오버로딩에서, 이와 같은 경우는 멤버 함수의 연산자 오버로딩으로 설명이 가능하다.

a1 * 3; -> a1.operator*(3);

그러나, 아래와 같은 경우는 멤버 함수의 연산자 오버로딩으로 설명이 불가능하다

3 * a1; -> 3.operator*(a1);   -   ?

이러한 경우에는 전역 함수의 형태로 연산자 오버로딩을 진행해야 한다.

a1 * 3; -> operator*(a1, 3);
3 * a1; -> operator*(3, a1);

교환 법칙을 성립한 연산자 오버로딩의 예제이다.

  • cout, cin, endl의 정체 ( >> 연산자의 오버로딩 )

정체는 위의 예와 같다. cout은 생성된 객체의 이름이었고, endl은 개행 기능을 하는 함수이다.

  •  <<, >> 연산자의 오버로딩 ( 특별 자료형에 대한 coutcin의 구현 )

coutcin에 대해서 <<, >> 연산자 오버로딩을 하기 위해서는 전역 함수를 통한 오버로딩을 해야 한다. 왜냐하면 멤버 함수를 통하여 오버로딩을 하기 위해서는 ostream(객체 cout의 클래스) 클래스와 istream(객체 cin의 클래스) 클래스를 수정해야 하는데 그것은 불가능하기 때문이다.

  • 대입 연산자의 오버로딩

대입 연산자 오버로딩은 복사 생성자와 그 성격이 매우 유사하다.

디폴트 대입 연산자 : 객체 간의 대입 연산

다음은 복사 생성자와 대입 연산자의 공통점이다.

다음은 복사 생성자와 대입 연산자의 유일한 차이점이다.

그러므로 객체 간의 대입 연산을 진행할 시에는 복사 연산자의 경우처럼 깊은 복사 문제에 대해서도 고려를 해야 한다

상속 관계에서의 대입 연산자 오버로딩

Derived 클래스를 통하여 디폴트 대입 연산이 진행되면 base 클래스의 대입 연산자까지 호출한다. 그러나 derived 클래스에서 대입 연산자를 정의해주면 명시적으로 base 클래스의 대입 연산자의 호출문을 삽입하지 않으면, base 클래스의 대입 연산자는 호출되지 않아서 base 클래스의 멤버 대 멤버 복사는 이루어지지 않는다.

 

대입 연산자 호출과 이니셜라이저의 효과

38번 라인처럼 이니셜라이저를 사용하면 선언과 동시에 초기화가 이뤄지는 형태로 바이너리 코드가 생성되기 때문에(A aaa=ref와 같은 효과) 58번 라인에서는 복사 생성자만 호출된다. 그러나 생성자의 몸체 부분에서 대입 연산을 통한 초기화를 진행하면, 선언과 초기화를 각각 별도의 문장에서 진행하는 형태로 바이너리 코드가 생성된다. 그렇기 때문에 50번 라인처럼 초기화를 진행하는 경우이기 때문에 60번 라인은 생성자 이외에 대입 연산자자 호출되는 것이다.

  • 배열의 인덱스 연산자 오버로딩

C언어 및 C++언어의 배열은 배열 인덱스에 대한 검사를 하지 않는다. 그렇기 때문에 배열이름[범위를 넘어선 인덱스]의 경우를 허용한다. 이런 허용을 막는 배열 클래스를 만들어 볼 것이다. 배열의 역할을 하는 클래스.

위와 같이 만든 배열 클래스를 이용하여 배열 간의 대입을 이루어지게 하면 얕은 복사가 이루어지므로 깊은 복사가 이루어지도록 복사 생성자 및 대입 연산자를 수정할 수 있다. 그러나 배열은 저장소의 일종이고, 저장소에 저장된 데이터는 유일성이 보장되어야 하기 때문에 저장소의 복사는 불필요하거나 잘못된 일로 간주된다.

그러므로 다음과 같이 복사 생성자나 대입 연산자를 private 멤버로 둠으로써 복사와 대입을 차단한다.

 

  • Const 함수의 오버로딩

위의 예제에서 문제는 showAllData 함수의 매개 변수로 const Array& ref 형태로 Array 객체를 받아들이는데 41번 라인에서 const화 되지 않은 함수 operator[] 가 호출된다. 그렇기 때문에 에러가 발생하는데 그렇다고 해서 operator[] 함수를 const화 할 수도 없는 노릇이다. 왜냐하면 배열에 저장이 불가능해지기 때문이다. 그렇기 때문에 const 선언 유무에 따른 함수 오버로딩을 통해 위의 문제를 해결해야 한다.

 

  • new & delete 연산자의 오버로딩

new 연산자 오버로딩

위와 같은 문장에서 new 연산자가 오버로딩되어 있는 상태라면 어떻게 해석해야 하는가? 해석할 수 없다. 왜냐하면 오버로딩 된 new 연산자는 기본적으로 제공하는 new 연산자를 완벽 대체가 불가능하다.

기본 new 연산자의 기능

1.     메모리 공간의 할당

2.     생성자의 호출

3.     할당하고자 하는 자료형에 맞게 반환된 주소 값의 형 변환

기본 new 연산자의 기능 중 2, 3번은 여전히 컴파일러가 맡게 되고, 1번의 기능인 메모리 공간의 할당만 오버로딩 할 수 있다.

위와 같은 형태로 오버로딩 된다. 출력 타입은 무조건 void * 형태이고, 매개 변수로 사이즈를 받을 수 있다.

오버로딩 된 new 연산자를 이용하여 위와 같이 할당을 하더라도 new 연산자가 반환하는 값은 operator new 함수가 반환하는 값이 아니고, 컴파일러에 의해서 적절히 형 변환이 된 값이다. 또한 생성자의 호출정보는 operator new 함수와 아무런 상관이 없다.

 

delete 연산자 오버로딩

위와 같은 형태로 delete 연산자를 오버로딩 한다.

 

newdelete 연산자 오버로딩의 예

18번 라인과 24번 라인에서 static을 넣은 이유는 객체가 생성이 되기 전에 호출되어야 하기 때문이다.

new, delete 연산자를 이용하여 배열 할당 및 소멸을 할 경우에는 다음의 operator 함수가 호출된다.

 

  • 포인터 연산자 오버로딩

아래의 예는 위의 포인터 관련 연산자를 구현한 예제이다.

40번 라인의 경우에는 객체의 참조 값에 대입이 이루어지고 있으니 대입 연산자가 호출되어 number 멤버 변수에 값이 저장되었고, 41번 라인에서는 Number*형 주소 값을 이용하여 -> 연산자(추가)를 이용하여 멤버 함수에 간접 접근을 통해 호출한다. 그리고 42번 라인은 객체의 참조 값을 이용하여 .연산자를 이용하여 멤버 함수에 직접 접근을 통해 호출한다. 위의 예처럼 main 함수에서 private 멤버의 값을 변경하는 연산은 좋지 못함.

 

  • 스마트 포인터

스마트 포인터란 포인터의 역할을 하는 객체를 뜻한다.     

객체에 대해서 포인터 역할을 하는 객체이면서도 가리키는 객체에 대해서 delete 연산이 자동으로 이뤄졌다는 점에서 스마트 포인터이다. 위의 예는 아주 작은 기능의 스마트 포인터를 구현해 본 예이다.

 

  • ( ) 연산자의 오버로딩

( ) 연산자를 오버로딩 하면 객체를 함수처럼 사용할 수 있다. 함수처럼 동작하는 클래스를 가리켜 펑터(Functor)라고 한다.

 

  • 임시 객체로의 자동 형 변환과 형 변환 연산자

 

위의 예제에서 39번 라인에서 서로 다른 자료형 임에도 불구하고 대입 연산이 이루어 졌음을 확인할 수 있다.

num=30;

num = Number(30);
임시 객체의 생성

num.operator=(Number(30));
임시 객체를 대상으로 하는 대입 연산자 호출

위와 같은 단계로 대입이 이루어짐. 이는 정리를 해보자면,

A형 객체가 와야 할 위치에 B형 데이터(또는 객체)가 왔을 경우, B형 데이터를 인자로 전달받는 A형 클래스의 생성자 호출을 통해서 A형 임시 객체를 생성한다.

, Number 객체가 와야 할 위치에 정수형 데이터가 왔기 때문에 정수형 데이터를 인자로 전달받는 Number 형 클래스의 생성자 호출을 통해 해당 정수형 데이터가 대입되어 있는 Number형 임시 객체를 생성한다.

그렇다면 반대로 Number형 객체를 정수형 데이터 타입으로 변경할 수 있는지 다음의 예제로 확인한다

23번 라인에서 int 형 변환 연산자 오버로딩이 이루어지고 있다. 형 변환 연산자는 반환 형을 명시하지 않는다. 그러나 return 문을 통해서 값의 반환은 가능하다.

46번 라인에서 먼저 num1int 형 변환 발생이 일어나고 그 결과 값을 3과 더한 뒤, 그 정수 값을 다시 num2에 대입하는 과정에서 num2의 생성자가 호출되어 임시 객체를 통한 대입 연산자가 호출되고 대입이 이루어진다.

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

23 템플릿  (0) 2020.10.13
22 string 클래스 - 직접 일부 구현  (0) 2020.10.12
20 다중 상속  (0) 2020.09.22
19 멤버함수 가상함수의 동작 원리  (0) 2020.09.15
18 상속과 다형성  (0) 2020.09.15
728x90
반응형

함수 포인터와 void 포인터

  • 함수 포인터

함수 또한 메모리 상에 바이너리 형태로 올라가 호출 시 실행이 되는데, 메모리 상에 저장된 함수의 주소 값을 저장하는 포인터 변수가 함수 포인터 변수이다.

배열의 이름이 배열의 시작 주소 값을 의미하듯이 함수의 이름 또한 함수가 저장된 메모리 공간의 시작 주소 값을 의미한다.

함수 이름의 포인터 형은 반환 타입과 매개변수의 선언을 통해 결정 짓도록 약속. , 반환 타입과 매개변수의 선언이 일치하는 모든 함수들의 포인터 형은 모두 같다.

9번 라인과 10번 라인에서 볼 수 있듯이

반환 타입 (*포인터변수이름) (매개 변수) = 함수 이름

다음과 같은 형태로 함수 포인터 변수를 선언하고 함수의 주소를 받아서 12, 13번 라인처럼 함수 포인터 변수를 통한 함수 호출이 가능하다.

  • Void 형 포인터 ( 자료 형이 없는 포인터 )

다음과 같이 선언되는 포인터를 void 형 포인터 변수라고 한다.

Void 형 포인터는 어떤 변수의 주소 값이든 함수의 주소 값이든 상관 없이 주소값의 경우는 무엇이든 전부 담을 수 있다. 다음 예는 void * 변수에 다양한 주소 값을 저장하는 예를 나타낸 것이다.

, void 형 포인터 p를 이용하여 참조를 하는 순간(=포인터 연산을 하는 순간) 컴파일 에러가 발생한다. 왜냐하면 void * 포인터는 포인터 연산 시 증감의 크기에 대한 정보가 단 한 부분도 없다. , 해당 주소로부터 어느 범위까지 연산을 하여 참조를 해야하는지에 대한 정보가 없기 때문에 참조(포인터 연산)가 불가능하다.

void형 포인터는 주소 값을 저장하는 것에만 의미를 두고 포인터 형은 나중에 결정한 참조하는 경우에 유용하게 사용된다 -> 동적 메모리 할당

  • Main 함수를 통한 인자 전달

위의 예제에서 argc는 파일 이름을 포함한 전달 되어온 인자의 수를 나타내고, argv는 더블 포인터 형태로 싱글 포인터 혹은 싱글 포인터 배열의 각 요소를 가리킨다. , argv는 두 번째 결과에서 “./t”, “yoo”, “seung”, “ho”의 주소 값인 char * 타입을 가리키기 때문에 더블 포인터 형태이다.

728x90
반응형

'컴퓨터 언어 정리 > C 언어' 카테고리의 다른 글

16 구조체  (0) 2020.10.12
15 문자와 문자열  (0) 2020.09.29
13 다차원배열 및 포인터의 관계  (0) 2020.09.21
12 포인터의 포인터  (0) 2020.09.15
11 다차원 배열  (0) 2020.09.14
728x90
반응형

다중 상속

  • 다중 상속

위와 같은 상황을 다중 상속된 상황이다. 이러한 다중상속의 상황에서는 멤버 함수의 이름을 명확하게 구분 짓지 않으면 모호성이 발생하게 된다. 이런 경우에는 범위를 지정하여 멤버 함수를 호출하는 방법이 있다.

 

DeepDerived 클래스에서 DerivedLeft::func(); DerivedRight::func(); 또한, DeepDerived 클래스에서 Base 클래스의 멤버 함수를 호출할 때 DerivedLeft에 상속된 멤버를 호출해야 할지, DerivedRight에 상속된 멤버를 호출해야 할지 알 수 없는 경우가 발생한다. 이런 경우에는 가상 상속 개념을 이용한다.

위의 상황에서는 Base 클래스가 두 번 상속되게끔 되어 있다. 그러나 이 두 번 상속되는 다중 상속의 상황을 한 번 만 상속되도록 하는 문법이 가상 상속 개념이다. virtual 키워드를 이용하여 가상 상속을 적용하자.

위와 같은 방법을 적용하면 DeepDerived 클래스에서 Base 클래스의 멤버 함수를 호출할 때 DerivedLeft에 상속된 멤버를 호출해야 할지, DerivedRight에 상속된 멤버를 호출해야 할 지에 대한 문제는 자동으로 해결된다. 왜냐하면 Base 클래스가 딱 한 번 상속되기 때문이다. 그렇기 때문에 Base 클래스의 생성자도 한 번 실행되고, Base 클래스의 함수 또한 하나씩만 존재하게 된다.( 두 클래스에 걸쳐 있는 형태로 )

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

22 string 클래스 - 직접 일부 구현  (0) 2020.10.12
21 연산자 오버로딩  (0) 2020.09.23
19 멤버함수 가상함수의 동작 원리  (0) 2020.09.15
18 상속과 다형성  (0) 2020.09.15
17 상속  (0) 2020.09.14
728x90
반응형

다차원 배열과 포인터의 관계

  • 2차원 배열과 포인터의 잘못된 오해

정수형 2차원 배열의 배열 이름은 정수형 더블 포인터 형이라고 오해하고 2차원 배열의 이름을 매개 변수 혹은 변수로 주소 값을 받을 때 더블 포인터 변수로 받을 수 있다.

<2차원 배열과 포인터의 잘못된 관계>

더블 포인터로 받을 수 있는 것은 포인터 배열의 이름이다.

  • 2차원 배열의 배열 이름의 포인터 형은?

먼저, 알아야 할 것은 arrarr[0]의 차이를 알아야 한다.

arr arr[0] 2차원 배열의 첫 시작 주소를 가리키지만 차이점이 존재한다. sizeof 연산자를 이용하여 11번째 라인, 12번째 라인처럼 사이즈를 체크했을 때 arr은 배열의 전체 길이를 나타냈지만, arr[0]의 경우는 해당 행의 길이를 반환하였다.

그리고 다음으로는 배열 이름을 이용하여 포인터 연산을 행했을 때 어떤 연산 결과가 나타나는지 알아야한다.

2차원 배열 이름에 +1, +2 포인터 연산을 진행하였을 때 각 주소는 8씩 증가하는 것을 볼 수 있다.

이번에는 2차원 배열의 열에 해당하는 인덱스를 2에서 4로 변경한 후 포인터 연산을 진행했는데, 이 때 주소의 증가 폭은 16이 증가하였다.

두 경우를 잘 살펴보면 같은 정수형 2차원 배열 임에도 불구하고 포인터 연산의 증감 폭이 다르다. 포인터 연산 시 증감하는 폭을 결정하는 것은 2차원 배열에서 열의 길이에 따라서 달라짐을 알 수 있다.

두 가지 요인을 이용하여 최종 결론을 내리자면 먼저, 2차원 배열에서도 배열 이름을 통하여 배열 전체를 대표하고, 이 배열 이름을 통하여 포인터 연산을 행한다는 점과 같은 타입의 2차원 배열이라고 할지라도 배열 이름을 통하여 포인터 연산을 행할 때 증감의 차이는 열의 길이에 따라서 증감의 폭이 결정된다. 결국, 이 증감의 폭까지 같아야 비로소 같은 포인터 형이 된다는 점.

위의 결론을 토대로 7번 라인에서 알 수 있는 정보는 2차원 배열 이름(2차원 배열의 첫 주소)과 포인터 연산 시 증감하는 크기 그리고 자료 형을 알 수 있기 때문에 이를 반영하여 8번 라인처럼 2차원 배열을 받는 포인터 변수를 선언할 수 있다. pArr은 정수형 포인터 변수인데 포인터 연산 시 sizeof(int) x 열의 길이(4)만큼 증감하는 2차원 배열의 주소를 받는 포인터다 라고 해석하면 된다.

  • 2차원 배열을 받는 포인터와 포인터 배열과 혼동하지 말 것.
  • 2차원 배열을 함수의 인자로 전달하기

위와 같이 전달하고, 매개 변수는 두 형태 모두 허용한다.

  • 2차원 배열에서 또한 arr[i]=*(arr+i) 방법이 적용된다.
728x90
반응형

'컴퓨터 언어 정리 > C 언어' 카테고리의 다른 글

15 문자와 문자열  (0) 2020.09.29
14 함수 포인터와 void 포인터  (0) 2020.09.23
12 포인터의 포인터  (0) 2020.09.15
11 다차원 배열  (0) 2020.09.14
10 포인터와 함수  (0) 2020.09.13
728x90
반응형

Map은 연관 컨테이너에 속하는 컨테이너의 한 종류이다. 파이썬의 Dictionary 사전과 같은 자료구조를 갖는다.

Map은 사전처럼 Key(단어)를 갖고, 그 Key에 해당하는 Value(뜻)를 갖는다. 왼쪽 그림처럼 Apple이라는 단어를 찾으면 사과라는 뜻이 나오 듯, key 값을 넣으면 value 값이 나오는 형태이다.

Map에 자료를 추가하거나, 자료를 찾기 위해서는 insert(), find() 등의 함수를 사용하지만 Map의 특성으로 인해서 추가와 탐색을 한 형태로 나타낼 수 있다.

  my_map["apple"] = "핸드폰"; my_map["apple"];
"apple" key가 my_map내에 있을 경우
(value = "사과")
(1) "핸드폰" 값으로 대입 갱신. (2) "사과"를 반환
"apple" key가 my_map내에 없을 경우 (3) key - apple / value - 핸드폰으로
map 컨테이너에 추가 됨.
(4) 반환되는 값이 없다.

첫 번째, 기존의 동일한 key가 map에 존재하는데, 동일한 key로 다시 value 값을 등록하는 경우는 갱신된다.
두 번째, 기존의 동일한 key가 map에 존재한다면, 기존의 key값에 해당하는 value 값을 반환시킬 수 있다.
세 번째, key가 map에 존재하지 않는데, key로 value 값을 등록하는 경우 map에 새롭게 추가된다.
네 번째, key가 map에 존재하지 않는다면, key 자체도 없고 그에 따라서 value도 없으므로 반환되는 값이 없다.

간단한 예제로 마무리 지을 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// https://colorscripter.com/
#include <iostream>
#include <string>
#include <map>
 
using namespace std;
 
int main(void)
{
    map<stringstring> my_map; // key에 해당하는 string, value에 해당하는 string
 
    my_map["a"= "apple";
    my_map["b"= "banana";
    my_map["c"= "carrot";
 
    cout << "--- key로 접근하여 value을 출력하기. " << endl;
    cout << my_map["a"<< endl;
    cout << my_map["b"<< endl;
    cout << my_map["c"<< endl;
 
    cout << "--- iterator 이용하여 key와 value 출력하기. " << endl;
    map<stringstring>::iterator it;
    for (it = my_map.begin(); it != my_map.end(); it++)
        cout << it->first << " : " << it->second << endl;
 
    return 0;
}
 
cs

10번 라인과 같이 key 값의 자료형과, value 값의 자료형들을 명시해야한다. 또한 22번 라인에도 또한 동일한 자료형을 명시하여 iterator 선언해야한다.

24번 라인에서 map내의 iterator 클래스의 멤버 first와 second는 각각 key와 value를 나타낸다.

map을 공부하다보니 pair 및 multimap도 관련이 생기고, tuple도 정리해야겠다는 생각이 들었다.

다음 시간에 정리해보자.

728x90
반응형

'프로그래밍응용 > Modern & STL' 카테고리의 다른 글

Tuple  (0) 2021.01.03
Pair  (0) 2021.01.03
Set  (0) 2020.09.15
List  (0) 2020.09.14
Deque  (0) 2020.09.13
728x90
반응형

 

Set 컨테이너에 대해서 공부해보았다. Set은 연관 컨테이너 중 하나이다. 다음은 연관 컨테이너의 특성이다.

  • 원소들을 검색하기 위한 키(key)를 가지고 있다. (키와 값의 쌍 형태로 저장되는 컨테이너도 있다)
  • 자료들은 정렬되어 있다. (하지만 순서는 의미가 없다)
  • 삽입되는 요소의 위치를 지정할 수 없다.
  • Tree 구조를 기반으로 동작한다.

Set 컨테이너는 '집합'이다. 

중학교 때 배우는 집합의 특징에 대해서 생각해 볼 수 있다.

  • 중복이 허용되지 않는다.
  • 순서가 상관이 없다.

Set이라는 컨테이너는 집합의 특징을 그대로 갖는다. 그리고 이 집합 내에 저장되는 요소를 키(key)라고 한다.

그러나 중복이 허용되는 집합 컨테이너가 존재한다. 다중 집합인 multi-set이다.
중복이 허용되는 것을 제외하면 set과 똑같다. 

간단한 컨테이너 개념이기 때문에 코드로 바로 정리한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// https://colorscripter.com/
#include <iostream>
#include <set>
 
using namespace std;
 
template <typename T>
void print_list(const T& c);
 
int main(void)
{
    set<int> my_set;
    multiset<int> my_multiset;
 
    set<int>::iterator set_it;
    multiset<int>::iterator multiset_it;
 
    cout << "set에서의 요소 추가" << endl;
    my_set.insert(6);
    my_set.insert(7);
    my_set.insert(8);
    my_set.insert(8); // 중복 값이기 때문에 insert가 이루어지지 않음.
    print_list(my_set);
 
    cout << "multi set에서의 요소 추가" << endl;
    my_multiset.insert(100);
    my_multiset.insert(100); // 중복 값 허용이므로 insert가 이루어진다.
    my_multiset.insert(my_set.begin(), my_set.end());
    print_list(my_multiset);
 
    cout << "set에서의 요소 탐색" << endl;
    set_it = my_set.find(8);
    if (set_it != my_set.end())
        cout << *set_it << " 가 발견됨." << endl;
 
    cout << "multi set에서의 요소 탐색" << endl;
    multiset_it = my_multiset.find(100);
    if (multiset_it != my_multiset.end())
        cout << *multiset_it << " 가 발견됨." << endl;
 
    cout << "set에서의 요소 삭제(6)" << endl;
    my_set.erase(6); 
    print_list(my_set);
 
    cout << "multi set에서의 요소 삭제(100)" << endl;
    my_multiset.erase(100); // 100 요소가 여럿인 경우 모두 삭제
    print_list(my_multiset);
 
    return 0;
}
 
 
template <typename T>
void print_list(const T& c)
{
    typename T::iterator it;
    // typename을 붙이는 이유 : 
    // 붙이지 않을 경우 T::iterator에서 iterator을 T 클래스 내의 변수로 생각하기 때문에 T::iterator 자체가 type이라는 것을 명시해야함.
    for (it = c.begin(); it != c.end(); it++)
        cout << "iterator value : " << *it << endl;
    return;
}
cs

먼저, 21, 22번 라인처럼 set 컨테이너에 같은 값을 키로 등록하려고 하는 경우, 나중에 추가되는 중복 값은 추가되지 않고 무시된다. 내부적으로 조건에 의하여 처리된다.

반면, 26, 27번 라인처럼 multiset 컨테이너에서는 중복 값 추가가 허용된다.

46번 라인처럼 multiset컨테이너에서 중복되는 값을 삭제한다면 중복되는 값들 모두 사라진다. 예를 들어 100이 두개 들어있는 multiset의 경우에서 erase(100)을 실행하면 100 두 개가 모두 사라진다.

728x90
반응형

'프로그래밍응용 > Modern & STL' 카테고리의 다른 글

Pair  (0) 2021.01.03
Map  (0) 2020.09.15
List  (0) 2020.09.14
Deque  (0) 2020.09.13
모든 컨테이너 공통 멤버  (0) 2020.09.13

+ Recent posts