문제

32 비트 정수를 IP 주소로 변환하기위한이 샘플 코드가 있습니다.


#include <stdio.h>
int main()
{
 unsigned int c ;
 unsigned char* cptr  = (unsigned char*)&c ;
 while(1)
 {
  scanf("%d",&c) ;
  printf("Integer value: %u\n",c);
  printf("%u.%u.%u.%u \n",*cptr, *(cptr+1), *(cptr+2), *(cptr+3) );
 }
}

이 코드는 입력에 대한 잘못된 출력을 제공합니다 2249459722 . 그러나 내가 교체 할 때

scanf("%d",&c) ;
~에 의해
scanf("%u",&c) ;
출력이 올바르게 나옵니다.

추신 : 나는 알고있다 inet_ntop 그리고 inet_pton.
나는 그것들을 제안하는 것 외에 다른 대답을 기대합니다.

도움이 되었습니까?

해결책

당신은 코딩하고 있습니다 '죄로'(조만간 당신을 해칠 수있는 여러 실수를 저질렀습니다. 우선, 정수가 올바른 엔디 언이라고 가정합니다. 일부 머신에서는 인텔 머신 또는 PowerPC 또는 SPARC 머신에서 잘못 될 것입니다.

일반적으로 잘못된 결과를 얻는다는 말보다는 실제 결과를 보여 주어야합니다. 또한 예상 결과를 보여 주어야합니다. 그것은 사람들이 당신의 기대를 디버깅하는 데 도움이됩니다.


다음은 수정 된 코드 버전입니다. 입력을 요청하는 대신 지정한 값을 가정합니다.

#include <stdio.h>
int main(void)
{
    unsigned int c = 2249459722;
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
    return(0);
}

내 Mac (Intel, Little-Endian)에서 컴파일되면 출력이 다음과 같습니다.

Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 

내 태양 (SPARC, Big-Endian)에 편집되면 출력은 다음과 같습니다.

Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 134.20.8.10 

(SPARC에서 GCC 4.4.2를 사용하면 경고를받습니다.

xx.c:4: warning: this decimal constant is unsigned only in ISO C90

Mac에서 GCC 4.2.1 사용 - 많은 경고가 활성화 된 (gcc -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Werror) - 나는 그 경고를 얻지 못합니다. 흥미 롭습니다.) 나는 그것을 추가하여 그것을 제거 할 수 있습니다. U 정수 상수에 접미사.


문제를 보는 또 다른 방법은 다음 코드와 위에 표시된 매우 까다로운 컴파일러 설정으로 설명됩니다.

#include <stdio.h>

static void print_value(unsigned int c)
{
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}

int main(void)
{
    const char str[] = "2249459722";
    unsigned int c = 2249459722;

    printf("Direct operations:\n");
    print_value(c);

    printf("Indirect operations:\n");
    if (sscanf("2249559722", "%d", &c) != 0)
        printf("Conversion failed for %s\n", str);
    else
        print_value(c);
    return(0);
}

이것은 컴파일되지 않습니다 (때문입니다 -Werror 설정) 메시지와 함께 :

cc1: warnings being treated as errors
xx.c: In function ‘main’:
xx.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’

제거하십시오 -Werror 설정 및 IT는 컴파일하지만 다음 문제를 보여줍니다. 실패 할 수있는 함수에서 오류 표시를 확인하지 않은 것입니다.

Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations:
Conversion failed for 2249459722

기본적으로 sscanf() 함수는 문자열을 서명 된 정수로 변환하지 못했다고보고합니다 (값이 너무 커서 맞지 않기 때문에 GCC 4.4.2의 경고를 참조하십시오). sscanf(), 그래서 당신은 남은 값을 사용하고있었습니다. c 당시.

따라서 코드에는 여러 가지 문제가 있습니다.

  • 그것은 특정 아키텍처를 가정합니다 (빅 엔디 언도 존재한다는 것을 인식하기보다는 리틀 엔디언).
  • 많은 경고가있는 컴파일러를 사용할 때는 깨끗하게 컴파일하지 않습니다.
  • 실패 할 수있는 기능이 실제로 성공한 것을 확인하지 않습니다.

Alok의 의견

예, 테스트 sscanf() 잘못되었습니다. 그렇기 때문에 코드 리뷰가있는 이유와 테스트중인 코드를 게시하는 데 도움이되는 이유입니다.

나는 이제 약간 당황했다 - 즉시 설명 할 수없는 일관된 행동을 얻고있다. 명백한 개정 (MACOS X 10.6.2, GCC 4.2.1, 32 비트 및 64 비트 컴파일에 대한 명백한 개정)을 사용하면 제정신 대답이 없습니다. 더 모듈 식으로 다시 작성하면 제정신 대답이됩니다.

+ cat yy.c
#include <stdio.h>

static void print_value(unsigned int c)
{
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}

int main(void)
{
    const char str[] = "2249459722";
    unsigned int c = 2249459722;

    printf("Direct operations:\n");
    print_value(c);

    printf("Indirect operations:\n");
    if (sscanf("2249559722", "%d", &c) != 1)
        printf("Conversion failed for %s\n", str);
    else
        print_value(c);
    return(0);
}


+ gcc -o yy.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’


+ ./yy.32
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations:
Integer value:  2249559722
Integer value:  0x86158EAA
Dotted decimal: 170.142.21.134 

나는 값 170.142.21.134에 대한 좋은 설명이 없습니다. 그러나 현재 내 기계에서 일관성이 있습니다.

+ gcc -o yy.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes yy.c
yy.c: In function ‘main’:
yy.c:20: warning: format ‘%d’ expects type ‘int *’, but argument 3 has type ‘unsigned int *’


+ ./yy.64
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations:
Integer value:  2249559722
Integer value:  0x86158EAA
Dotted decimal: 170.142.21.134 

동일한 값-32 비트 대신 64 비트에서도. 어쩌면 문제는 정의되지 않은 행동을 설명하려고한다는 것입니다. 정의에 따라 설명 할 수없는 (설명 할 수 없음).

+ cat xx.c
#include <stdio.h>

static void print_value(unsigned int c)
{
    unsigned char* cptr  = (unsigned char*)&c;
    printf("Integer value:  %10u\n", c);
    printf("Integer value:  0x%08X\n", c);
    printf("Dotted decimal: %u.%u.%u.%u \n", *cptr, *(cptr+1), *(cptr+2), *(cptr+3));
}

static void scan_value(const char *str, const char *fmt, const char *tag)
{
    unsigned int c;
    printf("Indirect operations (%s):\n", tag);
    fmt = "%d";
    if (sscanf(str, fmt, &c) != 1)
        printf("Conversion failed for %s (format %s \"%s\")\n", str, tag, fmt);
    else
        print_value(c);
}

int main(void)
{
    const char str[] = "2249459722";
    unsigned int c = 2249459722U;

    printf("Direct operations:\n");
    print_value(c);
    scan_value(str, "%d", "signed");
    scan_value(str, "%u", "unsigned");

    return(0);
}

이와 같은 함수 인수를 사용하면 GCC가 더 이상 가짜 형식을 발견 할 수 없습니다.

+ gcc -o xx.32 -m32 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c


+ ./xx.32
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (signed):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (unsigned):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 

결과는 여기서 일관성이 있습니다.

+ gcc -o xx.64 -m64 -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes xx.c


+ ./xx.64
Direct operations:
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (signed):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134 
Indirect operations (unsigned):
Integer value:  2249459722
Integer value:  0x8614080A
Dotted decimal: 10.8.20.134

그리고 이것들은 32 비트 케이스와 동일합니다. 나는 공식적으로 사라졌다. 주요 관찰은 정확한 상태로 유지됩니다. 조심하고, 신중한 컴파일러 경고 (및 컴파일러 경고를 이끌어 내고) "모든 세계가 인텔 칩에서 실행"한다고 가정하지 않습니다 (전 세계가 vax ", 오래 전에 옛날에!).

다른 팁

%d는 서명 된 정수를위한 것입니다

%u는 서명되지 않은 정수를위한 것입니다

편집하다:

입력이 실제로 어떻게 해석되는지 확인하려면 다음과 같이 프로그램을 수정하십시오.

#include <stdio.h>
int main()
{
 unsigned int c ; 
 unsigned char* cptr  = (unsigned char*)&c ;
 while(1)
 {
  scanf("%d",&c) ;
  printf("Signed value: %d\n",c);
  printf("Unsigned value: %u\n",c);
  printf("%u.%u.%u.%u \n",*cptr, *(cptr+1), *(cptr+2), *(cptr+3) );
 }
}

int_max보다 큰 숫자를 공급할 때 발생하는 일은 왼쪽 비트가 1입니다. 이것은 음수 값의 서명 된 정수임을 나타냅니다. 그런 다음 숫자는 그대로 해석됩니다 둘의 보완

주요 질문에 답하기 위해 :

scanf("%d", &c);

scanf()변환되는 입력을 데이터 유형으로 표현할 수없는 경우의 동작이 정의되지 않습니다. 2249459722 당신의 컴퓨터에서는에 맞지 않습니다 int, 그래서 scanf() 쓰레기를 보관하는 것을 포함하여 무엇이든 할 수 있습니다 c.

C, int 유형은 범위에 값을 저장할 수 있도록 보장됩니다. -32767 에게 +32767. an unsigned int 사이의 보장 된 값입니다 0 그리고 65535. 따라서 2249459722 조차도 적합 할 필요가 없습니다 unsigned int. unsigned long, 그러나 가치를 최대로 저장할 수 있습니다 4294967295 (232−1), 따라서 사용해야합니다 unsigned long:

#include <stdio.h>
int main()
{
    unsigned long c ;
    unsigned char *cptr  = (unsigned char*)&c ;
    while(1)
    {
        if (scanf("%lu", &c) != 1) {
            fprintf(stderr, "error in scanf\n");
            return 0;
        }
        printf("Input value: %lu\n", c);
        printf("%u.%u.%u.%u\n", cptr[0], cptr[1], cptr[2], cptr[3]);
    }
    return 0;
}

C99 컴파일러가 있으면 가능합니다 #include <inttypes.h> 그리고 사용하십시오 uint32_t 대신에 unsigned long. 그만큼 scanf() 전화가됩니다 scanf("%" SCNu32, &c);

이것을 쓰는 올바른 엔디 니스-안전 방법은입니다

printf("Dotted decimal: %u.%u.%u.%u \n", (c >> 24) & 0xff, (c >> 16) & 0xff, (c >> 8) & 0xff, (c >> 0) & 0xff);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top