Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

일정 횟수의 반복을 위한 while 문의 제어식에서의 증감 연산자 사용

3 views
Skip to first unread message

김승범

unread,
Apr 9, 2003, 3:06:58 AM4/9/03
to
전웅님의 "C 언어 펀더멘털" 891쪽을 보면, while 문의 제어식에 사용된
증감 연산자를 전위형으로 사용하기 위해서는 다음과 같이 해야 한다고
되어 있습니다.

void my_memcpy(void *s1, const void *s2, unsigned int size)
{
unsigned char *c1 = s1;
const unsigned char *c2 = s2;

/* while (size-- > 0) *c1++ = *c2++; 후위형 증감 연산자 사용 */
while (--size >= 0) *c1++ = *c2++;
}

그런데, size는 무부호 정수형이므로 --size >= 0)이라는 제어식은
항상 참이 되고, 결국 무한 루프에서 빠져나오지 못하게 됩니다.

결국 이런 경우에는 증감 연산자를 전위형으로는 사용할 수 없고
반드시 후위형으로 사용해야 한다는 결론에 이르게 되는데, 이 부분과
관련하여 저자이신 전웅님의 의도가 어떤 것이었는지 궁금합니다.

아마도 전위형 증감 연산자에 대한 부분을 모두 삭제하든지, 아니면
my_memcpy를 고쳐서 size를 unsigned int가 아닌 int로 선언해야 하는데,
어느 쪽으로도 쉽게 고치기는 어려울 것 같네요.

--
김승범

Jun Woong

unread,
Apr 9, 2003, 9:45:43 AM4/9/03
to
"김승범" <musi...@bawi.org> wrote in message news:3E93C692...@bawi.org...

> 전웅님의 "C 언어 펀더멘털" 891쪽을 보면, while 문의 제어식에 사용된
> 증감 연산자를 전위형으로 사용하기 위해서는 다음과 같이 해야 한다고
> 되어 있습니다.
>
[snip source code]

>
> 그런데, size는 무부호 정수형이므로 --size >= 0)이라는 제어식은
> 항상 참이 되고, 결국 무한 루프에서 빠져나오지 못하게 됩니다.

맞습니다. 해당 예제가 잘못된 것입니다. 지난번 지적해 주신 것과 함께 정
오표에 반영하도록 하겠습니다 - 개인적인 일이 많아 정오표 업데이트가 늦
어지고 있습니다.

>
> 결국 이런 경우에는 증감 연산자를 전위형으로는 사용할 수 없고
> 반드시 후위형으로 사용해야 한다는 결론에 이르게 되는데, 이 부분과
> 관련하여 저자이신 전웅님의 의도가 어떤 것이었는지 궁금합니다.

제 의도가 무엇인지는 분명합니다. 저는 개인적으로 증감 연산자 사용에서
거의 항상 후위형을 선호합니다. 하지만, 많은 경우 (특히, 영어권 사람들
에 의해 제작된 프로그램의 경우) 증감 연산자는 전위형으로 사용되고 있습
니다. 대부분의 경우에 전위형, 후위형의 성능 차이가 무의미함을 강조하기
위해 붙인 예입니다 - 즉, 해당 프로그램 소스와 제 의도가 필연적인 연관
을 갖는 것은 아닙니다. 회상해보면 아마도 memcpy() 와 관련된 예가 먼저
들어가 있던 자리에 전위형, 후위형 이야기를 나중에 추가하면서 size 의
데이터형이 무부호 정수형임을 간과했던 것 같습니다. 유사한 잘못이 있으
면 또 찾아주시길 부탁드립니다.

>
> 아마도 전위형 증감 연산자에 대한 부분을 모두 삭제하든지, 아니면
> my_memcpy를 고쳐서 size를 unsigned int가 아닌 int로 선언해야 하는데,
> 어느 쪽으로도 쉽게 고치기는 어려울 것 같네요.
>

해당 (전위형 증감 연산자를 사용하는) while 문을 이렇게 고칠까요? ;-)

size++;
while (--size > 0) *c1++ = *c2++;

아무래도 전위형을 위한 예를 다른 것으로 대체하고 my_malloc() 에서는
size 가 무부호 정수형임을 강조하는 다소 긴 이야기가 추가될 듯 합니다.

