2012-07-11 2 views
0

몇 가지 uint32_t 및 uint16_t 숫자를 char *에 넣어야합니다. 그런 다음 버퍼에서 다시 가져와야합니다.uint32/16_t에서 문자열을 만든 다음 원본 숫자를 다시 구문 분석하십시오.

몇 가지 질문을 읽고 sprintf를 사용하여 char *에 넣고 sscanf가 원래 숫자를 다시 얻으려고했습니다. 그러나, 나는 그들을 정확하게 얻을 수 없습니다.

다음은 2 개의 숫자 만있는 코드 예입니다. 하지만 2 이상이 필요합니다. 그래서 realloc을 사용합니다. 또한, 나는 sprintf를 사용하는 방법을 모르고 제대로 uint16_t와

uint32_t gid = 1100; 
uint32_t uid = 1000; 
char* buffer = NULL; 
uint32_t offset = 0; 

buffer = realloc(buffer, sizeof(uint32_t)); 
sprintf(buffer, "%d", gid); 
offset += sizeof(uint32_t); 

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 
sprintf(buffer+sizeof(uint32_t), "%d", uid); 

uint32_t valorGID; 
uint32_t valorUID; 

sscanf(buffer, "%d", &valorGID); 
buffer += sizeof(uint32_t); 
sscanf(buffer, "%d", &valorUID); 

printf("ValorGID %d ValorUID %d \n", valorGID, valorUID); 

그리고 내가 무엇을 얻을 것이 내가 할 필요가있는 무엇

ValorGID 11001000 ValorUID 1000 

입니다 sscanf에서 내가

ValorGID 1100 ValorUID 1000 

입니다 C 언어의 새로운 기능이므로 어떤 도움을 주시면 감사하겠습니다.

답변

1
buffer = realloc(buffer, sizeof(uint32_t)); 
sprintf(buffer, "%d", gid); 
offset += sizeof(uint32_t); 

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 
sprintf(buffer+sizeof(uint32_t), "%d", uid); 

: 예를 들어.

보통 CHAR_BIT == 8이라고 가정하면, sizeof(uint32_t) == 4입니다. 또한 int은 패딩 비트가없는 2의 보수 표현으로 부호있는 32 비트 정수라고 가정합시다.

sprintf(buffer, "%d", gid)gid의 비트 패턴 표현을 버퍼에 int으로 해석하여 인쇄합니다. 위의 가정하에 gid은 -2147483648과 2147483647 사이의 숫자로 해석됩니다. 따라서 십진수 문자열 표현은 '-'을 포함 할 수 있으며 1에서 10 자리 숫자와 0 종결자를 포함하며 모두 2에서 12 바이트를 사용합니다. 그러나 4 바이트 만 할당 했으므로 999 < gid < 2^32-99 (부호가있는 2의 보수가 > 999 또는 < -99) 인 경우 sprintf은 할당 된 버퍼 크기를 초과하여 씁니다.

이것은 정의되지 않은 동작입니다.

일반적으로 4 바이트를 할당하면 메모리가 더 효율적으로 사용되므로 (예 : malloc이 16 바이트로 정렬 된 블록을 반환하는 경우 할당 된 4 바이트 바로 뒤에 12 바이트를 다른 부분에서 사용할 수 없음) 프로그램이지만 프로그램의 주소 공간에 속하며, 아마도 그들에게 글쓰기는 감지되지 않을 것입니다).그러나 할당 된 청크의 끝이 페이지 경계에있을 때 쉽게 추락 할 수 있습니다.

sprintf s에 대해 쓰기 오프셋을 4 바이트 씩 늘리므로 문자열 표현 (0-termnator 제외)이 4 바이트 이상을 사용하면 이전 숫자의 일부가 덮어 쓰여집니다 (프로그램이 아직 할당되지 않은 메모리에 쓰기 때문에 충돌).

라인

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 

는 상기 에러를 포함한다.

  1. buffer = realloc(buffer, new_size);는 할당 된 메모리에 대한 참조를 잃고 realloc 실패 할 경우 누설을 야기한다. 임시을 사용하여 새로운 할당의 성공

    char *temp = realloc(buffer, new_size); 
    if (temp == NULL) { 
        /* reallocation failed, recover or cleanup */ 
        free(buffer); 
        exit(EXIT_FAILURE); 
    } 
    /* it worked */ 
    buffer = temp; 
    /* temp = NULL; or let temp go out of scope */ 
    
  2. 새로운 크기 sizeof(uint32_t) + sizeof(buffer)를 확인하는 것은 항상 sizeof(uint32_t) + sizeof(char*) 동일합니다. 일반적으로 8 바이트 또는 12 바이트이므로 할당 된 영역 외부에서 쓰고 크래시 나 메모리 손상 (많은 경우 나중에 크래시가 발생할 수 있음)이 발생하지 않도록 많은 숫자가 필요하지 않습니다.

