2009-02-13 39 views
12

Tôi muốn đọc sizeof(int) byte từ một mảng char*.Đọc byte kích thước "số nguyên" từ mảng char *.

a) Trong trường hợp nào chúng ta cần phải lo lắng nếu cần kiểm tra độ tin cậy?

b) Làm thế nào bạn đọc 4 byte đầu tiên hoặc xem xét tính cuối cùng.

EDIT:sizeof(int) byte mà tôi đã đọc cần được so sánh với giá trị số nguyên.

phương pháp tốt nhất để đi về vấn đề này là gì

+0

Tôi hơi bối rối về những gì bạn đang cố gắng làm. Bạn có thể viết một số mã giả, làm ví dụ không? Bạn đang cố phân tích các số nguyên từ mảng ký tự? –

+0

Tôi đang cố gắng tìm các byte sizeof (int) từ một mảng char * và cố gắng so sánh nó với một số nguyên. Nguồn dữ liệu là một máy khác. – kal

Trả lời

1

Bạn không cần phải lo lắng về endianess trừ khi bạn đang đọc các byte từ một nguồn tạo ra trên một máy khác nhau, ví dụ luồng mạng.

Cho rằng, bạn không thể sử dụng vòng lặp for?

void ReadBytes(char * stream) { 
    for (int i = 0; i < sizeof(int); i++) { 
     char foo = stream[i]; 
     } 
    } 
} 

Bạn có yêu cầu điều gì đó phức tạp hơn không?

+0

Dữ liệu của tôi thực sự được tạo từ một nguồn khác nhau – kal

1

Bạn chỉ cần lo lắng về endianess nếu dữ liệu bạn đang đọc bao gồm các số lớn hơn một byte.
nếu bạn đang đọc byte sizeof (int) và mong muốn diễn giải chúng dưới dạng int thì cuối cùng sẽ tạo ra sự khác biệt. về cơ bản là endianness là cách thức máy tính diễn giải một chuỗi nhiều hơn 1 byte thành một giá trị số.

3

Phụ thuộc vào cách bạn muốn đọc chúng, tôi nhận được những cảm giác bạn muốn đúc 4 byte vào một số nguyên, làm như vậy qua mạng trực tiếp dữ liệu thường sẽ kết thúc trong một cái gì đó như thế này:

int foo = *(int*)(stream+offset_in_stream); 
+1

Điều đó có thể dẫn đến truy cập chưa được ký. – gimpf

+0

@gimpf: Tôi tò mò: trên thực tế hệ thống nào sẽ dẫn đến lỗi? – Christoph

+0

I.e. trên 80486 và bất kỳ CPU nào tốt hơn với bộ Align-Flag. –

18

Đỗ bạn có nghĩa là một cái gì đó như thế ?:

char* a; 
int i; 
memcpy(&i, a, sizeof(i)); 

Bạn chỉ phải lo lắng về việc kết thúc nếu nguồn dữ liệu từ một nền tảng khác, như thiết bị.

+0

Điều gì có thể rõ ràng hơn? : D –

+0

Đây là một cách hợp pháp tốt đẹp mà không vi phạm các quy tắc loại bỏ/bí danh loại. Đối với những người tự hỏi, "nhưng nó có vẻ chậm hơn so với một diễn viên!" a) các diễn viên là hành vi không xác định, do đó, chỉ cần không đi có b) mã được tạo ra là không có khác nhau cho x86/x64: https://godbolt.org/g/gxtVFZ – Eloff

1

Chỉ cần sử dụng vòng lặp for di chuyển qua mảng trong các khối sizeof (int).
Sử dụng hàm ntohl (được tìm thấy trong tiêu đề <arpa/inet.h>, ít nhất là trên Linux) để chuyển đổi từ byte theo thứ tự mạng (thứ tự mạng được định nghĩa là thứ tự lớn) thành thứ tự byte cục bộ. Chức năng thư viện đó được thực hiện để thực hiện chuyển đổi từ mạng đến máy chủ chính xác cho bất kỳ bộ xử lý nào bạn đang chạy.

+0

Tất nhiên, điều này chỉ áp dụng nếu bạn thực sự đọc một cái gì đó từ mạng ... – gimpf

+0

Ok, ông đã nói trong _comment_ rằng ông đang đọc nó từ một máy khác. Vâng, có thể được thực hiện bằng cách đốt/đọc một đĩa CD, nhưng có lẽ anh ta thực sự có nghĩa là một số loại mạng. – gimpf

9

a) Bạn chỉ cần lo lắng về "endianness" (tức là, hoán đổi byte) nếu dữ liệu được tạo trên máy tính lớn và đang được xử lý trên máy tính nhỏ hoặc ngược lại. Có nhiều cách có thể xảy ra, nhưng đây là một vài ví dụ.

  1. Bạn nhận dữ liệu trên máy Windows qua ổ cắm. Windows sử dụng kiến ​​trúc nhỏ gọn trong khi dữ liệu mạng là "được cho là" ở định dạng lớn.
  2. Bạn xử lý tệp dữ liệu được tạo trên hệ thống có "độ dài cuối cùng" khác.

Trong cả hai trường hợp này, bạn sẽ cần phải chuyển đổi byte tất cả các số lớn hơn 1 byte, ví dụ:, quần short, ints, longs, double, etc. Tuy nhiên, nếu bạn luôn xử lý dữ liệu từ cùng một nền tảng, các vấn đề cuối cùng sẽ không đáng lo ngại.