그럼, 좋은 지적 감사드립니다.


--
Jun, Woong (myco...@hanmail.net)
Dept. of Physics, Univ. of Seoul
Web : http://c-expert.uos.ac.kr

Kim, Seungtai

unread,
Apr 9, 2003, 9:53:40 AM4/9/03
to
"Jun Woong" <myco...@hanmail.net> wrote in message
news:b71858$3e3$1...@news.hananet.net...
[...]

> 해당 (전위형 증감 연산자를 사용하는) while 문을 이렇게 고칠까요? ;-)
>
> size++;
> while (--size > 0) *c1++ = *c2++;
>
[...]

my_memcpy(s1, s2, UINT_MAX);

는 어떻게 하실려고요? :)


S Kim


Jun Woong

unread,
Apr 9, 2003, 9:56:31 AM4/9/03
to
"Kim, Seungtai" <stkim@Heartmore. Com> wrote in message news:b718s2$8vp$1...@news.kreonet.re.kr...

무부호 정수형은 overflow 가 없으며 modulo 연산으로 정의됩니다.

그 예는 장난 삼아 적은 예가 아닙니다. :)

그럼...

Kim, Seungtai

unread,
Apr 9, 2003, 10:34:35 AM4/9/03
to

"Jun Woong" <myco...@hanmail.net> wrote in message

news:b718pg$3ss$1...@news.hananet.net...


> "Kim, Seungtai" <stkim@Heartmore. Com> wrote in message
news:b718s2$8vp$1...@news.kreonet.re.kr...
> > "Jun Woong" <myco...@hanmail.net> wrote in message
> > news:b71858$3e3$1...@news.hananet.net...
> > [...]
> > > 해당 (전위형 증감 연산자를 사용하는) while 문을 이렇게 고칠까요? ;-)
> > >
> > > size++;
> > > while (--size > 0) *c1++ = *c2++;
> > >
> > [...]
> >
> > my_memcpy(s1, s2, UINT_MAX);
> >
> > 는 어떻게 하실려고요? :)
> >
>
> 무부호 정수형은 overflow 가 없으며 modulo 연산으로 정의됩니다.
>
> 그 예는 장난 삼아 적은 예가 아닙니다. :)

크. 제가 착각을...

그런데, 원 글이 무엇을 설명하고자 한 것인지 자세히 모르겠습니다만, 이와같은
방식이 후위형 연산자를 사용한 것과는 계산량의 차이가 있는데 이를 후위형
연산을 전위형 연산으로 대체할 예로 적당한 것인지 모르겠습니다.


S Kim


Jun Woong

unread,
Apr 9, 2003, 10:51:18 AM4/9/03
to
"Kim, Seungtai" <stkim@Heartmore. Com> wrote in message news:b71b8p$aii$1...@news.kreonet.re.kr...

매개변수의 값이 더 이상 필요하지 않은 경우, 추가적인 루프 제어 변수를
도입하지 않고, 동일한 반복 횟수만을 얻고자 하는 경우입니다.

복덕방

unread,
Apr 10, 2003, 2:21:00 AM4/10/03
to
"김승범" <musi...@bawi.org> wrote in message
news:3E93C692...@bawi.org...
> 전웅님의 "C 언어 펀더멘털" 891쪽을 보면, while 문의 제어식에 사용된
> 증감 연산자를 전위형으로 사용하기 위해서는 다음과 같이 해야 한다고
> 되어 있습니다.
>
> void my_memcpy(void *s1, const void *s2, unsigned int size)
> {
> unsigned char *c1 = s1;
> const unsigned char *c2 = s2;
>
> /* while (size-- > 0) *c1++ = *c2++; 후위형 증감 연산자 사용 */
> while (--size >= 0) *c1++ = *c2++;
> }
>
복사되는 속도에 관해서 말씀드리고 싶은게 있는데

unsigned char = 1byte
unsigned int = 4byte
unsigned long = 8byte 라면

unsigned char *는 1byte씩 증가되고,
unsigned int *는 4byte씩 증가되고,
unsigned long *는 8byte씩 증가되면서 복사하기 때문에

위에서 unsigned char *로 선언하는 것과
unsigned int *또는 unsigned long *으로 선언하는 것과 속도에 차이가 있지
않나요?