당신 buffer에 할당 된 바이트 수를 추적하고 새 크기를 계산하는 것을 사용해야합니다. 포인터에서 시작까지 할당 된 메모리 블록의 크기를 결정하는 (portable¹) 방법은 없습니다.


이제 문자열 표현 또는 비트 패턴을 버퍼에 저장할지 여부가 문제입니다.

문자열 표현을 저장하면 문자열 표현의 길이가 값에 따라 달라진다는 문제점이 있습니다. 따라서 숫자의 표현 사이에 구분 기호를 포함 시키거나 필요에 따라 모든 표현의 길이를 공백이나 선행 0으로 채워서 동일한 길이를 유지해야합니다. 예를 들어

#include <stdint.h> 
#include <inttypes.h> 

#define MAKESTR(x) # x 
#define STR(x) MAKESTR(x) 

/* A uint32_t can use 10 decimal digits, so let each field be 10 chars wide */ 
#define FIELD_WIDTH 10 

uint32_t gid = 1100; 
uint32_t uid = 1000; 

size_t buf_size = 0, offset = 0; 
char *buffer = NULL, *temp = NULL; 
buffer = realloc(buffer, FIELD_WIDTH + 1); /* one for the '\0' */ 
if (buffer == NULL) { 
    exit(EXIT_FAILURE); 
} 
buf_size = FIELD_WIDTH + 1; 
sprintf(buffer, "%0" STR(FIELD_WIDTH) PRIu32, gid); 
offset += FIELD_WIDTH; 

temp = realloc(buffer, buf_size + FIELD_WIDTH); 
if (temp == NULL) { 
    free(buffer); 
    exit(EXIT_FAILURE); 
} 
buffer = temp; 
temp = NULL; 
buf_size += FIELD_WIDTH; 
sprintf(buffer + offset, "%0" STR(FIELD_WIDTH) PRIu32, uid); 
offset += FIELD_WIDTH; 
/* more */ 

uint32_t valorGID; 
uint32_t valorUID; 

/* rewind for scanning */ 
offset = 0; 

sscanf(buffer + offset, "%" STR(FIELD_WIDTH) SCNu32, &valorGID); 
offset += FIELD_WIDTH; 
sscanf(buffer + offset, "%" STR(FIELD_WIDTH) SCNu32, &valorUID); 

printf("ValorGID %u ValorUID %u \n", valorGID, valorUID); 

같이 0으로 채워진 고정 너비 필드가 사용됩니다. 너비가 고정 너비보다 큰 분리 기호를 사용하려면 필요한 길이와 오프셋의 계산이 더 복잡해 지지만 숫자가 크지 않으면 공간을 덜 사용하게됩니다. 당신이 알고있는 경우

size_t buf_size = 0, offset = 0; 
unsigned char *buffer = NULL, temp = NULL; 
buffer = realloc(buffer, sizeof(uint32_t)); 
if (buffer == NULL) { 
    exit(EXIT_FAILURE); 
} 
buf_size = sizeof(uint32_t); 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    buffer[offset + b] = (gid >> b*8) & 0xFF; 
} 
offset += sizeof(uint32_t); 

temp = realloc(buffer, buf_size + sizeof(uint32_t)); 
if (temp == NULL) { 
    free(buffer); 
    exit(EXIT_FAILURE); 
} 
buffer = temp; 
temp = NULL; 
buf_size += sizeof(uint32_t); 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    buffer[offset + b] = (uid >> b*8) & 0xFF; 
} 
offset += sizeof(uint32_t); 

/* And for reading the values */ 
uint32_t valorGID, valorUID; 

/* rewind */ 
offset = 0; 
valorGID = 0; 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    valorGID |= buffer[offset + b] << b*8; 
} 
offset += sizeof(uint32_t); 
valorUID = 0; 
for(size_t b = 0; b < sizeof(uint32_t); ++b) { 
    valorUID |= buffer[offset + b] << b*8; 
} 
offset += sizeof(uint32_t); 

같은 것을 사용하십시오

오히려 저장하는 가장 컴팩트 한 방법이 될 것이다 비트 패턴을 저장하고 싶은 경우는, ¹ 방법 malloc 등 작업 구현시 malloc의 부기 데이터에서 크기를 찾을 수 있습니다.

+0

귀하의 의견에 진심으로 감사드립니다. 내가 말했듯이, 나는 C.에서 새로운 것이다. 나는 realloc이 실패 하는지를 확인해야한다는 것을 몰랐다. 첫 번째 해결 방법은 문자열 표현입니다. 그것을 구현할 것입니다. 다시 한 번 감사드립니다. – Fdiazreal

+0

반갑습니다. 나는 당신이 chastising하고 있다는 인상을받지 못했기를 바랍니다. 요점은 C에서 프로그래머는 에러 검사를해야한다는 것입니다. 코드의 어떤 부분에서 잘못되었는지 정확히 알려주는 멋진 예외는 없습니다. 물론,'malloc/realloc'의 실패를 검사하는 것은 실패를 결코 감지 할 수없는 좋은 기회입니다. 하지만 한 번만 해내면 기꺼이받을 수 있습니다. –