b) Dựa trên câu hỏi của bạn, có vẻ như bạn có con trỏ char và muốn trích xuất 4 byte đầu tiên dưới dạng int rồi xử lý bất kỳ vấn đề nào về cuối. Để thực hiện việc trích xuất, hãy sử dụng tính năng này:

int n = *(reinterpret_cast<int *>(myArray)); // where myArray is your data 

Rõ ràng, điều này giả định myArray không phải là con trỏ rỗng; nếu không, điều này sẽ sụp đổ vì nó dereferences con trỏ, do đó, sử dụng một chương trình lập trình phòng thủ tốt.

Để trao đổi các byte trên Windows, bạn có thể sử dụng các hàm ntohs()/ntohl() và/hoặc htons()/htonl() được định nghĩa trong winsock2.h. Hoặc bạn có thể viết một số thói quen đơn giản để làm điều này trong C++, ví dụ:

inline unsigned short swap_16bit(unsigned short us) 
{ 
    return (unsigned short)(((us & 0xFF00) >> 8) | 
          ((us & 0x00FF) << 8)); 
} 

inline unsigned long swap_32bit(unsigned long ul) 
{ 
    return (unsigned long)(((ul & 0xFF000000) >> 24) | 
          ((ul & 0x00FF0000) >> 8) | 
          ((ul & 0x0000FF00) << 8) | 
          ((ul & 0x000000FF) << 24)); 
} 
+1

u nên đề cập đến đoạn mã đầu tiên có cùng vấn đề như Daniels ': nó có thể truy cập dữ liệu không được ký hiệu không phù hợp với int * –

3

Cách đơn giản để giải quyết việc này là để đảm bảo bất cứ điều gì tạo ra các byte làm như vậy trong một endianness nhất quán. Thông thường, "thứ tự byte mạng" được sử dụng bởi nhiều thứ TCP/IP khác nhau là tốt nhất: các thói quen thư viện htonlntohl hoạt động rất tốt với điều này, và chúng thường được tối ưu hóa khá tốt.

Tuy nhiên, nếu thứ tự byte mạng không được sử dụng, bạn có thể cần thực hiện mọi việc theo các cách khác nhau theo cách . Bạn cần phải biết hai điều: kích thước của một số nguyên và thứ tự byte. Khi bạn biết điều đó, bạn biết số byte cần trích xuất và thứ tự để đặt chúng lại với nhau thành một int.

Một số mã ví dụ, cho rằng sizeof (int) là số bên phải của byte:

#include <limits.h> 

int bytes_to_int_big_endian(const char *bytes) 
{ 
    int i; 
    int result; 

    result = 0; 
    for (i = 0; i < sizeof(int); ++i) 
     result = (result << CHAR_BIT) + bytes[i]; 
    return result; 
} 

int bytes_to_int_little_endian(const char *bytes) 
{ 
    int i; 
    int result; 

    result = 0; 
    for (i = 0; i < sizeof(int); ++i) 
     result += bytes[i] << (i * CHAR_BIT); 
    return result; 
} 


#ifdef TEST 

#include <stdio.h> 

int main(void) 
{ 
    const int correct = 0x01020304; 
    const char little[] = "\x04\x03\x02\x01"; 
    const char big[] = "\x01\x02\x03\x04"; 

    printf("correct: %0x\n", correct); 
    printf("from big-endian: %0x\n", bytes_to_int_big_endian(big)); 
    printf("from-little-endian: %0x\n", bytes_to_int_little_endian(little)); 
    return 0; 
} 

#endif 
+0

Bây giờ thay thế "int" bằng "unsigned" và câu trả lời của bạn là chính xác;) –

+1

tôi sẽ thay thế + và + = với | và | = tương ứng. nó là khó hiểu để sử dụng toán tử toán học ở đây imho. –

1

Tại sao đọc khi bạn chỉ có thể so sánh?

bool AreEqual(int i, char *data) 
{ 
    return memcmp(&i, data, sizeof(int)) == 0; 
} 

Nếu bạn lo lắng về tính cuối cùng khi bạn cần chuyển đổi tất cả các số nguyên thành một dạng bất biến nào đó. htonl và ntohl là những ví dụ hay.

+0

Điều này sẽ luôn trả về false. Tôi nghĩ bạn có nghĩa là memcmp(), không phải memcpy(). –

+0

Cảm ơn bạn đã sửa. – okutane

3

Làm thế nào về

int int_from_bytes(const char * bytes, _Bool reverse) 
{ 
    if(!reverse) 
     return *(int *)(void *)bytes; 

    char tmp[sizeof(int)]; 

    for(size_t i = sizeof(tmp); i--; ++bytes) 
     tmp[i] = *bytes; 

    return *(int *)(void *)tmp; 
} 

Bạn muốn sử dụng nó như thế này:

int i = int_from_bytes(bytes, SYSTEM_ENDIANNESS != ARRAY_ENDIANNESS); 

Nếu bạn đang ở trên một hệ thống nơi đúc void *-int * có thể dẫn đến xung đột liên kết, bạn có thể sử dụng

int int_from_bytes(const char * bytes, _Bool reverse) 
{ 
    int tmp; 

    if(reverse) 
    { 
     for(size_t i = sizeof(tmp); i--; ++bytes) 
      ((char *)&tmp)[i] = *bytes; 
    } 
    else memcpy(&tmp, bytes, sizeof(tmp)); 

    return tmp; 
} 
Các vấn đề liên quan