c언어 기본적인 질문

kimtaehan91의 이미지

안녕하세요 C언어를 공부하고있는 학생입니다.

리눅스 환경에서 공부를 하고 있습니다.

질문은 리턴 값에 관한 건데 최근 엔디안이란걸 알아서 리틀 -> 빅엔디안 변환하는 과정에서 궁금한게 있어 질문합니다.

아래와 같이 스왑 해주는 함수를 만들어서 스왑까지 성공을 했는데

int int_swap(int int_buf,int int_buf_size)
{
int i;
int output_int_buf;
char *char_buf = (char*)&int_buf;
char *output_char_buf = (char*)&output_int_buf;

for(i = 0; i < int_buf_size; i++)
output_char_buf[i] = char_buf[int_buf_size - i - 1];

return output_int_buf;
}

int main(void)
{
int a = 12345;
a = int_swap(a,sizeof(a));
return 0;
}

질문은 지금 int형으로 매개변수 전달을 해줬는데, 저 한함수로 short형, long형, int형을 다 스왑하고 싶습니다.
아무생각없이 함수를 3개 만들어서 하는도중 갑자기 생각난건데 가능이 합니까?? 소스를 알려달라는게 아니라 어떤식으로 가능한지 궁금해서 여쭈어봅니다.
반환하는 부분이 int형으로 고정이 되어있는데 3가지 종류의 타입이 가능한지.. 알려주시면 감사하겠습니다.

익명 사용자의 이미지

1. 안 됩니다. 유감스럽지만.

가장 직접적인 원인은, C언어는 C++와 달리 함수 오버로딩을 지원하지 않는다는 겁니다. 똑같은 함수 이름인데 매개변수 타입(short, int, long)에 따라 다른 함수를 호출하도록 만들 수가 없습니다.
결국 매개변수 타입이 다른 일련의 함수들은 그 함수 이름들도 달라야 하는 겁니다.

2. 데이터의 시작점(char *) 및 길이(size_t)를 받아서 바이트 순서를 뒤집는 구현 함수를 하나 만들고, short, int, long을 매개변수로 받는 인터페이스 함수를 각각 따로 만들면 최소한 구현 코드 중복은 없어지겠군요. 인터페이스는 여전히 3개가 따로 주어지지만요.
거기까지가 한계입니다.

3. C++로는 가능합니다. 템플릿이라는 깔끔한 방법을 써서 구현 코드와 인터페이스 모두 깔끔하게 한 번에 만들 수 있습니다.

twinwings의 이미지

아니오 동작하긴 합니다... 초기화 구문이 빠져서 그 부분만 넣으면 되네요.

#include <stdio.h>
#include <arpa/inet.h>
 
int int_swap(int int_buf,int int_buf_size)
{
int i;
int output_int_buf = 0;
char *char_buf = (char*)&int_buf;
char *output_char_buf = (char*)&output_int_buf;
 
for(i = 0; i < int_buf_size; i++)
output_char_buf[i] = char_buf[int_buf_size - i - 1];
 
return output_int_buf;
}
 
void test1()
{
    int a = 12345;
    int b = htonl(a);
    printf("a       (0x%08X)\n", a);
    printf("int_swap(0x%08X)\n", int_swap(a,sizeof(a)));
    printf("b       (0x%08X)\n", b);
}
 
void test2()
{
    short a = 12345;
    short b = htons(a);
    printf("a       (0X%04X)\n", a);
    printf("int_swap(0X%04X)\n", int_swap(a,sizeof(a)));
    printf("b       (0x%04X)\n", b);
 
}
 
int main(void)
{
    test1();
    test2();
 
    return 0;
}

결과

:~/Desktop$ ./a.out 
a       (0x00003039)
int_swap(0x39300000)
b       (0x39300000)
a       (0X3039)
int_swap(0X3930)
b       (0x3930)
:~/Desktop$ 

* 32bit 까지는 잘 되는데 64bit는 잘 모르겠네요.
* 초기화 구문이 빠져서 short(16bit)의 경우 이상한 값이 출력됩니다. 초기화 구문 넣어주세요.

익명 사용자의 이미지

입력 파라미터가 int int_buf인데도 long까지 퉁쳐서 돌아갈 수 있는 건 아마도 sizeof(int)=sizeof(long)인 환경에서 돌리셨기 때문이겠죠.
int가 항상 long을 커버할 수 있다는 가정은 쉽게 깨집니다. 대표적으로 x86 64비트 리눅스에서 gcc는 sizeof(int)=4, sizeof(long)=8이죠.

결국 일반적인 구현을 하려면 함수 자체를 (unsigned char *, size_t)쌍을 받도록 하는 수밖에 없습니다. 근데 그러면 address를 넘겨 줘야 해서 사용이 불편해지죠.
가급적이면 좀 더 편한 인터페이스를 제공해주고 싶은데 오버로딩도 없고 템플릿도 없고... 어쩌겠습니까.

생각해보니 매크로 함수를 쓰면 제한적이게나마 구현을 할 수는 있겠습니다. 한번 해봤는데 어떨지 모르겠네요.

#include <stdio.h>
#include <stdlib.h>
 
static void byte_reverse(void *p, size_t l) {
        unsigned char *pb = (unsigned char *)p;
        unsigned char *pe = ((unsigned char *)p) + l - 1;
        while (pb < pe) {
                *pb ^= *pe;
                *pe ^= *pb;
                *pb ^= *pe;
                pb++;
                pe--;
        }
}
 