0
uint32_t gid = 1100; 
uint32_t uid = 1000; 
char* buffer = NULL; 
uint32_t offset = 0; 

buffer = realloc(buffer, sizeof(uint32_t)); 
sprintf(buffer, "%d", gid); 
offset += sizeof(uint32_t); 

buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer)); 
sprintf(buffer+sizeof(uint32_t), "%d", uid); 

uint32_t valorGID; 
uint32_t valorUID; 

sscanf(buffer, "%4d", &valorGID); 
buffer += sizeof(uint32_t); 
sscanf(buffer, "%d", &valorUID); 

printf("ValorGID %d ValorUID %d \n", valorGID, valorUID); 
`

나는이 문제를 해결할 수 있습니다 생각!

+0

예.하지만 하나 이상의 번호를 추가해야합니다. 그리고 때로는 4 자리 이상. – Fdiazreal

1

형식 지정자 '%d'int이고 따라서 uint32_t은 잘못되었습니다. 먼저 uint32_t은 부호없는 유형이므로 적어도 '%u'을 사용해야하지만 int 또는 unsigned과 다른 너비가있을 수도 있습니다. 표준에서 예상되는 매크로는 printf의 경우 PRIu32이고 scanf의 경우 SCNu32입니다. 이건 정말 이해가되지 않습니다, 그리고 운이 좋은 경우를 제외하고 의도 한대로 작동하지 않습니다

sprintf(buffer, "%" PRIu32, gid); 
+0

해당 매크로를 인쇄 및 기타 기능에도 사용할 수 있습니까? 고맙습니다! – Fdiazreal

+0

물론, 다른 유형과 지정자에 대해 비슷한 매크로가 많이 있습니다. –

1

sprintf가 반환하는 표현은 char *입니다. 문자열 배열로 정수 배열을 저장하려는 경우 기본 데이터 형식은 char **입니다. 문자열 데이터 자체 만 저장하는 경우 이는 불분명 한 행렬입니다. 그러나 uint32_t이 가져올 수있는 가장 긴 문자열은 종료 문자가 null 인 경우 10 자입니다. 각 문자열을 보유하기 위해이 많은 바이트를 미리 할당하는 것이 좋습니다.

그래서 저장하는 N 문자열로 배열 S의 어레이 A로부터 uint32_t 년대 :

const size_t kMaxIntLen=11; 

uint32_t *a,b; 
// fill a somehow 
... 

size_t n,i; 
char **s.*d; 

if((d=(char*)malloc(n*kMaxIntLen))==NULL) 
    // error! 
if((s=(char**)malloc(n*sizeof(char*)))==NULL) 
    // error! 
for(i=0;i<n;i++) 
    { 
    s[i]=d+i; // this is incremented by sizeof(char*) each iteration 
    snprintf(s[i],kMaxIntLen,"%u",a[i]); // snprintf to be safe 
    } 

이제 번째 번호 [I]는 때문에 단지 printf("%s",s[i]); 인 인쇄 및 정수로 검색 (S)에있을 bsscanf(s[i],"%u",&b);입니다.

이후의 메모리 관리는 조금 까다 롭습니다. 버퍼를 늘리려면 항상 realloc()을 사용하는 대신, 메모리 덩어리를 미리 할당하고 고갈되었을 때만 변경하는 것이 좋습니다. realloc()이 실패하면 NULL을 반환하므로 호출하기 전에 주 버퍼에 포인터를 저장하면 데이터에 대한 참조가 손실되지 않습니다. d 버퍼를 먼저 다시 할당하십시오 - 다시 여러 개의 문자열을위한 충분한 공간을 할당하십시오 - 성공하면 d이 변경되었는지 확인하십시오. 그렇다면 s 버퍼 (free())를 다시 버퍼하고 malloc() 인덱스를 다시 작성하십시오. d이 모든 인덱스를 변경 한 경우이 작업을 수행해야합니다. 그렇지 않은 경우 realloc()s 및 새 색인을 수정하십시오. 나는 그것을 운영하는 루틴의 집합, 예컨대 :

typedef struct StringArray 
{ 
char **strArray; 
char *data; 
size_t nStrings; 
} StringArray; 

이 많은 작업입니다 구조에서이 모든 것을 감싸고 가진 건의 할 것입니다. 이 있습니까? 이것은 C++ STL vector<string> 또는 list<string>istringstream 클래스와 push_back() 컨테이너 메소드를 사용하면 훨씬 더 쉽습니다.

+0

예, 저는 대학을위한 연습으로 ext2 FS에서 작업을 수행 할 소프트웨어를 작성하고 있습니다. 그리고 우리가 C를 사용하는 것은 이번이 처음입니다. – Fdiazreal

관련 문제