물론 unsigned int *또는 unsigned long *으로 선언할 때엔 size를
4나 8의 배수로 맞추어야겠지요.

Chong-Dae Park

unread,
Apr 10, 2003, 3:13:41 AM4/10/03
to
"복덕방" <dug...@shinbiro.com> wrote:
>>
> 복사되는 속도에 관해서 말씀드리고 싶은게 있는데
>
> unsigned char = 1byte
> unsigned int = 4byte
> unsigned long = 8byte 라면
>
> unsigned char *는 1byte씩 증가되고,
> unsigned int *는 4byte씩 증가되고,
> unsigned long *는 8byte씩 증가되면서 복사하기 때문에
>
> 위에서 unsigned char *로 선언하는 것과
> unsigned int *또는 unsigned long *으로 선언하는 것과 속도에 차이가 있지
> 않나요?
>
> 물론 unsigned int *또는 unsigned long *으로 선언할 때엔 size를
> 4나 8의 배수로 맞추어야겠지요.

이렇게 속도 향상을 할 수도 있습니다만, 해당 CPU의 구조에 따라
달라집니다. 그리고 아무 고려없이 이 방법을 구현한다면 정렬 문제가 발생할
가능성이 높습니다.

han.comp.lang.c에서 다룰 주제는 아닌 듯 싶습니다. glibc 등의 해당
소스를 보시면 됩니다. 여러 무림 고수들이 머리를 싸매고 만든 것이니깐요.

--
박종대
--
"Money, when it was a matter of electronic exchange, meant nothing.
There was no feeling of either wealth or of poverty above a certain level.
The world was a matter of plastic cards and of slots, and all the world
transferred, transferred, transferred."
- from Isaac Asimov's "Gold" -

Jun Woong

unread,
Apr 11, 2003, 12:17:20 AM4/11/03
to
"복덕방" <dug...@shinbiro.com> wrote in message news:b730ij$lva$1...@news1.kornet.net...

만약, my_memcpy() 로 전달된 포인터가 unsigned int 나 unsigned long 으
로 접근할 수 없는 위치에 정렬되어 있다면 문제를 일으킬 수 있습니다.

또한, 표준에 의해 unsigned char 는 모든 bit 를 값을 위해 사용하도록 정
의되고 있습니다 - 즉, 문제를 일으킬 수 있는 trap representation 을 갖
지 않습니다. 반면, unsigned int/long 은 표현 중 일부 비트를 padding
bit 로 사용할 수 있고, 이중 일부가 trap representation 으로 예약될 수
있습니다. 이 경우, 임의의 값을 unsigned int/long 으로 접근하는 것은
trap representation 을 건드려 undefined behvaior 를 일으킬 수 있습니다.

이와 같은 문제로 위의 예제에서 unsigned char 를 unsigned int/long 으로
대체하는 것은 해당 프로그램을 정의되지 않은 행동을 일으키는 것으로 만
들어 버립니다. 따라서, 임의의 bit pattern 을 접근하는 "일반적인" 프로
그램에서는 반드시 unsigned char 형을 사용해야만 합니다.

이와 관련된 내용은 이미 책에서 다루고 있는 바입니다 - p.363)

그리고 속도와 관련된 성능 문제는, 늘 말씀드리듯이, 추상적인 단계의 C
프로그램에서 충분한 profiling 이나 어셈블리어 혹은 기계어 수준의 관찰
없이 단정할 수 없는 문제입니다. super-scalar machine 의 컴파일러는 위
의 함수를 unsigned char/int/long 에 무관하게 동일한 성능을 보이도록 구
성할 수도 있는 것이며, unsigned char 를 사용한 위와 같은 구조를 패터닝
하여 더 나은 성능을 보이는 기계어로 번역할 수도 있는 것입니다. C 프로
그램은 어디까지나 추상적인 단계에서 프로그래머의 의도를 표현하는 수단
임을 잊지 마시고, 진정 해당 부분이 프로그램의 전체 성능에서 병목 현상
을 일으키는 부분임이 확실할 때에만, C 프로그램 혹은 기계어 수준의 대책
을 생각해야 합니다.

(IT 백두대간 C 언어, pp.31-36)

