라인 2: char *p1;
p1 = malloc( 10 );
p1 = "1234567890";
라인1로 했을때와 라인2로 했을때의 차이점이 무엇입니까?
경험상 보면 라인 1과 같이 하는 경우는 p1 변수에 들어갈 값이
작은 경우이며..만일 큰 데이터를 넣는데 사용할 변수인데 malloc을 사용하지
않으면 포인터 에러가 나더군요.. 그래서 큰 데이터를 넣고 사용하는 경우라면
malloc을 사용해야 하더군요..
그래서 저는 그렇게 생각했습니다. "라인 1과 같이 할 경우 컴파일러가
일정한 크기의 영역을 자동으로 잡기때문에 간단하게 사용할때는 라인 1과 같이
사용하고, 넣을 값이 큰경우는 반드시 malloc을 사용해야한다" <-- 이말이
맞습니까?
맞다면? 도대체 어느정도 크기까지가 malloc으로 잡을 필요가 없는건가요?
(보통 라인1과 같이 할 경우 말록으로 안잡고 라인1과같이 그냥 넣고 printf
하잖아요..)
----------------------------------------------------------------------------
질문2
라인 1: char p1[10];
라인 2: char *p1;
p1 = malloc(10);
라인 1과 라인 2는 같은 말이죠? 그럼
라인 3: char p2[];
라인 4: char *p2;
라인 3과 라인 4는 같은 말인가요?
----------------------------------------------------------------------------
질문3
다음과 같은 소스가 있습니다. (전체코드는 아니고 필요한 부분만 넣었습니다.)
file://source start..
struct str_var
{
char *name;
char *value;
};
struct varentry vartable[1024];
void insert_value( const char *name, const char *val )
{
char *nptr, *vptr;
int i;
nptr = (char *) malloc( strlen(name)+1); <---- 이 부분과
vptr = (char *) malloc( strlen(val)+1); <---- 이 부분은 필요가 없는거
아닌가요?
strcpy( nptr, name );
strcpy( vptr, val );
for( i=0 ; i<1024 ; i++ )
{
if( vartable[i].name == NULL )
{
vartable[i].name = nptr;
vartable[i].val = vptr;
break;
}
}
}
int main( void )
{
querystr = (char *) malloc( size );
valuestr = (char *) malloc( size );
querystr에 값 집어넣는 코드;
valuestr에 값 집어넣는 코드;
insert_value( querystr, valuestr );
}
file://source end.
-------
nptr = (char *) malloc( strlen(name)+1); <---- 이 부분과
vptr = (char *) malloc( strlen(val)+1); <---- 이 부분은 필요가 없는거
아닌가요?
insert_value에 넘어가는 값(querystr, valuestr)은 malloc으로 범위를
잡았습니다.
그리구나서 insert_value에서 call by value로 name과 val에 주소를 넣죠?
즉, name과 val이 가리키는 것은 메모리 영역이 잡혀있는 어떤주소 영역을
가리키고 있겠죠?
그러므로 char *nptr, *vptr을 새로 메모리 할당 받아서 strcpy로 복사한다음
vartable[i].name, vartable[i].val에 넣는 동작을 할필요없이..
그냥 vartable[i].name = name; vartable[i].val = val; 이렇게 하면 되는거
아닌가여?
매개변수로 넘어온 name과 val은 이미 영역이 잡혀있는 안전한 포인터 아닙니까?
그런데 왜 그렇게 또 malloc을 잡고 저렇게 하죠?
혹시 나중에 다른 곳에서 쓰일것 을 위해 함수를 견고하게 만드느냐고 저렇게
한건가요?
아니면 다른 의미가 있는건가요?
=================================================================
헉헉.. 제 질문을 이해하셨는지 모르겠습니다.. ^^;;
그럼 답변 부탁드립니다.
> 질문1
> --------------------------------------------------------------------------
--
> 라인 1: char *p1 = "1234567890";
>
> 라인 2: char *p1;
> p1 = malloc( 10 );
> p1 = "1234567890";
>
[ 질문1]
unix, linux- gcc or g++, turbo c 아무데서나 compile해도 같습니다. 에러가
나지 않는 것이 정상입니다. 하나 referencing을 하기위해서는 메모리를 잡아주는
것이 바람직하답니다.
> --------------------------------------------------------------------------
--
> 질문2
>
> 라인 1: char p1[10];
> 라인 2: char *p1;
> p1 = malloc(10);
>
> 라인 1과 라인 2는 같은 말이죠? 그럼
>
> 라인 3: char p2[];
> 라인 4: char *p2;
>
> 라인 3과 라인 4는 같은 말인가요?
[질문2]
정확히 같은 notation입니다. 즉 *p2나 p2[]모두 array의 첫번째 시작 번지수를
나타냅니다. Base랍니다.
> --------------------------------------------------------------------------
--
> 질문3
>
> 다음과 같은 소스가 있습니다. (전체코드는 아니고 필요한 부분만 넣었습니다.)
>
> struct str_var
> {
> char *name;
> char *value;
> };
>
> struct varentry vartable[1024];
>
> void insert_value( const char *name, const char *val )
> {
> char *nptr, *vptr;
> int i;
>
> nptr = (char *) malloc( strlen(name)+1); <---- 이 부분과
> vptr = (char *) malloc( strlen(val)+1); <---- 이 부분은 필요가
없는거
>
> nptr = (char *) malloc( strlen(name)+1); <---- 이 부분과
> vptr = (char *) malloc( strlen(val)+1); <---- 이 부분은 필요가 없는거
> 아닌가요?
>
[질문3]
이건 call by reference와 관련된 것 같군요. 또 Aliasing도....
이와같이 새로이 Allocation을 해주고 같은 값을 string copy를 하는 이유는
전혀다른 범주의 데이터를 만들기 위함입니다.
>그냥 vartable[i].name = name; vartable[i].val = val; 이렇게 하면
되는거아닌가여? 이와 같은 경우에는 당근 segment 에러가 떨어지겠지요? 그리고
된다고 하더라도 copy를 해주지 않고 번지수를 직접주었을 때 문제가 발생합니다.
이것을 Aliasing이라고 합니다.
즉 주 함수에서 name이나 val에 새로운 값을 Assign하게되면 structure안의 값도
기냥 바뀝니다. 똑같은 주소를 서로 다른 name에서 referencing 하게 됩니다.(흑.
옛날에 graphics의 아픈 추억이.......)
>매개변수로 넘어온 name과 val은 이미 영역이 잡혀있는 안전한 포인터 아닙니까?
안전한 포인터입니다.
>그런데 왜 그렇게 또 malloc을 잡고 저렇게 하죠?
위에서 설명드린 것처럼 새로운 영역을 잡아서 copy하는 겁니다.
-------------------------
설명이 잘되었는지 모르겠군요. 졸음이 시나브로..................... 아 그리고
포인터만 관련된 책이 있습니다. 하두 오래되서 까먹었는데 그걸 함 보시면
천재되십니다. 주저리주저리 한말 끝냅니다.
>포인터만 관련된 책이 있습니다. 하두 오래되서 까먹었는데 그걸 함 보시면
>천재되십니다. 주저리주저리 한말 끝냅니다.
>
>
>
-----------------===== Posted via NetPle Usenet Service =====-----------------
http://news.NetPle.com 에 접속하시면 새로운 유즈넷이 열립니다.
사용할 데이터의 크기와 malloc은 아무 관계가 없습니다. 포인터는 그냥 메모리
어딘가를 가리키는 변수입니다. 그래서 1번의 경우에는 "1234567890"이라는
스트링이 저장되어 있는 데이터 영역을 가리키게 됩니다. 2번의 경우는
malloc()을 쓸 데 없이 한 것입니다. malloc()으로 얻어진 10바이트 데이터는
쓰레기가 되지요. 다음과 같은 예를 드는 것이 더 낳아 보입니다.
2-1:
char *p1, *p2;
p1 = "1234567890";
p2 = malloc(11);
strcpy(p2, p1);
이렇게 하면 p1은 "1234567890"이 있는 메모리의 맨 앞을 가리키고 p2는 힙 영역
어딘가를 가리키게 되어 p1과 p2는 서로 다른 곳을 가리키고 있지만, 가리키는
곳에 있는 값을 같게 됩니다.
>
> --------------------------------------------------------------------------
--
> 질문2
>
> 라인 1: char p1[10];
> 라인 2: char *p1;
> p1 = malloc(10);
>
> 라인 1과 라인 2는 같은 말이죠? 그럼
>
> 라인 3: char p2[];
> 라인 4: char *p2;
>
> 라인 3과 라인 4는 같은 말인가요?
>
>
전혀 다른 이야기입니다. 1에서 p1은 문자 배열이고, 2에서는 문자 포인터입니다.
배열과 포인터는 같은 semantics로 사용되는 경우도 있으나, 전혀 다른
semantics를 보이는 경우도 있기 때문에 이 둘은 같은 것으로 이해하면 오류가
발생할 가능성이 큽니다. 3은 ansi C에서는 쓰지 않는 표현입니다. 만약
3-1: extern char p2[];
4-1: extern char *p2;
이것이 같은 표현이냐고 물으신 것이라면, 그 경우에도 완전히 다른 것입니다.
이것은 포인터 레퍼런싱과 어레이 레퍼런싱이 다르기 때문에 발생하는 문제인데,
초보시라면 배열로 반드시 정의(definition)된 것은 배열로
선언(declaration)하고 포인터로 정의된 것은 포인터로 선언한다고 이해하시는
것이 좋습니다.
> --------------------------------------------------------------------------
이해하셔야 할 것은 포인터를 대입한다는 것은 같은 메모리를 가리키게 한다는
것을 의미하고, strcpy를 한다는 것은 한 포인터가 가리키는 메모리에 있는 값을
다른 포인터가 가리키는 메모리로 복사한다는 것을 의미합니다.
데이터를 한 번 더 집어넣는 경우를 생각해보시면 쉽습니다.
querystr = (char *) malloc( size );
valuestr = (char *) malloc( size );
querystr에 값 집어넣는 코드; 값 "a"
valuestr에 값 집어넣는 코드; 값 "b"
(1) insert_value( querystr, valuestr );
querystr에 다른 값 집어넣는 코드; 값 "c"
valuestr에 다른 값 집어넣는 코드; 값 "d"
(2) insert_value( querystr, valuestr );
님께서 말씀하신대로 포인터 대입을 한다면, (1)에서 값을 넣을 때
variable[0].name은 querystr가 같은 메모리를 가리키게 되고, variable[0].val은
valuestr과 같은 메모리를 가리키게 됩니다. (2)에서 값을 넣을 때에도
variable[0].name은 querystr가 같은 메모리를 가리키게 되고, variable[0].val은
valuestr과 같은 메모리를 가리키게 됩니다. 그럼 variable[0].name이 가리키는
메모리에는 무슨 값을 저장되어 있을까요? 그림을 그려보시면서 과정을 따라가면
쉽게 답을 내실 수 있을 것입니다.
----
Kim, Sung-Ryong
te...@oski.co.kr
http://www.oski.co.kr
--
______________________________________________________________
ARA Network Technologies Co., Ltd.
Jeong-Ho Jang
Research Engineer
Tel : +82-042-828-7770 (413), Fax : +82-042-828-7771
Mobile : +82-011-9400-8022
jhj...@aratech.co.kr
http://cestlavie.homeip.net
--------------------------------------------------------------
<love...@dreamwiz.com> wrote in message
news:9afdhu$u0d$1...@news.netple.com...
그렇군요..
그런데 한가지 이해가 가질 않는 부분이 있습니다.
[질문3]을 실험해 본결과 그냥 vartable[i].name = name; vartable[i].val = val;
--------------------------------
----------------
이렇게 해도 에러는 발생하지 않습니다.
그런데 님의 답변중에
> copy를 해주지 않고 번지수를 직접주었을 때 문제가 발생합니다.
> 이것을 Aliasing이라고 합니다.
이런 말이 있는데.. 번지수를 직접 주었을 때 어떤 문제가 발생한다는 말인가요?
죄송합니다만,, 좀 더 자세하게 설명 해 주실수 있습니까?
아니면.. 이 문제와 관련된 참고 서적이래도 알려주시겠습니까?
그럼.. 또 답변 부탁드립니다. ^^
그런데 한가지 질문이 있습니다.
님의 말씀중에 다음과 같은 말이 있습니다.
> > 라인 3: char p2[];
> > 라인 4: char *p2;
>
> 전혀 다른 이야기입니다. 1에서 p1은 문자 배열이고, 2에서는 문자
포인터입니다.
>배열과 포인터는 같은 semantics로 사용되는 경우도 있으나, 전혀 다른
>semantics를 보이는 경우도 있기 때문에 이 둘은 같은 것으로 이해하면 오류가
>발생할 가능성이 큽니다.
문자배열과 문자 포인터가 다르다는것은 어디서 들은 기억이 나는데..
어떻게 다른건지 좀더 자세하게 말씀해주실수 있나요? 제가 이제까지
사용한것들은
사용하는 방법이나 개념이 너무나도 동일해서 저는 포인터와 배열을 거의
동일하게
간주했었습니다. 도대체 어떻게 처리되길래 다르다고 말씀하시는 건가요?
위의 질문을 써 놓고 보니 제 글이 상당히 공격적인것처럼 보이네요..
용서하시구요. ^^
의도한건 아니구요.. 어떻게 다르게 표현할 방법이 없네요. ^^;;;
----
글구 저도 상당히 오랜기간(직장은 안다녔지만) 프로그램을 공부했다고
생각하는데
아직도 이런 문제에 부딫히면 머가먼지 모르겠습니다. 님은 도대체 C언어를
어떻게 공부하시나요? 시중에 나온 일반적인 C 책에는 저의 물음에 대답해 줄 수
있는
책을 보지 못했습니다. (물론 제가 찾지 못해서 그럴수도 있지만.. ^^;;)
C언어 표준 문서 같은 것들을 보시면서 습득하신건가요?
아닙니다. 동일한 notation이 아닙니다. 라인 3은 char 형 변수배열을 선언한
것이며,
라인 4는 char 형 포인터 변수를 선언한 것입니다. *p2나 p2[]가 모두 array의
첫번째 시작 번지
수를 나타낸다는 설명 또한 맞지 않습니다.
라인3을 정확히 표현하자면 char p2[];는 incomplete array type 으로 선언된
것입니다.
다음은 포인터와 문자열배열과 관련된 간단한 예 입니다 .
아래와 같이 선언되었다고 가정합니다.
char p1[] = "always writable";
char *p2 = "possible not writable ";
p1[0]='x'와 같은 할당식은 언제가 가능합니다. 그러나 p2[0]='x'와 같은
할당식은 가능할 수도 있으며, run-time error를 발생시킬 수도 있습니다. 즉,
운에 따라 다르며, 이와 같은 식에 의존하지 않는 것이 좋습니다.
이처럼 둘은 비슷해보이지만 전혀 다릅니다. 포인터와 문자열배열이 동일하다고
막연하게 개념정리하고 있다간, 위 와 같은 경우에서 좌절?하기 쉽죠...
배열과 포인터와의 연관관계가 혼동을 초래하는 개념이기 때문에 처음에 분명한
차이점을
두고 개념정리를 명확히 해주어야 합니다.
일단, 힙이니 스택이니 하는 개념은 무시하고, 표준에서 사용하는 3가지 기
억부류만으로 설명을 드리겠습니다. 후에 특정 시스템에 추상적 내용을 적
용시키면서 쉽게 이해하실 수 있으리라 믿습니다.
라인1: p1 라는 char * 형의 포인터를 선언합니다. "1234567890" 은 정적
기억부류 (static) 를 가지는 char 형의 배열 (array of char) 입니
다. *) 배열의 포인터 변환에 의해, 포인터 p1 에는 "1234567890"
중 첫번째 요소인 1 을 가르키는 포인터가 대입됩니다.
*) 또 역시나, 문자열 "상수" 인데, 어떻게 배열이 될 수 있느냐는 등의 항
의성 질문에 미리 답합니다. 전에도 이야기했지만 문자열 상수는 (사실
상, "상수" 라는 의미는 영어용어에는 없습니다. string literal 이죠)
C 에서 lvalue 입니다. 따라서, 아래와 같은 행동도 가능합니다.
printf("%c\n", "1234567890"[3]);
라인2: p1 이라는 char * 형의 포인터를 선언합니다. malloc 로 allocated
(사용자에 의해 할당되고, 사용자에 의해 해제되는) 기억부류를 갖
는 10 bytes 의 기억장소를 할당받아 그 첫번지를 p1 이 가르키도록
합니다. 그리고서는, 다시금 p1 에 위 라인1에서 했던 행동을 합니
다. 이는 malloc 로 할당받은 메모리를 잃어버린다는 것을 의미합니
다. (어떻게 p1 에 대입되었던 할당된 메모리의 번지를 복구해 내겠
습니까?)
>
>경험상 보면 라인 1과 같이 하는 경우는 p1 변수에 들어갈 값이
>작은 경우이며..만일 큰 데이터를 넣는데 사용할 변수인데 malloc을 사용하지
>않으면 포인터 에러가 나더군요.. 그래서 큰 데이터를 넣고 사용하는 경우라면
>malloc을 사용해야 하더군요..
제가 보기엔 위의 행동은 이 설명과 아무런 관계가 없습니다. 님이 의미하
는 것이 무엇인지는 알겠지만, 위와 같은 행동은 10 bytes 의 메모리를 버
리는 결과만을 초래합니다.
이는 표준에서 요구하는 바는 아니지만, 대부분의 시스템에서는 정적 기억
부류를 갖는 대상체를 저정하는 메모리와 allocated 기억부류를 갖는 대상
체를 저장하는 메모리를 따로 두며, 이 메모리에 허락되는 크기역시 다릅니
다. (일부 환경에서는 메모리 모델이라는 것을 사용해서, 이 크기들을 자신
의 프로그램에 최적화시킬 수 있도록 하고 있습니다) 따라서, allocated
기억부류를 갖는 대상체를 위한 메모리가 더 크게 허락된다면, 님이 말씀하
셨듯이 정적 배열등으로 대상체를 다룰 때보다 더 넉넉한 메모리를 얻을 수
있기도 합니다. 하지만, 이러한 사항들은 환경에 의존적인 것이며 표준에서
는 어떠한 정의나 요구도 하고 있지 않습니다. 따라서, 늘 그렇다고 일반화
해서는 안됩니다. *)
*) 표준은 하나의 strictly conforming (가장 이식성이 큰) program 이 가
질 수 있는 정적 대상체의 크기를 명시해두어, 모든 표준을 따르는
implementation 이 최소한 그것만큼은 지원하도록 하고 있습니다.
[Translation limits] 하지만, 지극히 최소한의 요구이기 때문에, 대부
분의 implementation 은 보통 그보다 훨씬 많은 여유를 두고 있습니다.
>
>그래서 저는 그렇게 생각했습니다. "라인 1과 같이 할 경우 컴파일러가
>일정한 크기의 영역을 자동으로 잡기때문에 간단하게 사용할때는 라인 1과 같이
>사용하고, 넣을 값이 큰경우는 반드시 malloc을 사용해야한다" <-- 이말이
>맞습니까?
>맞다면? 도대체 어느정도 크기까지가 malloc으로 잡을 필요가 없는건가요?
님이 작업을 하고 있는 환경에 따라 많이 달라집니다. 심지어는 동일한 환
경에서라도, 메모리가 어떠한 상황에 있느냐에 따라서도 달라질 수 있습니
다. 따라서, 명시적으로 이만큼은 항상 가능하다고 말씀드릴 수는 없으며,
더 자세한 사항은 님의 프로그래밍 환경을 구성하는 요소들에 의해 정의됩
니다. C 표준은 malloc 류의 메모리 할당 함수에 의해 할당되는 메모리의
속성, 그와 관련된 포인터의 값, 할당의 실패여부등에 대한 정의만을 하고
있습니다.
>(보통 라인1과 같이 할 경우 말록으로 안잡고 라인1과같이 그냥 넣고 printf
>하잖아요..)
>
>----------------------------------------------------------------------------
>질문2
>
> 라인 1: char p1[10];
> 라인 2: char *p1;
> p1 = malloc(10);
>
>라인 1과 라인 2는 같은 말이죠? 그럼
아닙니다. 같은 말이라면, 가각 위와 같이 선언하고 아래의 행동이 모두 가
능해야 겠지만 그렇지 않습니다. 이유는 아래 Kong Hyeog Jun 님의 답변을
참고하시기 바랍니다.
char array[10];
[라인1 혹은 라인2가 나옴]
p1 = array; /* 라인1은 불가능하지만, 라인2일 경우는 가능 */
>
> 라인 3: char p2[];
> 라인 4: char *p2;
>
>라인 3과 라인 4는 같은 말인가요?
>
Kong Hyeog Jun 님의 답변을 참고하세요.
--
Jun Woong (myco...@hanmail.net)
Dept. of Physics, Univ. of Seoul
Cell: +82 16 467 6247
Web : http://c-expert.uos.ac.kr (Korean only)
아래, 장정호님의 답변중에서 장정호님이 가정하신 상황을 참고하시기 바랍
니다. 님이 전체 소스가 아닌 부분적인 소스만을 인용하셨기 때문에, 저를
포함한 대부분의 분들은 2-3번째 입력은 main() 안에서 더이상 malloc 를
통해 메모리를 할당받지 않고 이미 첫번째 입력에서 할당받은 메모리를 그
대로 사용하여 insert...() 함수를 이용한다고 생각하고 있습니다. 장정호
님의 가정을 따라서 메모리 상황을 그림으로 그려가면서 이해해 보시기 바
랍니다.
물론, p1 의 경우 p1 의 요소를 수정하는 행동이 undefined 이지만, p2 의
경우에는 지극히 정의된 행동을 하는 차이가 있지요.
>>
>> --------------------------------------------------------------------------
>--
>> 질문2
>>
>> 라인 1: char p1[10];
>> 라인 2: char *p1;
>> p1 = malloc(10);
>>
>> 라인 1과 라인 2는 같은 말이죠? 그럼
>>
>> 라인 3: char p2[];
>> 라인 4: char *p2;
>>
>> 라인 3과 라인 4는 같은 말인가요?
>>
>>
>
>전혀 다른 이야기입니다. 1에서 p1은 문자 배열이고, 2에서는 문자 포인터입니다.
>배열과 포인터는 같은 semantics로 사용되는 경우도 있으나, 전혀 다른
>semantics를 보이는 경우도 있기 때문에 이 둘은 같은 것으로 이해하면 오류가
>발생할 가능성이 큽니다. 3은 ansi C에서는 쓰지 않는 표현입니다. 만약
표준에서도 충분히 정의하고 있는, 아주 잘 쓰는 표현입니다. 아래 코드를
-ansi, -pedantic -Wall 옵션으로 gcc 로 컴파일 해보시기 바랍니다.
(물론, 그렇다고 해서 gcc 가 C90 이나 C99 표준을 완벽해 따른다는 것은
아닙니다)
#include <stdio.h>
char p2[]; /* line A */
int main(void)
{
extern char p2[10];
printf("%lu\n", (unsigned long)sizeof(p2));
return 0;
}
line A 에서, p2 의 type 은, array of char 이며, 그 크기를 알 수 없기에
incomplete type 입니다. 블럭안에서 p2 의 추가적인 선언에 의해 composite
type 을 만들게 됩니다.
뚜렷한 증거가 없을 때는, 주장의 근거로 표준을 사용하는 일이 없었으면
합니다. 근거로서 표준만큼 중대성을 갖는 것도 없지만, 그릇된 내용을 주
장할 때 뒷받침된다면 문제도 그만큼 커진다고 생각합니다.
>
>3-1: extern char p2[];
>4-1: extern char *p2;
>
>이것이 같은 표현이냐고 물으신 것이라면, 그 경우에도 완전히 다른 것입니다.
>이것은 포인터 레퍼런싱과 어레이 레퍼런싱이 다르기 때문에 발생하는 문제인데,
>초보시라면 배열로 반드시 정의(definition)된 것은 배열로
>선언(declaration)하고 포인터로 정의된 것은 포인터로 선언한다고 이해하시는
>것이 좋습니다.
단, 함수의 매개변수에서는 매개변수 다시쓰기 (rewrite) 가 일어나기
때문에 어떠한 차이도 없습니다. 위의 경우 말씀하신 array referencing
등으로 무엇을 의미하시려 하는 것인지는 모르겠지만, 위 각각의 수식만
으로는 아무런 문제도 없습니다. 다만, 전혀 다른 type 을 갖는 것 뿐이며,
사용자가 그 차이를 올바르게 구별하지 못할 때 문제가 발생하는 것 입니다.
[...]
에러는 없지만, 메모리 10 bytes 를 쓸데없이 낭비하는 코드입니다.
님이 의미하시는 referencing 이 정확히 무엇을 의미하는지는 모르겠지만,
위와 같은 상황에서는 메모리를 잡아주는 것이 아무런 좋은 의미를 갖지
못합니다.
>>
>> 라인 1: char p1[10];
>> 라인 2: char *p1;
>> p1 = malloc(10);
>>
>> 라인 1과 라인 2는 같은 말이죠? 그럼
>>
>> 라인 3: char p2[];
>> 라인 4: char *p2;
>>
>> 라인 3과 라인 4는 같은 말인가요?
>
>[질문2]
>정확히 같은 notation입니다. 즉 *p2나 p2[]모두 array의 첫번째 시작 번지수를
>나타냅니다. Base랍니다.
그렇지 않습니다. 다른 답변에서 다른 분이 충분히 설명해 드렸습니다. 물
론, 라인1과 라인2 역시 다른 말입니다.
[질문3 생략]
배열과 포인터는 type 자체가 다릅니다. 따라서, 님이 C 언어의 다른 부분
을 정확하게 이해하고 계시고 그 내용만을 그대로 적용한다면 혼동의 여지
가 없습니다. 초보들이 배열과 포인터의 관계에 대해서 어려워 하는 이유는
배열, 포인터 자체의 문제가 아니라 (아래에 적겠지만 배열과 포인터의 변
환 관계는 매우 간단합니다) C 의 다른 부분의 지식이 부족하기 때문입니다.
배열은 대부분의 문맥에서 그 첫번째 요소를 가르키는 포인터로 decay 합니
다. 또한 한번 decay 한 배열은 다시는 배열로 돌아올 수 없습니다. 아래는
배열이 포인터로 변환되는 상황에 대한 설명입니다.
array of type 은 sizeof, 단항 & 연산자의 피연산자인 경우를 제외하고는
그 요소중 첫번째를 가르키는 포인터 pointer to type 으로 변환됩니다.
(또한, 문자배열 array of char 이나 wide character 의 배열 array of
wchar_t 를 초기화하는 (wide) string literal 역시 포인터로 decay 하지
않습니다 - 제가 다른 글에서 string literal 이 분명 배열이라고 말씀드
렸죠?)
따라서, pointer to char 의 크기가 4 bytes 라고 가정한다면, 다음 수식의
결과는 이렇습니다. (제 위의 설명을 잘 참고하십시요)
char s[10];
sizeof(s); /* 10 */
sizeof(&s[0]); /* 4 */
sizeof(s+0); /* 4 */
sizeof(*&s); /* 10 */
C 의 다른 부분에 대한 지식이 부족해서 혼동하는 경우를 생각해 보지요.
배열은 incomplete type 을 요소로 가질 수 없습니다. 하지만 포인터는 가
르켜지는 type 으로서 incomplete type 을 가질 수 있지요. 따라서,
struct foo; /* incomplete type */
typedef struct foo foo_type;
foo_type array[10]; /* invalid! */
foo_type *pointer; /* valid */
하지만, 함수의 매개변수 선언에서는 "유효한" 배열형은 포인터형으로 다시
쓰여집니다 (rewrite). 따라서,
int func(char array[]);
int func(char *array);
int func(char array[10]);
int func(char array[20]);
위의 모든 함수원형선언은 호환되며 같은 composite type 을 만듭니다.
이외에도, 만들려고 마음만 먹으면 몇개의 상황이라도 가능합니다. 문제는
이러한 것들을 case by case 로 해결하려고 해서는 안된다는 것입니다. (이
이야기는 함수형과 함수 포인터에 대한 설명을 하면서도 나왔던 말인 것 같
군요) C type 체계에 대한 전체적인 이해와 배열과 포인터의 정확한 type
구분만 이루어진다면, 위와 같은 상황은 "케잌 한조각" ^^ 입니다.
>
>어떻게 공부하시나요? 시중에 나온 일반적인 C 책에는 저의 물음에 대답해 줄 수
>있는
>책을 보지 못했습니다. (물론 제가 찾지 못해서 그럴수도 있지만.. ^^;;)
예, 님이 찾지 못한 것이지요. :)
대부분의 책들 역시 어설프게 공부한 사람들이 써내서 그렇습니다.
>
>C언어 표준 문서 같은 것들을 보시면서 습득하신건가요?
>
C 표준 문서는 물론 이를 가장 명확하게 정의하고 있지만, 매우 기술적인
문서이기 때문에, 단순히 배열과 포인터의 관계만을 학습하기 위해 해석하
기 어렵고 내용도 방대한 표준을 본다는 것은 낭비일 수 있습니다. 아래는
comp.lang.c 그룹의 공식 FAQ (자주 물어보는 질문들) 입니다. 내용중 님이
궁금해 하시는 배열과 포인터의 관계에 대해서 다룬 부분이 있습니다. 참고
하시기 바랍니다. (물론, 영문입니다. 한글 버젼도 있지만, 저는 추천하지
는 않습니다 - 보고 안보고는 자유겠지만...)
http://www.eskimo.com/~scs/C-faq/top.html
그럼...
[질문 1]과 관계된 말들
> 라인 1: char *p1 = "1234567890";
>
> 라인 2: char *p1;
> p1 = malloc( 10 );
> p1 = "1234567890";
그렇습니다. 절대로 에러가 나지 않습니다. 이건 String이기 때문에 가능한
것입니다. 그림을 그려보시면 쉽게 아실 겁니다.
(그림을 아스키로........... --; 생략)
그런데... 대부분의 사람들이 알면서도 실수 하는 것은 다음과 같은 경우입니다.
[error code]
int *p1;
*p1=100;
가장 위험하면서도 빠지기 쉬운 오류... 이걸 그림으로.............할까나?
음................ 죄송...
그림이 힘든 관계로 바른 코드를 써보겠습니다. 죄송
[correct code]
int x, *p1;
p&=x; file://memory allocation
*p=100;
이와같인 주소를 referencing한 후 값을 집어넣어주는 습관을 길러주십시오.
[질문 2]와 관계된 말들
C에서 지원하는 SemiDynamic Array - int p[]-를 어떻게 컴파일러가 어떻게
인식할까요?
그렇습니다. 컴파일러는 아무생각없이 int p[]를 int *p 선언으로 변경합니다.
(어떤 책을 참조하셔도 될겁니다. Neill Graham이 쓴 책을 보십시오). 따라서 int
p[]나 int *p나 같은 말입니다.
사실 incomplete array type 비완성 배열(?)이라는 것은 그 시점에서는 Array가
아니라는 것을 주의하십시오. 저는 배열이 된 시점에서 드린 이야기가 아닙니다.
저도 공부하는 의미에서 array와 pointer가 같을 때와 다를 때를 정리해 보도록
하겠습니다.
- array와 pointer가 같은 경우
1. expression 안의 array 이름은 그 array의 첫번째 항목을 가리키는 포인터로
취급된다.
예1)
int a[10], *p;
p = a;
2. array의 첨자(subscript)는 포인터 오프셋과 동일하다.
예2)
int a[10], *p, i;
p = a;
for (i = 0; i < 10; i++) a[i] = 0;
for (i = 0; i < 10; i++) p[i] = 0;
for (i = 0; i < 10; i++) *(p+i) = 0;
for (i = 0; i < 10; i++) *p++ = 0;
위에 있는 네개의 for 문은 모두 동일한 결과를 가져옵니다. (물론 컴파일러가
만든 코드는 다를 수 있습니다만)
3. 함수의 인자로 선언된 array 이름은 그 array의 첫번째 항목을 가리키는
포인터로 취급된다.
예3)
void func(int *para) {}
void func(int para[]) {}
void func(int para[100]) {}
세개의 함수 정의 모두가 동일합니다.
- array와 pointer가 다른 경우
원래 type 자체가 틀리므로 위의 경우를 제외하고는 둘은 서로 다른 것입니다만,
(물론 현실에서는 위의 경우들이 대부분이지만 ^^) 가장 많이 틀리는 부분만 좀
적어보록 하겠습니다.
1. array 이름은 l-value가 될 수 있지만 unmodifiable이다.
예4)
int a[10], *p;
a = p; /* 이런 일을 하는 것은 불가능합니다. */
2. referencing
어레이의 위치는 컴파일을 할 때 결정됩니다. 따라서 어레이의 어떤 요소를
읽어오기 위해 컴파일러가 어레이 위치를 가져올 필요가 없습니다. 하지만
포인터가 가리키는 값을 알기 위해서는 indirect referencing을 해야합니다.
예5)
char a[10] = "123456789";
char *p = "123456789";
char c;
(5-1) c = p[4];
(5-2) c = a[4];
(5-1)의 경우에는 컴파일러는 p의 위치만을 알고 있습니다. (예를 들어
4000번지라고 하지요) 1) 먼저 4000 번지에 저장된 값을 읽어옵니다. 이 값은 이
포인터가 가리키는 메모리 위치입니다. (예를 들어 5000번지라고 하지요) 2)
다음으로 5000에 4를 더해 5004를 만들고, 3) 마지막으로 5004 번지에 저장된
값을 읽어다 c에 넣습니다.
(5-2)의 경우에는 컴파일러가 a의 위치를 알고 있습니다. (예를 들어
4500번지라고 하지요) 1) 4500에 4를 더해 4504를 만들고, 2) 4504 번지에 있는
값을 읽어다 c에 넣습니다.
이런 레퍼런싱의 차이로 인해서 다음과 같은 코드는 실행시 오류를 내게 됩니다.
예6)
file A:
int a[10];
file B:
extern int *a;
int c;
c = a[10];
>
> ----
>
> 글구 저도 상당히 오랜기간(직장은 안다녔지만) 프로그램을 공부했다고
> 생각하는데
>
> 아직도 이런 문제에 부딫히면 머가먼지 모르겠습니다. 님은 도대체 C언어를
>
> 어떻게 공부하시나요? 시중에 나온 일반적인 C 책에는 저의 물음에 대답해 줄
수
> 있는
>
> 책을 보지 못했습니다. (물론 제가 찾지 못해서 그럴수도 있지만.. ^^;;)
>
> C언어 표준 문서 같은 것들을 보시면서 습득하신건가요?
>
>
표준 문서를 보는 것 보다는 잘 된 책을 보시는게 더 낳을 거 같습니다. 표준
문서라는 게 무슨 법전 같은 것이라 딱딱하고 재미없고 이해하기도 어렵거든요.
아마도 컴파일러를 만들거나 언어에 아주 관심이 많은 사람들이나 보는 게
아닐까요? 제 생각엔 무슨 마법과 같은 책이 있어서 님께서 궁금해 하시는 걸 다
설명해 줄 수는 없을 것 같습니다. 많은 분들이 추천하시는 좋은 책을 읽어
소화하시고, 거기에 설명이 없는 것은 고민하고 물어보시는 게 가장 좋은 방법일
거라 생각합니다.
위에서 말씀하신 것은 function의 formal parameter로 사용될 때만 참입니다.
> 사실 incomplete array type 비완성 배열(?)이라는 것은 그 시점에서는 Array가
> 아니라는 것을 주의하십시오. 저는 배열이 된 시점에서 드린 이야기가
아닙니다.
array 이름이 pointer로 쓰일 수는 있지만, 이렇게 한 번 바뀌고 나면 다시
array로 돌아올 수 없지 않습니까? incomplete array가 pointer로 변화하였다가,
다시 어느시점에 array가 된다는 것은 이해하기 어려운 말씀이군요. 좀 더 설명이
필요하지 않을까요?
C 에는 SemiDynamic Array 라는 것이 없습니다. 특정 implementation 에서
확장으로 지원하는 것일 뿐입니다. 표준 C 에서 int p[] 는 incomplete type
을 갖는 배열일 뿐입니다.
>그렇습니다. 컴파일러는 아무생각없이 int p[]를 int *p 선언으로 변경합니다.
절대로 그렇지 않습니다. 특정 implementation 의 확장을 일반적인 경우로
기술하지 마십시오.
>(어떤 책을 참조하셔도 될겁니다. Neill Graham이 쓴 책을 보십시오). 따라서 int
>p[]나 int *p나 같은 말입니다.
절대로 그렇지 않습니다. 저 아래 이와 관련해 이미 많은 분들이 그렇지
않다고 열심히 설명했습니다. 다시 읽어보시기 바랍니다.
>사실 incomplete array type 비완성 배열(?)이라는 것은 그 시점에서는 Array가
>아니라는 것을 주의하십시오. 저는 배열이 된 시점에서 드린 이야기가 아닙니다.
>
그 시점에서도 배열입니다. 즉 incomplete type 이기 때문에 구체적 크기를
필요로 하지 않는 모든 곳에서 배열로서 사용이 가능합니다. (C 에서는 첨
자연산 역시 포인터로 정의되기 때문에 가능합니다)
제 생각에 님은 C 의 특정 확장으로 인해 표준 C 의 올바른 정의와
semantic 을 받아들이지 못하고 계신듯 합니다. 확장이 아닌, 표준 C 언어
를 기술한 책으로 배열과 포인터의 관계를 명확히 하시는 것이 좋을 듯
싶습니다. (clc FAQs 를 참고하시기 바랍니다)
ebazy 님은 특정 implementation 의 확장으로 인해 incomplete array 에 대
해서 오해를 하고 계십니다. 표준 C 언어를 생각한다면 올바른 설명이 아닙
니다.
그리고, C99 표준에서는 C90 과는 달리 variable-size array 라는 것을 지
원합니다. 이는 개념상 ebazy 님의 SemiDynamic array 하고 유사할지 모르
지만, 그 선언방법과 정의된 행동의 격이 완전히 다릅니다.
>저도 공부하는 의미에서 array와 pointer가 같을 때와 다를 때를 정리해 보도록
>하겠습니다.
>
>- array와 pointer가 같은 경우
>
>1. expression 안의 array 이름은 그 array의 첫번째 항목을 가리키는 포인터로
>취급된다.
물론 sizeof 연산자나 단항 & 연산자의 피연산자인 경우에는 decay 하지 않
습니다.
>
>예1)
>
>int a[10], *p;
>p = a;
>
>2. array의 첨자(subscript)는 포인터 오프셋과 동일하다.
a[i] 는 정의 자체가 *((p)+(i)) 입니다. 포인터 오프셋이란 결국 대상체
포인터 (pointer to object type) 와 정수의 +, - 연산입니다. 물론 배열
첨자 역시 (단순한 설명이 아닌) 언어적 정의 자체가 포인터 연산으로 정의
되기에 둘 사이의 구분은 전혀 필요없습니다. - 다만 프로그래머의 의도를
표현하는 데 필요할 뿐입니다.
>예2)
>
>int a[10], *p, i;
>p = a;
>
>for (i = 0; i < 10; i++) a[i] = 0;
>for (i = 0; i < 10; i++) p[i] = 0;
>for (i = 0; i < 10; i++) *(p+i) = 0;
>for (i = 0; i < 10; i++) *p++ = 0;
>
>위에 있는 네개의 for 문은 모두 동일한 결과를 가져옵니다. (물론 컴파일러가
>만든 코드는 다를 수 있습니다만)
C 의 언어적 정의는 actual machine 이 아닌 abstract machine 이므로, 컴
파일러가 만드는 코드는 (C 언어의 입장에서는) 중요하지 않습니다. 다만
그러한 정의가 요구하는 행동이 이루어지는 것처럼 (as if) 해주기만 하면
되는 것입니다.
또한 위의 4개의 for 문중 마지막 for 문은 위의 3개와 결과로 나오는 행동
이 다릅니다. (p 의 값이 변하겠지요)
>3. 함수의 인자로 선언된 array 이름은 그 array의 첫번째 항목을 가리키는
>포인터로 취급된다.
아래 설명은 맞는 말이지만, 이 설명은 조금 틀립니다. 매개변수 선언은 어
디까지나 명칭에 대한 type 의 선언입니다. 선언에서 첫번째 요소를 가르키
고 안 가르키고등의 문제는 상관이 없습니다. 좀 더 올바르게 설명하자면,
"함수의 매개변수로 선언된 "올바른" (-> 자꾸 강조하는 이유를 아시겠습니
까?) 배열형 (array of type) 의 매개변수는 포인터형 (pointer to type)
으로 다시 쓰여집니다 (rewrite)." 겠지요.
>예3)
>
>void func(int *para) {}
>void func(int para[]) {}
>void func(int para[100]) {}
>
>세개의 함수 정의 모두가 동일합니다.
>
>- array와 pointer가 다른 경우
>
>원래 type 자체가 틀리므로 위의 경우를 제외하고는 둘은 서로 다른 것입니다만,
>(물론 현실에서는 위의 경우들이 대부분이지만 ^^) 가장 많이 틀리는 부분만 좀
>적어보록 하겠습니다.
>
>1. array 이름은 l-value가 될 수 있지만 unmodifiable이다.
표준은 이를 lvalue 와 modifiable lvalue 로 구분하고 있습니다.
>예4)
>
>int a[10], *p;
>a = p; /* 이런 일을 하는 것은 불가능합니다. */
>
>2. referencing
>
>어레이의 위치는 컴파일을 할 때 결정됩니다. 따라서 어레이의 어떤 요소를
>읽어오기 위해 컴파일러가 어레이 위치를 가져올 필요가 없습니다. 하지만
>포인터가 가리키는 값을 알기 위해서는 indirect referencing을 해야합니다.
이 내용은 C 언어로 작성된 프로그램의 더 낮은 레벨에서 토론될 내용입니
다. 언어의 추상적 정의인 C 언어의 정의는 배열의 위치를 가져오는 등의
행동을 정의하고 있지 않습니다. 어디까지나 type 에 맞는 수식의 평가와
side effect 등이 있을 뿐입니다.
또한, 정적 기억부류를 갖는 배열의 경우 프로그램의 한 실행동안 주소상수
(address constant) 남게 되지만, 자동 기억부류를 갖는 배열은 그렇지 않
지요.
[...]
>
>이런 레퍼런싱의 차이로 인해서 다음과 같은 코드는 실행시 오류를 내게 됩니다.
>
>예6)
>
>file A:
> int a[10];
>
>file B:
> extern int *a;
> int c;
> c = a[10];
C 언어는 이 프로그램의 오류를 그런식으로 해석하지 않습니다. 위의 프로
그램이 undefined behavior 를 갖는 이유는 대상체 (object) 를 위한 명칭
인 a 의 type 이 호환되도록 선언되어 있지 않기 때문입니다. 또한 B 에서
올바르게 a[10] 으로 선언된다고 하더라도 배열의 마지막 요소 다음을
dereferencing 하는 행동은 undefined behavior 입니다.
[...]
>
>표준 문서를 보는 것 보다는 잘 된 책을 보시는게 더 낳을 거 같습니다.
표준문서보다 더 잘된 책이 있습니까? :) (농담입니다)
>저도 공부하는 의미에서 array와 pointer가 같을 때와 다를 때를 정리해 보도록
>하겠습니다.
>
>- array와 pointer가 같은 경우
>
>1. expression 안의 array 이름은 그 array의 첫번째 항목을 가리키는 포인터로
>취급된다.
물론 sizeof 연산자나 단항 & 연산자의 피연산자인 경우에는 decay 하지 않
습니다.
>
>예1)
>
>int a[10], *p;
>p = a;
>
>2. array의 첨자(subscript)는 포인터 오프셋과 동일하다.
a[i] 는 정의 자체가 *((p)+(i)) 입니다. 포인터 오프셋이란 결국 대상체
포인터 (pointer to object type) 와 정수의 +, - 연산입니다. 물론 배열
첨자 역시 (단순한 설명이 아닌) 언어적 정의 자체가 포인터 연산으로 정의
되기에 둘 사이의 구분은 전혀 필요없습니다. - 다만 프로그래머의 의도를
표현하는 데 필요할 뿐입니다.
>예2)
>
>int a[10], *p, i;
>p = a;
>
>for (i = 0; i < 10; i++) a[i] = 0;
>for (i = 0; i < 10; i++) p[i] = 0;
>for (i = 0; i < 10; i++) *(p+i) = 0;
>for (i = 0; i < 10; i++) *p++ = 0;
>
>위에 있는 네개의 for 문은 모두 동일한 결과를 가져옵니다. (물론 컴파일러가
>만든 코드는 다를 수 있습니다만)
C 의 언어적 정의는 actual machine 이 아닌 abstract machine 이므로, 컴
파일러가 만드는 코드는 (C 언어의 입장에서는) 중요하지 않습니다. 다만
그러한 정의가 요구하는 행동이 이루어지는 것처럼 (as if) 해주기만 하면
되는 것입니다.
또한 위의 4개의 for 문중 마지막 for 문은 위의 3개와 결과로 나오는 행동
이 다릅니다. (p 의 값이 변하겠지요)
>3. 함수의 인자로 선언된 array 이름은 그 array의 첫번째 항목을 가리키는
>포인터로 취급된다.
아래 설명은 맞는 말이지만, 이 설명은 조금 틀립니다. 매개변수 선언은 어
디까지나 명칭에 대한 type 의 선언입니다. 선언에서 첫번째 요소를 가르키
고 안 가르키고등의 문제는 상관이 없습니다. 좀 더 올바르게 설명하자면,
"함수의 매개변수로 선언된 "올바른" (-> 자꾸 강조하는 이유를 아시겠습니
까?) 배열형 (array of type) 의 매개변수는 포인터형 (pointer to type)
으로 다시 쓰여집니다 (rewrite)." 겠지요.
>예3)
>
>void func(int *para) {}
>void func(int para[]) {}
>void func(int para[100]) {}
>
>세개의 함수 정의 모두가 동일합니다.
>
>- array와 pointer가 다른 경우
>
>원래 type 자체가 틀리므로 위의 경우를 제외하고는 둘은 서로 다른 것입니다만,
>(물론 현실에서는 위의 경우들이 대부분이지만 ^^) 가장 많이 틀리는 부분만 좀
>적어보록 하겠습니다.
>
>1. array 이름은 l-value가 될 수 있지만 unmodifiable이다.
표준은 이를 lvalue 와 modifiable lvalue 로 구분하고 있습니다.
>예4)
>
>int a[10], *p;
>a = p; /* 이런 일을 하는 것은 불가능합니다. */
>
>2. referencing
>
>어레이의 위치는 컴파일을 할 때 결정됩니다. 따라서 어레이의 어떤 요소를
>읽어오기 위해 컴파일러가 어레이 위치를 가져올 필요가 없습니다. 하지만
>포인터가 가리키는 값을 알기 위해서는 indirect referencing을 해야합니다.
이 내용은 C 언어로 작성된 프로그램의 더 낮은 레벨에서 토론될 내용입니
다. 언어의 추상적 정의인 C 언어의 정의는 배열의 위치를 가져오는 등의
행동을 정의하고 있지 않습니다. 어디까지나 type 에 맞는 수식의 평가와
side effect 등이 있을 뿐입니다.
또한, 정적 기억부류를 갖는 배열의 경우 프로그램의 한 실행동안 주소상수
(address constant) 남게 되지만, 자동 기억부류를 갖는 배열은 그렇지 않
지요.
[...]
>
>이런 레퍼런싱의 차이로 인해서 다음과 같은 코드는 실행시 오류를 내게 됩니다.
>
>예6)
>
>file A:
> int a[10];
>
>file B:
> extern int *a;
> int c;
> c = a[10];
C 언어는 이 프로그램의 오류를 그런식으로 해석하지 않습니다. 위의 프로
그램이 undefined behavior 를 갖는 이유는 대상체 (object) 를 위한 명칭
인 a 의 type 이 호환되도록 선언되어 있지 않기 때문입니다. 또한 B 에서
올바르게 a[10] 으로 선언된다고 하더라도 배열의 마지막 요소 다음을
dereferencing 하는 행동은 undefined behavior 입니다.
[...]
>
>표준 문서를 보는 것 보다는 잘 된 책을 보시는게 더 낳을 거 같습니다.
표준문서보다 더 잘된 책이 있습니까? :) (농담입니다)
예가 틀렸군요 다음과 같이 수정합니다.
예6)
file A:
int a[10];
file B:
extern int *a;
int c;
c = a[0];
참가합니다. 일반적으로 array type의 lvalue는 usual unary conversion에 의해,
array의 첫 번째 element를 가리키는 pointer로 변환됩니다. 단 다음의 3가지의
경우는
제외입니다.
1. array가 sizeof 또는 address operator(&)의 argument일 때,
2. character string literal 이 character array를 초기화하기 위해 사용될때,
3. wide string literal 이 wchar_t type의 array를 초기화하기 위해 사용될 때,
예를 들면,
char a[] = "abcd"; /* No conversion */
char *b = "abcd"; /* Array converted to pointer */
int i = sizeof(a); /* No conversion; size of whole array */
b = a +1 ; /* Array converted to pointer */
> >예1)
> >int a[10], *p;
> >p = a;
> >
> >2. array의 첨자(subscript)는 포인터 오프셋과 동일하다.
>
> a[i] 는 정의 자체가 *((p)+(i)) 입니다. 포인터 오프셋이란 결국 대상체
> 포인터 (pointer to object type) 와 정수의 +, - 연산입니다. 물론 배열
> 첨자 역시 (단순한 설명이 아닌) 언어적 정의 자체가 포인터 연산으로 정의
> 되기에 둘 사이의 구분은 전혀 필요 없습니다. - 다만 프로그래머의 의도를
> 표현하는 데 필요할 뿐입니다.
>
> >예2)
> >
> >int a[10], *p, i;
> >p = a;
> >
> >for (i = 0; i < 10; i++) a[i] = 0;
> >for (i = 0; i < 10; i++) p[i] = 0;
> >for (i = 0; i < 10; i++) *(p+i) = 0;
> >for (i = 0; i < 10; i++) *p++ = 0;
> >
> >위에 있는 네개의 for 문은 모두 동일한 결과를 가져옵니다. (물론 컴파일러가
> >만든 코드는 다를 수 있습니다만)
첨언하자면, 여기서 언급한 목적 코드다 다르다는 것은 대표적으로
a[i] 와 p[i]의 목적코드 자체가 다르다는 것입니다.
a[i] 와 p[i]가 똑같은 array element를 참조하고 똑같은 결과 값을 가진다고
해서 동일한(identical) 수식은 아닙니다. 단지 동등한(equivalent) 수식일
뿐입니다.
목적코드 부분은 Jun Woong 님의 지적대로 그리 중요한 부분은 아니지만,
개념 정리하실 때, 알면 도움이 되는 이야기라 생각합니다.
> C 의 언어적 정의는 actual machine 이 아닌 abstract machine 이므로, 컴
> 파일러가 만드는 코드는 (C 언어의 입장에서는) 중요하지 않습니다. 다만
> 그러한 정의가 요구하는 행동이 이루어지는 것처럼 (as if) 해주기만 하면
> 되는 것입니다.
> 또한 위의 4개의 for 문중 마지막 for 문은 위의 3개와 결과로 나오는 행동
> 이 다릅니다. (p 의 값이 변하겠지요)
> >3. 함수의 인자로 선언된 array 이름은 그 array의 첫 번째 항목을 가리키는
> >포인터로 취급된다.
aaa[] 와 *aaa 라는 일반적으로는 다른 표현이지만, 함수의 argument로
사용될 때에 한하여 동일한 표현으로 취급됩니다.
정확히 표현하자면, array type을 가진 aaa가 pointer 형으로 변환되어 취급
되기에 둘의 표현이 동일한 표현으로 취급되어지는 것입니다.
> 아래 설명은 맞는 말이지만, 이 설명은 조금 틀립니다. 매개변수 선언은 어
> 디까지나 명칭에 대한 type 의 선언입니다. 선언에서 첫번째 요소를 가르키
> 고 안 가르키고등의 문제는 상관이 없습니다. 좀 더 올바르게 설명하자면,
> "함수의 매개변수로 선언된 "올바른" (-> 자꾸 강조하는 이유를 아시겠습니
> 까?) 배열형 (array of type) 의 매개변수는 포인터형 (pointer to type)
> 으로 다시 쓰여집니다 (rewrite)." 겠지요.
>
> >예3)
> >
> >void func(int *para) {}
> >void func(int para[]) {}
> >void func(int para[100]) {}
> >
> >세개의 함수 정의 모두가 동일합니다.
> >
> >- array와 pointer가 다른 경우
> >
> >원래 type 자체가 틀리므로 위의 경우를 제외하고는 둘은 서로 다른
것입니다만,
> >(물론 현실에서는 위의 경우들이 대부분이지만 ^^) 가장 많이 틀리는 부분만
좀
> >적어보록 하겠습니다.
> >
> >1. array 이름은 l-value가 될 수 있지만 unmodifiable이다.
>
> 표준은 이를 lvalue 와 modifiable lvalue 로 구분하고 있습니다.
>
> >예4)
> >
> >int a[10], *p;
> >a = p; /* 이런 일을 하는 것은 불가능합니다. */
첨언하자면, a는 array type의 lvalue은 usual unary conversion에 따라 first
element의 pointer로 변환되므로 value를 assignment 할 수 없게 되는 것입니다.
표준문서가 좋긴 한데 어렵죠.. ^^; 가끔은 성경책 읽는 기분이 들기도 합니다.
*^^*
나! 수도승, 너! 표준문서
^0^
------------------------
Kong Hyeog Jun
"국가보안법 철폐되는 날을 기다리며"