C언어 가변인수에 관한 질문입니다.

loopinger의 이미지

아래와 같은 코드를 실행할 경우에 대한 결과값은

{0, 10, 0, 20, 0, 30} 과 같은 결과가 나옵니다.

제 생각에 컴퓨터가 이전과 달리 64bit 운영체제로 변경되면서

int형은 4byte를 차지하지만 메모리는 8byte를 차지하기 때문에

8byte공간에 4byte를 차지하기 때문이라는 생각이듭니다.

int형을 모두 __int64 로 바꾸면

p[0], p[1], p[2]가 {10, 20, 30}이 나오는 것은 확인하였지만

결론적으로 4byte공간만 사용하고 싶은데

8byte공간을 사용하고 있는 경우라고 생각이 들어서

메모리적으로 낭비라는 생각이 듭니다.

정말로 4byte 공간만 사용하는 방법이 있을까요?

혹은 제가 생각한 부분중 틀린 부분이 있나요?

#include <stdio.h>
 
void test(int num, ...);
 
int main(void)
{
    int a=10, b=20, c=30;
 
    test(1, a, b, c);
    test(2, a, b, c);
    test(3, a, b, c);
    test(4, a, b, c);
    test(5, a, b, c);
    test(6, a, b, c);
    return 0;
}
 
void test(int num, ...)
{
    int* p=NULL;
 
    p=&num + 1;
 
    if(num==1)
    {
        printf("%d \n", p[0]);
    }
    else if (num==2)
    {
        printf("%d \n", p[1]);
    }
    else if (num==3)
    {
        printf("%d \n", p[2]);
    }
    else if (num==4)
    {
    	printf("%d \n", p[3]);
	}
    else if (num==5)
    {
    	printf("%d \n", p[4]);
    }
    else if (num==6)
    {
    	printf("%d \n", p[5]);
    }
}
jick의 이미지

가변인자를 사용하는 함수는 인자를 모두 스택에 넣어야 하고 꺼낼 때도 스택에서 하나씩 꺼내야 하기 때문에 컴파일러가 최적화를 하기 어렵습니다. 이게 무슨 얘기냐 하면, 가변인자를 사용하는 시점에서 이미 최적화는 어느 정도 포기하고 그 대신 코드 사이즈 혹은 코드 재사용을 더 중시한다는 결정을 했다고 볼 수 있는 거죠.

게다가 요즘 CPU에서 스택에 4바이트를 넣느냐 8바이트를 넣느냐는 무한 재귀호출을 돌리지 않는 이상 사실상 성능에 아무런~ 차이가 없습니다. 그런 차이는 그냥 무시하시는 게 답입니다.

익명 사용자의 이미지

1. 일단 가변 인수 사용 방법이 틀렸습니다.
정석적으로 사용하려면 아래와 같이 사용해야 됩니다.

#include <stdio.h>
#include <stdarg.h>
 
void test(int num, ...);
 
int main(void)
{
    int a=10, b=20, c=30;
 
    test(1, a, b, c); puts("");
    test(2, a, b, c); puts("");
    test(3, a, b, c);
    /*
    실제로 넘긴 것보다 더 많은 인수를 사용하려는 경우는 UB
    test(4, a, b, c);
    test(5, a, b, c);
    test(6, a, b, c);
    */
    return 0;
}
 
void test(int num, ...)
{
	va_list args;
 
	va_start(args, num);
	for (int i = 1; i <= num; ++i){
		printf("%d: %d\n", i, va_arg(args, int));
	}
	va_end(args);
}

C언어에서 포인터 산술을 지원한다고, 제멋대로 막 쓰면 안 되는 겁니다.

가변 인수를 스택에 어떤 레이아웃으로 올릴지는 컴파일러 재량이고, 함부로 가정해서는 안 되지요.

저는 솔직히 *printf/*scanf류 함수 인터페이스를 구현해야 할 때를 제외하고는 C언어의 가변 인수 기능을 사용하는 것 자체를 별로 권장하지 않습니다만,

만약 써야만 한다면 위와 같이 언어 차원에서 제공하는 방식을 써야 합니다.

2. who cares?

C언어에서는 "컴파일러가 반드시 보장해 줘야 하는 것"이 있는 반면, "컴파일러가 재량껏 할 수 있는 것"도 있습니다.

앞서 간단히 말씀드렸듯이, 가변 인수를 전달하기 위한 스택 메모리 레이아웃은 명백히 후자입니다.

컴파일러가 보장해 주는 건, va_list를 적절히 조작하면 넘겨 받은 인수를 모두 읽을 수 있다는 것뿐이죠.

"난 4바이트짜리 int를 보내고 싶은데 왜 8바이트 정렬을 해 놨냐"고 따져봤자 "컴파일러 마음이다"라는 답밖에 들을 수가 없어요.

게다가 낭비되는 자원이라고 해 봤자 별 거 없을 겁니다. 가변 인수로 매개변수를 백 개쯤 보낼 것도 아니고, 설령 그렇게 한다 해도 400byte 정도밖에 더 되나요.

3. 정 신경이 쓰인다면...

int 배열을 쓰세요.

제 생각엔, 주어진 use case에서는 이렇게 쓰는 게 더 깔끔해 보입니다.
구체적으로 메모리가 어떻게 쓰일지는 (또다시) 여전히 컴파일러 재량이긴 합니다만.

#include <stdio.h>
#include <stdarg.h>
 
void test(int num, int args[]);
 
int main(void)
{
    int a=10, b=20, c=30;
 
    test(1, (int []){a, b, c}); puts("");
    test(2, (int []){a, b, c}); puts("");
    test(3, (int []){a, b, c});
    return 0;
}
 
void test(int num, int args[])
{
	for (int i = 1; i <= num; ++i){
		printf("%d: %d\n", i, args[i - 1]);
	}
}

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.