예를 들어, 오래전 Tom Duff 가 Lucas 에서 일하고 있을 때 고안해낸
Duff's device 는 일반적으로 기계어 수준에서 이루어지는 loop unrolling
이라는 optimization 기술을 C 로 표현한 것입니다. 물론, 그가 해당 기술
을 Usenet 에 포스팅한 이유는 switch 문의 "fall through" 특성이 유용하
게 사용될 수도 있음을 보이려는 것이었지만 다수의 사람들은 그 기술이
"모든" implemenation 에서 효율적인 것은 아니기에 무의미하다고 주장했습
니다. 물론, 이는 사실입니다. 다만, Tom Duff 가 Duff's device 를 고안해
낸 이유는 자신이 개발 중인 프로그램의 "해당" 부분이 "특정" 환경
(machine 이름은 잊어버렸습니다) 에서 병목 현상을 일으키기 때문이었고,
실제 해당 부분을 Duff's device 로 대체한 결과 2배 이상의 성능 향상을
얻었다고 합니다. 즉, 이와 같은 분석 과정 없이 C 프로그램의 특정 부분이
지대한 성능 저하 혹은 향상을 가져올 것이라는 추측은 결코 유의미하지 않
습니다.

(IT 백두대간 C 언어, pp.885-886)

물론, memcpy() 같은 기반 라이브러리 함수는 프로그램 전체에서 매우 빈번
하게 사용되는 것이고 이에 따라 많은 implementation 은 memcpy() 가 해당
machine 에서 가장 최적의 성능을 내도록 여러가지 아이디어를 쏟아 부을
것입니다 - 심지어 일부 라이브러리 함수는 아예 어셈블리어로 제작될 수도
있습니다. 이것이 표준이 strcpy(), memcpy() 등을 라이브러리로 제공해 주
는 이유이며, 우리가 따로 만들어 쓰지 않고 implementation 을 믿으며 그
라이브러리 함수를 사용하는 이유입니다.

그럼...

r00kie

unread,
Apr 17, 2003, 12:44:15 AM4/17/03
to
"Jun Woong" <myco...@hanmail.net> wrote in message news:<b75fjj$ou3$1...@news.hananet.net>...

> "복덕방" <dug...@shinbiro.com> wrote in message news:b730ij$lva$1...@news1.kornet.net...
> > "김승범" <musi...@bawi.org> wrote in message
> > news:3E93C692...@bawi.org...
> > > 전웅님의 "C 언어 펀더멘털" 891쪽을 보면, while 문의 제어식에 사용된
> > > 증감 연산자를 전위형으로 사용하기 위해서는 다음과 같이 해야 한다고
> > > 되어 있습니다.
> > >
> > > void my_memcpy(void *s1, const void *s2, unsigned int size)
> > > {
> > > unsigned char *c1 = s1;
> > > const unsigned char *c2 = s2;
> > >
> > > /* while (size-- > 0) *c1++ = *c2++; 후위형 증감 연산자 사용 */
> > > while (--size >= 0) *c1++ = *c2++;
> > > }
> > >

위의 예제에서,


const unsigned char *c2 = s2;

선언/초기화를 했음에도 구. c2의 포인터를 위치를 변경할려는 행위(?)가 올바른지요?

그리구, 밑에 설명하신 부분중에 'trap representation'에 대해서 좀 더 구체적인 설명을 해 주실수 있습니까? 처음듣는 용어라서....

그럼, 많은 조언 감사드리며...

김승범

unread,
Apr 17, 2003, 1:03:19 AM4/17/03
to
r00kie wrote:
>
> "Jun Woong" <myco...@hanmail.net> wrote in message news:<b75fjj$ou3$1...@news.hananet.net>...
> > "복덕방" <dug...@shinbiro.com> wrote in message news:b730ij$lva$1...@news1.kornet.net...
> > > "김승범" <musi...@bawi.org> wrote in message
> > > news:3E93C692...@bawi.org...
> > > > 전웅님의 "C 언어 펀더멘털" 891쪽을 보면, while 문의 제어식에 사용된
> > > > 증감 연산자를 전위형으로 사용하기 위해서는 다음과 같이 해야 한다고
> > > > 되어 있습니다.
> > > >
> > > > void my_memcpy(void *s1, const void *s2, unsigned int size)
> > > > {
> > > > unsigned char *c1 = s1;
> > > > const unsigned char *c2 = s2;
> > > >
> > > > /* while (size-- > 0) *c1++ = *c2++; 후위형 증감 연산자 사용 */
> > > > while (--size >= 0) *c1++ = *c2++;
> > > > }
> > > >
>
> 위의 예제에서,
> const unsigned char *c2 = s2;
> 선언/초기화를 했음에도 구. c2의 포인터를 위치를 변경할려는 행위(?)가 올바른지요?