#define DEF_SWAP(T) static T T ## _swap(T in) {byte_reverse(&in, sizeof(T)); return in;}
#define SWAP(T,E) (T ## _swap(E))
 
DEF_SWAP(short)
DEF_SWAP(int)
DEF_SWAP(long)
 
int main(void) {
        printf("%hx\n", SWAP(short, 0x0123));
        printf("%x\n", SWAP(int, 0x01234567));
        printf("%lx\n", SWAP(long, 0x0123456789abcdef));
        return 0;
}

T 자리에는 반드시 하나의 identifier만 들어가야 해서 (다시 말하면, unsigned int 같은 건 바로 못 넣고 typedef 해서 넣어야 합니다) 사용이 좀 제한적인데, 어떻게 개선시킬 수 있을지는 바로 잘 안 떠오르네요.

twinwings의 이미지

음.. 원하는 답변하고 다를 수 있지만 생각나는 것 대로 적어볼게요.

틀린게 있다면 지적 부탁드립니다.

1.
시스템에 따라 바이트오더(빅/리틀 엔디안)가 다르기 때문에
로컬 시스템에서 바이트오더를 받아와서 조건부로 처리해야 합니다.
보통 매크로를 이용해 조건부 컴파일을 하지요.
위의 함수는 특정 바이트오더로 바꾸는게 아니라 무조건 다른 바이트오더로 바꾸네요.

2.
해당 구현을 따라해서 공부하는 목적이 아니라 실용적인 목적이라면,
arpa/inet.h 헤더에

       #include <arpa/inet.h>
 
       uint32_t htonl(uint32_t hostlong);
 
       uint16_t htons(uint16_t hostshort);
 
       uint32_t ntohl(uint32_t netlong);
 
       uint16_t ntohs(uint16_t netshort);

같이 로컬 바이트오더 -> 빅엔디안 으로 바꿔주는 함수를 제공합니다.
(로컬 바이트오더가 빅엔디안이든 리틀엔디안이든 관계 없이 알아서 빅엔디안으로 바꿔줍니다.
인터넷 통신의 표준 바이트 오더는 빅엔디안이기 때문에 이러한 함수를 제공합니다.)

애초에 바이트오더 변환이 통신을 위해 존재하기 때문에 로컬 바이트오더에 모든것을 맞추면 문제가 없습니다. 즉 통신 모듈에만 저 함수를 이용하고 나머지는 바이트오더를 고려하지 않아도 되는 거죠. 그래서 따로 빅엔디안<->리틀엔디안 변환 함수는 잘 쓰이지 않습니다.
(*틀리면 지적 부탁드립니다.)

또, 위에 함수들은 표준 헤더에 있는 만큼 최적화가 잘 되어 있겠죠.
아마도 어셈블러로 작성되어있지 않을까..
(추정입니다.)

3.
단일 int 변수보단 1바이트 짜리 배열 이용해서 넣으시면 될 듯 합니다.

하지만 시스템에 따라서 int 등의 변수의 크기가 결정되기 때문에

표준헤더에 있는 것 처럼 byte 길이가 명시되어 있는 함수가 낫지 않을까 생각해봅니다.

익명 사용자의 이미지

질문에 대한 것과는 관련없는 충고를 드리자면,
C에 없는 기능은 매크로나 테크닉으로 구현하려고 힘빼지 마시고
과감히 포기하십시오.

더 필요하면 다른 언어로 가는게 맞으며
C로 구현하면 억지춘향식의 모양새가 되어 가독성, 성능이 떨어집니다.

위의 경우에도 어차피 제한된 몇개 타입에 대해서만 쓰게 될 것이므로
일반화된 함수를 만들어봤자 시간낭비입니다.
성능 면에서도 while 구문은 빈번하게 쓰일경우 성능저하가 되겠지요.
함수 자체가 간단한 만큼 상당한 비율로.

익명 사용자의 이미지

동의 한표.

말씀을 드릴까 말까 했는데 다른 분이 날카롭게 찌르고 들어오시니 저도 한 말씀 드려야겠네요.
C언어를 깊숙히 알고 있으면 대충 보기에 안 되어 보이는 걸 별 이상한 매크로 테크닉으로 어찌어찌 되는 것처럼 보이게 만들 수 있는데, 그 중에선 정말 실제로 쓸 만한 코드 스니펫도 여럿 있긴 합니다. 세상엔 고수가 참 많아서...
하지만 제 코드는 그렇지 않습니다. 일반적인 arithmetic type에 적용가능한 코드를 짜겠다는 일념만으로 byte order 변환에 while문을 쓴다는 게 납득 가능한 범위일까요? 글쎄요.
타입이 알려졌을 때의 byte order 변환은 웬만하면 비트 연산 몇 개로 금방 끝난다고요. 여기에 루프를 끌고 들어왔는데 잘한 짓이라고 할 수는 없죠. 특히나 이 함수가 자주 쓰일 거라면 더더욱.

ioccc 같은 대회에 출품작을 낸다면야 매크로 등으로 복잡미묘한 테크닉을 써서 비효율적인 코드를 만들더라도 감탄을 사겠지만 실전 프로그래밍에서 그렇게 되지는 않겠죠.
제가 짠 코드는 그저 "이렇게 하면 불만족스럽게나마 일반적인 타입에 대한 구현 비슷한 게 가능해지긴 한다" 정도로만 봐 주시기 바랍니다. 그 이상의 의미는 없습니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.