-
Notifications
You must be signed in to change notification settings - Fork 4
/
pointer.tex
509 lines (435 loc) · 21.1 KB
/
pointer.tex
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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
\chapter{Pointer} \label{chap:pointer}
% chapter 4: pointer
Pointer는 C 언어에서 제공하는 가장 강력하고 인기있는 기능이지만,
초보 programmer들에게는 악몽과도 같은 것입니다.
Pointer가 가리켜야 할 것을 가리키고 있지 않을 때 발생하는 혼란은
끝이 없습니다. (사실 포인터에서 발생하는 이러한 문제들은 대개
메모리 할당과 관련이 있습니다; Chapter~\ref{chap:memalloc} 참고하기 바랍니다.)
\section{Basic Pointer Use}
% from 4.1
\begin{faq}
\Q{4.1} % 4.1 COMPLETE
도대체, 포인터를 써서 좋은 점이 있나요?
\A
다음과 같은 장점을 포함하여 여러가지 좋은 점이 있습니다:
\begin{itemize}
\item
dynamically allocated arrays (질문 \ql{6.14}, \ql{6.16} 참고)
\item
generic access to several similar variables
\item
(simulated) by-reference function parameters (질문 \ql{4.8}, \ql{20.1} 참고)
\item
dynamically allocated structures of all kinds, especially
trees and linked lists
\item
walking over arrays (for example, while parsing strings)
\item
efficient, by-reference ``copies'' of arrays and structures,
especially as function parameters
\end{itemize}
(물론, 위의 목록이 전부는 아닙니다.)
% (Note that this is hardly a comprehensive list!)
\seealso{\ql{6.8}}
\end{faq}
\begin{faq}
\Q{4.2} % 4.2 COMPLETE
포인터를 선언하고 메모리를 할당하려고 합니다. 그런데 제대로
동작하지 않습니다. 이 코드에 잘못된 부분이 있나요?
\begin{verbatim}
char *p;
*p = malloc(10);
\end{verbatim}
\A
질문하신 분이 선언한 pointer는 \verb+p+이지, \verb+*p+가 아닙니다.
pointer가 어떤 곳을 가리키게 하려면 다음과 같이 그냥 pointer의
이름을 써야 합니다:
\begin{verbatim}
p = malloc(10);
\end{verbatim}
\noindent 그리고, 이 pointer가 가리키는 메모리 공간을 쓰기 위해서는
다음과 같이 `indirection' operator(연산자)인 \verb+*+를 사용합니다:
\begin{verbatim}
*p = 'H';
\end{verbatim}
질문하신 것같은 실수는 자주 볼 수 있습니다.
왜냐하면 \TT{malloc}을 지역 변수 선언의 초기값으로 썼다면 다음과
같이 하기 때문입니다:
\begin{verbatim}
char *p = malloc(10);
\end{verbatim}
이 코드를 선언과 지정(assignment)으로 분리시키려면 \verb+*+를
제거해야 한다는 것을 기억하기 바랍니다.
요약하면, expression에서 \TT{p}는 pointer이고, \TT{*p}는
포인터가 가리키는 곳이라는 (이 경우 하나의 \TT{char}) 것입니다.
\seealso{\ql{1.21}, \ql{7.1}, \ql{8.3}}
\R
\cite{ctp} \S\ 3.1 \page{28}
\end{faq}
\begin{faq}
\Q{4.3} % 4.3
`\verb2*p++2'은 `\verb+p+'를 증가시키는 것인가요, 아니면, `\verb+p+'가
가리키는 것을 증가시키는 것인가요?
\A
% Postfix \verb2++2 연산자는 prefix 연산자보다 높은 우선 순위를 갖습니다.
\TT{*}, \TT{++}, \TT{--}와 같은, 피연산자가 하나인 연산자는 (unary operator라고
합니다.) 항상 오른쪽에서 왼쪽으로 결합합니다.
따라서 \verb2*p++2는 \verb2*(p++)2와 같습니다; 일단 \TT{p}를 증가시킨
다음, \verb+p+가 증가하기 전에 가리키던 곳의 값을 리턴합니다.
\verb+p+가 가리키는 것을 증가시키려면 \verb2(*p)++2을 (또는, 부작용이
일어나도 상관없는 경우, \verb2++*p2를 써도 됩니다) 쓰면 됩니다.
\R
\cite{kr1} \S\ 5.1 \page{91} \\
\cite{kr2} \S\ 5.1 \page{95} \\
\cite{ansi} \S\ 3.3.2, \S\ 3.3.3 \\
\cite{c89} \S\ 6.3.2, \S\ 6.3.3 \\
\cite{hs} \S\ 7.4.4 \Page{192--3}, \S\ 7.5 \page{193},
\S\ 7.5.7, 7.5.8 \Page{199--200}
\end{faq}
\section{Pointer Manipulations}
% from 4.4
\begin{faq}
\Q{4.4} % 4.4 COMPLETE
\TT{int} 배열에 포인터를 써서 작업하려 합니다.
아래 코드에서 어디가 잘못된 것일까요?
\begin{verbatim}
int array[5], i, *ip;
for (i = 0; i < 5; i++) array[i] = i;
ip = array;
printf("%d\n", *(ip + 3 * sizeof(int)));
\end{verbatim}
\A
해야될 것보다 더 많은 일을 손수 해버린 셈입니다. C 언어에서 포인터
연산은 항상 포인터가 가리키고 있는 대상의 크기만큼 scale됩니다. 따라서
올바른 코드는 (간단하게) 다음과 같습니다:
\begin{verbatim}
printf("%d\n", *(ip + 3));
/* 또는 ip[3] - 질문 6.3 참고 */
\end{verbatim}
위 코드는 배열의 세번째 요소를 출력합니다. 이러한 코드에서 여러분이 직접 포인터에,
그 포인터가 가리키고 있는 대상의 크기만큼 곱해주지 않는 것이 정답입니다. 직접 곱해줄 경우,
(위 코드에서는 \TT{sizeof(int)}에 따라 다르지만 대개 \TT{array[6]} 또는
\TT{array[12]}) 존재하지 않는 범위에 접근하려고 할 것입니다.
\R
\cite{kr1} \S\ 5.3 \page{94} \\
\cite{kr2} \S\ 5.4 \page{103} \\
\cite{ansi} \S\ 3.3.6 \\
\cite{c89} \S\ 6.3.6 \\
\cite{hs} \S\ 7.6.2 \page{204}
\end{faq}
\begin{faq}
\Q{4.5} % 4.5 COMPLETE
문자 포인터로 (\verb+char *+ pointer) 어떤 int를 가리키고 있고,
현재, 이 포인터를 써서 다음 int를 가리키게 하려고 합니다. 왜 이 코드가
동작하지 않을까요?
\begin{verbatim}
((int *)p)++;
\end{verbatim}
\A C 언어에서 캐스팅(casting)은 ``실제 비트들이 다른 타입인 것처럼
흉내내는 것''이 아닙니다; 캐스팅은 변환(conversion) 연산이며,
정의에 의해 (대입되거나, \verb2++2를 써서 증가될 수
없는) \EM{rvalue}를 만들어 냅니다. (위와 같은 문장을 실수로 만들어냈던,
의도적으로 한 것이든, 또 설령 어떤 컴파일러가---오래된 컴파일러나, \TT{gcc}의
확장 기능---위와 같은
코드를 원하는 대로 코드를 만들어낸다고 해도 이것은 표준 기능이
아닙니다.
% TODO: 번역 맞는지 확인할 것
% (It is either an accident
% or a delibrate but nonstandard extension if a particular
% compiler accepts expressions such as the above.)
따라서 다음과 같이 하는 것이 옳습니다:
\begin{verbatim}
p = (char *)((int *)p + 1);
\end{verbatim}
\noindent (\verb+p+가 \verb+char *+이기 때문에)
또는 간단히 다음과 같이 할 수 있습니다:
\begin{verbatim}
p += sizeof(int);
\end{verbatim}
또는, (좀 더 복잡하게 직접 캐스팅을 하는) 다음과 같이 할 수도 있습니다.
\begin{verbatim}
int *ip = (int *)p;
p = (char *)(ip + 1);
\end{verbatim}
될 수 있으면 캐스팅 대신 올바른 타입의 포인터를 쓰는 것이 바람직하다는
것을 염두에 두시기 바랍니다.
\seealso{\ql{16.7}}
\R
\cite{kr2} \S\ A7.5 \page{205} \\
\cite{ansi} \S\ 3.3.4 (esp. footnote 14) \\
\cite{c89} \S\ 6.3.4 \\
\cite{rationale} \S\ 3.3.2.4 \\
\cite{hs} \S\ 7.1 \Page{179--80}
\end{faq}
\begin{faq}
\Q{4.6} % 4.6 COMPLETE
왜 \TT{void *}에 사칙연산을 쓸 수 없나요?
\A
질문 \ql{11.24}를 보기 바랍니다.
\end{faq}
\begin{faq}
\Q{4.7} % 4.7
외부에서 얻은 구조체를 분석하는 코드를 얻었는데, 실행하면 ``unaligned address''라는
메시지를 출력하고 끝납니다. 이게 무슨 뜻인가요?
\A
질문 \ql{16.7}을 보기 바랍니다.
\end{faq}
\section{Pointers as Function Parameters}
% from 4.8
\begin{faq}
\Q{4.8} % 4.8
포인터를 인자로 받는 함수를 만들고, 그 함수에서 이 포인터를
초기화하는 함수를 만들었습니다:
\begin{verbatim}
void f(ip)
int *ip;
{
static int dummy = 5;
ip = &dummy;
}
\end{verbatim}
\noindent 그러나 다음과 같이 호출했을 때, 포인터의 값은 변경되지
않는군요.
\begin{verbatim}
int *ip;
f(ip);
\end{verbatim}
\A
C 언어는 인자를 전달할 때, `call by value' 방식을 쓴다는 것을
기억하기 바랍니다. 위의 함수에서는 단지 복사된 포인터를 변경하기 때문에
실제 인자로 전달한 \verb+ip+는 변경되지 않습니다. 따라서
이 문제를 해결하려면 함수가 포인터의 주소를 받을 수 있도록
(즉, 인자가 포인터를 가리키는 포인터(pointer-to-pointer)) 만들거나,
함수가 포인터를 리턴하도록 해야 합니다.
\begin{verbatim}
void f(ipp)
int *ipp;
{
static int dummy = 5;
*ipp = &dummy;
}
...
int *ip;
f(&ip);
\end{verbatim}
\noindent 이 경우, 우리는 `pass by reference'를 흉내내는 효과를 얻습니다.
또 다른 방법은 포인터를 리턴하는 함수를 만드는 것입니다:
\begin{verbatim}
int *f()
{
static int dummy = 5;
return &dummy;
}
\end{verbatim}
질문 \ql{4.9}와 \ql{4.11}을 참고하기 바랍니다.
\end{faq}
\begin{faq}
\Q{4.9} % 4.9 COMPLETE
함수가 범용 포인터(generic pointer)를 레퍼런스(reference)로 받게
하기 위해 다음과 같이 \verb+void **+ 포인터를 써도 되나요?
\begin{verbatim}
void f(void **);
double *dp;
f((void **)&dp);
\end{verbatim}
\A
이식성이 없습니다. 위와 같은 코드는 동작할 수도 있고, 때때로 좋은 코드로
여겨지기도 하지만, 이 코드는 ``모든 포인터 타입이 실제로 같은 방식으로 표현된다''를
전제로 하고 있으며, 이 전제는 대부분의 시스템에서 참이지만 그렇지 않은 경우도 있습니다.
질문 \ql{5.17}을 참고하기 바랍니다.
C 언어에서 범용의 포인터를 가리키는 포인터
(pointer-to-pointer)는 존재하지 않습니다. \verb+void *+가 범용의
포인터로 쓰이는 이유는, 단지 다른 포인터 타입의 값에 대입하거나,
대입될 때, 자동으로 변환(conversion)이 일어나기 때문입니다;
이러한 변환은 \TT{void **}에, \TT{void *}가 아닌 다른 포인터
타입을 대입하거나, 대입될 때는 일어나지 않습니다.
여러분이 \TT{void **}를 쓸 때 (예를 들어 \TT{void **}에
\TT{*} 연산을 써서 원래의 \TT{void *}를 얻으려고 할 때),
컴파일러는 \TT{void *}가, 다른 포인터 타입에서
변환되어서 온 것인지를 알 지 못합니다. 그래서 컴파일러는 말
그대로 \TT{void *}로 인식합니다; 다른 형태의 implicit conversion을
전혀 수행할 수 없습니다.
즉, 여러분이 작업하고자 하는 \TT{void **}는 반드시
어딘가에 실제로 있는, \TT{void *} 값을 가리키고 있는 포인터이어야
합니다. \verb+(void **)&dp+와 같은 코드는,
강제로 캐스팅하므로 컴파일러가 경고를 출력하지 않지만,
이식성이 떨어지며, 때때로 원하는 결과를 얻지 못할 수 있습니다;
질문 \ql{13.9} 참고. 만약 \TT{void **}가 가리키고
있는 포인터가 \TT{void *}가 아닐 경우, 그리고 그 포인터가
\TT{void *}와는 다른 크기나 내부 표현을 (internal representation)
쓰고 있을 경우, 컴파일러는 그 값을
올바른 방법으로 얻지 못합니다.
질문에 주어진 코드를 동작하게 만들려면, \TT{void *} 타입의
임시 변수를 만드는 것이 바람직합니다:
\begin{verbatim}
double *dp;
void *vp = dp;
f(&vp);
dp = vp;
\end{verbatim}
변수 \TT{vp}에 할당하고, 얻어내는 방식을 쓰면, 컴파일러가 (필요한 경우) 적당하게
conversion을 수행할 수 있습니다.
다시 말하지만, 이 모든 논쟁은, 포인터가 타입별로 크기나 표현 방식이 다르다는 가정 아래에서
이뤄진 것이며, 최근의 시스템에서는 이런 경우가 드뭅니다. \TT{void **}에서 발생할 수
있는 문제를 좀 더 쉽게 알기 위해, 다음과 같은 상황을 생각해 봅시다. 일단 \TT{int}와
\TT{double}의 크기와 내부 표현 방식이 다르다고 하고, 다음과 같은 함수를 보기 바랍니다.
\begin{verbatim}
void *incme(double *p)
{
*p += 1;
}
\end{verbatim}
그리고 나서 다음과 같은 코드를 실행합니다:
\begin{verbatim}
int i = 1;
double d = i;
incme(&d);
i = d;
\end{verbatim}
당연히, \TT{i}는 1만큼 증가합니다. (이 것은 앞에서 \TT{vp}라는 임시 변수를 써서
설명했던 \TT{void **} 코드와 같은 방식을 쓴 것입니다.) 이와 달리, 아래와 같은 코드를
실행했다고 생각해 보기 바랍니다:
\begin{verbatim}
int i = 1;
incme((double *)&i) /* WRONG */
\end{verbatim}
이 것은, 앞에서 동작하지 않는다고 말했던 (질문에 나온) 코드와 일치합니다.
\end{faq}
\begin{faq}
\Q{4.10} % 4.10 COMPLETE
다음과 같은 함수가 있을 때:
\begin{verbatim}
extern int f(int *);
\end{verbatim}
\noindent 상수(constant)의 레퍼런스를 전달할 수 있나요? 다음과 같이
해 봤지만 잘 되질 않습니다:
\begin{verbatim}
f(&5);
\end{verbatim}
\A
직접적으로 할 수는 없습니다. 일단 임시 변수를 선언하고, 이 변수의
주소를 함수에 전달해야 합니다:
\begin{verbatim}
int five = 5;
f(&five);
\end{verbatim}
일반적으로, 어떤 간단한 값이 아닌, 어떤 값을 가리키는 포인터를 인자로 받는 함수들은
대개, 그 값을 변경하기 위해서 포인터를 인자로 받습니다. 따라서 상수에 대한 포인터를
전달하는 것은 좋은 방식이라고 말하기 곤란합니다. 만약 \TT{f}가 정말로 \TT{int *}를
인자로 받는 함수로 선언되어 있었다면, \TT{const int}에 대한 포인터를 넘겨 주기 전에
먼저 그 함수가 어떤 일을 하는지 분석해 보아야 합니다. (주어진 함수가 \TT{int *}가 아닌
\TT{const int *}를 받는 것으로 선언되어 있었다면, 그 함수 내부에서 인자를 변경하지
않는다는 것이 보장되므로, 안전합니다.)
\noindent 질문 \ql{2.10}, \ql{4.8}, \ql{20.1}을 참고하기 바랍니다.
\end{faq}
\begin{faq}
\Q{4.11} % 4.11 COMPLETE
C 언어에 ``pass by reference''가 존재하나요?
\A
존재하지 않는다고 할 수 있습니다. 엄밀히 말해 C 언어는 무조건
`call by value'만 사용합니다. 단, 원하는 타입의 포인터 타입을
인자로 받고, \verb+&+ 연산자를 써서 주소를 전달함으로써,
`pass by reference'를 흉내낼 수는 있습니다.
또한 배열을 인자로 전달할 경우에는 컴파일러가 자동으로 이 작업을
해 줍니다 (배열 대신 포인터를 사용하는 것은 질문 \ql{6.4}를 참고하기
바랍니다). 어쨋든, C 언어는 진정한 `pass by reference'나
C++의 레퍼런스 파라메터(parameter)와 같은 것은 없습니다.
(그러나 매크로 함수들은 ``pass by name''을 제공합니다.)
\seealso{\ql{4.8}, \ql{7.9}, \ql{12.27}, \ql{20.1}}
\R \cite{kr1} \S\ 1.8 \Page{24--5}, \S\ 5.2 \Page{91--3} \\
\cite{kr2} \S\ 1.8 \Page{27--8}, \S\ 5.2 \Page{95--7} \\
\cite{ansi} \S\ 3.3.2.2, esp. footnote 39 \\
\cite{c89} \S\ 6.3.2.2 \\
\cite{hs} \S\ 9.5 \Page{273--4}
\end{faq}
\section{Miscellaneous Pointer Use}
% from 4.12
\begin{faq}
\Q{4.12}
함수를 호출할 때, 포인터를 써서 호출하는 방식을 봤습니다.
왜 이런 일을 하는 거죠?
\A 원래, 함수를 가리키는 포인터는 \verb+*+ 연산자를 쓸 경우 (그리고
우선 순위를 위해 괄호를 같이 쓸 경우) 진짜(real) 함수 호출로
변경됩니다:
\begin{verbatim}
int r, func(), (*fp)() = func;
r = (*fp)();
\end{verbatim}
위 코드의 마지막 줄의 의미는 다음과 같습니다: \TT{fp}는 함수를 가리키는 포인터이고,
\TT{*fp}는 이 함수를 뜻합니다. 그리고 뒤따르는 \TT{()}는 함수 호출에서 쓰이는
인자를 받는 인자 list입니다. 이때 연산자 우선 순위를 고려해서 \TT{*fp}를 괄호로
둘러쌉니다. 이러면 완벽한 함수 호출이 됩니다.
다음과 같은 이론도 있습니다:
함수는 항상 포인터를 써서 불려지고, 수식에서 ``진짜'' 함수 이름은, 초기화에서
이루어지는 것처럼, 항상 함축적으로 포인터로 변환됩니다 (decay): 질문 \ql{1.34} 참고.
이 이유는 실제로 \TT{pcc}에서부터 널리 퍼졌고, ANSI 표준에 채택되었습니다. 따라서
\TT{fp}가 위와 같이, 함수를 가리키는 포인터일 때, \TT{r = fp();}는 올바른
표현입니다. (이렇게 쓰는 것은 전혀 혼동스러워 할 필요가 없습니다. 왜냐하면, 함수 이름
뒤에 인자 리스트가 따라올 때, 함수 호출 이외에 다르게 쓰이는 경우가 없기 때문입니다.)
직접 \TT{*}을 써서 함수 호출하는 것은, 오래된 컴파일러로 이식이 필요한 경우에 많이
쓰입니다.
\seealso{\ql{1.34}}
\R \cite{kr1} \S\ 5.12 \page{116} \\
\cite{kr2} \S\ 5.11 \page{120} \\
\cite{ansi} \S\ 3.3.2.2 \\
\cite{c89} \S\ 6.3.2.2 \\
\cite{rationale} \S\ 3.3.2.2 \\
\cite{hs} \S\ 5.8 \page{147}, \S\ 7.4.3 \page{190}
\end{faq}
\begin{faq}
\Q{4.13} % 4.13 COMPLETE
완전하게 `generic'한 포인터 타입이 있나요? 제가 쓰는 컴파일러는 함수 포인터를
\TT{void *}로 변환하려고 하니 경고가 발생합니다.
\A
완벽하게, 아무때나 쓸 수 있는 `total generic'한 포인터 타입은 존재하지 않습니다.
\TT{void *}는 아무 오브젝트(또는 데이터) 포인터를 저장할 수 있도록 보장된 포인터입니다;
따라서 함수 포인터를 \TT{void *}로 변환하는 것은 이식성이 떨어집니다. (어떤 시스템들에서,
함수에 대한 주소값은 매우 큰 값입니다---모든 데이터 포인터가 가질 수 있는 값보다 훨씬 큰)
그렇지만, 모든 함수 포인터들은, 쓰이기 전에 원래의 타입으로 다시 변환된다는 가정 아래에서,
서로 다른 함수 포인터를 가리킬 수 있도록 보장되어 있습니다. 따라서 아무런 함수 포인터 타입을
하나 정해서 (일반적으로 \TT{int (*)()}나 \TT{void (*)()}. 즉 \TT{int}나
\TT{void}를 리턴하는 함수 포인터), 'generic'한 함수 포인터로 쓸 수 있습니다.
만약, 데이터와 함수를 구별하지 않는 포인터가 필요하다면, 그리고 이식성이 중요하다면,
union을 써서, 한 멤버는 \TT{void *}로, 다른 하나는 (앞에서 말한) 함수 포인터로
만들어 씁니다.
\seealso{\ql{1.22}, \ql{5.8}}
\R
\cite{ansi} \S\ 3.1.2.5, \S\ 3.2.2.3, \S\ 3.3.4 \\
\cite{c89} \S\ 6.1.2.5, \S\ 6.2.2.3, \S\ 6.3.4 \\
\cite{rationale} \S\ 3.2.2.3 \\
\cite{hs} \S\ 5.3.3 \page{123}
\end{faq}
\begin{faq}
\Q{4.14} % 4.14
정수값을 포인터로 변환하거나, 그 반대로 해도 상관없나요? 일시적으로 정수 값을 포인터에
저장하거나, 포인터를 정수 타입의 변수에 저장해도 되나요?
\A
예전에는, 포인터가 (비록 \TT{int}인지 \TT{long}인지는 알 수 없으나) 정수로
변환될 수 있다는 것이 보장되어 있었습니다. 즉, 정수가 포인터 값으로 변경되고, 다시
나중에 이 포인터 값을 (충분히 큰) 정수로 바꾸는 등의 변환이 이루어졌습니다. (물론
이런 변환은, 시스템의 주소 구조를 잘 알고 있는 사람들이 써 왔습니다.)
다시 말하면, 이러한 정수/포인터 변환은 선례가 많고 예전부터 지원되어 왔으나, 항상
시스템 의존적인 부분이므로, 이식성이 없습니다. 그리고 이런 변환에는 (비록 오래된 컴파일러들은
경고를 보여주지 않을 수 있지만) 항상 직접 캐스팅해 주어야 합니다.
ANSI/ISO C 표준은, C 언어가 널리 구현되기를 바라는 목적에서, 이러한 정수/포인터
변환 보장을 약화시켰습니다. 따라서 포인터를 정수로 변환하거나, 정수를 포인터로 변환하는 것은
항상 `implementation-defined'입니다. (질문 \ql{11.33} 참고), 그리고 값의 변환없이
이 변환이 이루어진다는 것도 보장하지 않습니다.
(즉, 변환 도중, 값이 바뀔 수도 있다는 말입니다.)
따라서 포인터를 정수로 변환하거나, 정수를 포인터로 변환하는 것은 좋은 습관이 아닙니다.
만약, 정수 또는 포인터를 저장해야 하는 타입이 필요하다면 union을 쓰기 바랍니다.
\seealso{\ql{5.18}, \ql{19.25}}
\R
\cite{kr1} \S\ A14.4 \page{210} \\
\cite{kr2} \S\ A6.6 \page{199} \\
\cite{ansi} \S\ 3.3.4 \\
\cite{c89} \S\ 6.3.4 \\
\cite{rationale} \S\ 3.3.4 \\
\cite{hs} \S\ 6.2.3 \page{170}, \S\ 6.2.7 \Page{171--2}
\end{faq}
%
% Local Variables:
% coding: utf-8
% fill-column: 78
% End:
%