c2 자체는 const가 아닙니다. c2가 가리키는 unsigned char가 const지요.
pointer to const xxx와 const pointer xxx를 구분하셔야 합니다. 만일

unsigned char *const c2 = s2;

라고 했다면 c2++라는 식을 사용할 수 없었겠지요.

>
> 그리구, 밑에 설명하신 부분중에 'trap representation'에 대해서 좀 더 구체적인 설명을 해 주실수 있습니까? 처음듣는 용어라서....

유효한 수치값을 갖지 않고, 사용시 trap을 일으키는 표현 정도로 이해하시면
괜찮을 듯싶습니다. 예컨대 1바이트가 9비트인 시스템에서, 그 중 8비트만
수치값으로 사용하고 나머지 한 비트는 무조건 0으로 채우며, 만일 이 비트가
1로 되어 있을 때 이 값을 읽어 연산에 사용하려 할 경우 trap이 일어난다면,
이 비트가 1로 되어 있는 것을 trap representation이라고 할 수 있겠지요.

>
> 그럼, 많은 조언 감사드리며...
>
> > [... 과도한 인용 ...]

꼭 필요한 부분만 인용해 주시고 나머지는 지워서 올려 주시면 좋겠습니다.
:)

--
김승범

Jun Woong

unread,
Apr 17, 2003, 2:02:55 AM4/17/03
to
"김승범" <musi...@bawi.org> wrote in message news:3E9E3597...@bawi.org...
> r00kie wrote:
[...]

> > 그리구, 밑에 설명하신 부분중에 'trap representation'에 대해서 좀 더 구체적인 설명을 해 주실수 있습니까? 처음듣는 용어라서....
>
> 유효한 수치값을 갖지 않고, 사용시 trap을 일으키는 표현 정도로 이해하시면
> 괜찮을 듯싶습니다. 예컨대 1바이트가 9비트인 시스템에서, 그 중 8비트만
> 수치값으로 사용하고 나머지 한 비트는 무조건 0으로 채우며, 만일 이 비트가
> 1로 되어 있을 때 이 값을 읽어 연산에 사용하려 할 경우 trap이 일어난다면,
> 이 비트가 1로 되어 있는 것을 trap representation이라고 할 수 있겠지요.
>

구체적인 예를 들면, parity bit 를 도입한 implementation 을 생각해 볼
수 있습니다. 물론, 이렇게 정수형의 표현에 padding bit 를 도입한
implementation 을 일반적인 목적의 machine 에서 만나기는 어렵습니다 -
하지만, DSP 나 embedded system 에서는 가능합니다. 따라서, 그러한
padding bit 와 trap representation 을 "엄격히" 적용하면 다음과 같은 결
론을 얻을 수 있습니다.

int a[10];
memset(a, 0, sizeof(a));
a[0]; /* undefined behavior */

a[0] 은 padding bit 와 trap representation 의 가능성으로 정의되지 않은
값 (indeterminate value) 을 갖게 되며, 그 값에 접근하는 행동은
undefined behavior 를 일으킵니다.

"유일하게" unsigned char 형은 절대 padding bit 를 갖지 않는다고 정의됩
니다 - 즉, 모든 bit 가 값을 표현하는데 사용됩니다. 따라서, (바람직한
예는 아니지만)

int main(void)
{
int i; /* indeterminate value */
unsigned char uc; /* unspecified value */

i; /* undefined behavior */
uc; /* okay */

return uc - uc; /* same as return 0; */
}

여기서 unspecified value 는 indeterminate value 와는 달리 값의 집합이
trap representation 을 갖지 않는 경우에 사용합니다.

그럼...

(IT 백두대간 C 언어, p.181, p.188, p.363, p.694)

0 new messages