尝试将short int写入缓冲区start会产生奇怪的输出

时间:2015-12-08 17:54:08

标签: c pointers buffer

我已经在这个问题上被困了一段时间了,我似乎无法找到解决方案

简要描述我想做的事情。我试图生成一个随机数据缓冲区(作为一行上传到测试表中)。但是,我正在使用一个系统,该系统要求我首先使用数据生成并填充缓冲区,然后在缓冲区的最开始添加一个2字节长度说明符,以便它知道需要多少字节读入系统,然后最后是一个终止字符。

我有生成器和大多数逻辑实现(测试),除了我因为某种原因无法正确地将两个字节长度说明符放入缓冲区的开头。

    int generate() {
        char buffer[BUFSIZ];
        memset(buffer, '\0', BUFFLEN);
        unsigned short rec_len = insert_data(buffer);

        //... continue

        return 0;
    } 

    unsigned short insert_data(char* buff_ptr) {
            // Save a pointer to the start of the buffer
            char *sbuff_ptr = buff_ptr;

            // Save space to fit two bytes length specifier to be filled
            // once all my data has been generated and put into the buffer 
            buff_ptr += 2;

            // Set in the two byte record length specifier
            unsigned short rec_len = buff_ptr - sbuff_ptr;
            memcpy(sbuff_ptr, &rec_len, 2);

            // Generate data using self-implemented data generator functions
            // For example, to generate the first data value
            int id = rand_int(0);              // id = 0
            memcpy(buff_ptr, &id, 4);
            buff_ptr += 4;

            // ... This continues on until all required dummy data has been 
            // generated

            // Set in the end of record marker to indicate end of data row
            memset(buff_ptr, '\n', 1);
            buff_ptr += 1;

            // Set in the two byte record length specifier
            unsigned short rec_len = buff_ptr - sbuff_ptr;
            memcpy(sbuff_ptr, &rec_len, 2);

            if (DEBUG) printf("\n\tBUFF_PTR (END):   %p\n", buff_ptr);
            if (DEBUG) printf("\tSBUFF_PTR  (START): %p\n", sbuff_ptr);
            if (DEBUG) printf("\tRECORD_LENGTH:      %hu\n", rec_len);

            return 0;
    }

    Output
    -----------
    BUFF_PTR  (END):   0xffff9169     
    SBUFF_PTR (START): 0xffff90ce
    RECORD_LENGTH:     155       

    // Record Length: Confirmed to be correct (after manually counting up all the bytes

但是当我尝试打印出缓冲区的内容作为验证时 检查长度说明符是否设置正确

    unsigned int i;
    for(i = 0; i < rec_len; i++) {
        printf("\tbyte %d BUFFER's CONTENTS: %d\n", i, buffer[i]); 
    }

    byte 0 BUFFER's CONTENTS: -101    // This should be 155
    byte 1 BUFFER's CONTENTS: 0       // End of two byte length specifier
    byte 2 BUFFER's CONTENTS: 0       // int id = 0
    byte 3 BUFFER's CONTENTS: 0
    byte 4 BUFFER's CONTENTS: 0
    byte 5 BUFFER's CONTENTS: 0              
    byte 6 BUFFER's CONTENTS: 110     // Start of some string value
    byte 7 BUFFER's CONTENTS: 111
    byte 8 BUFFER's CONTENTS: 110
    byte 9 BUFFER's CONTENTS: 118
    ...
    byte 154 BUFFER's CONTENTS: 10   // Correct termination

在尝试调试时,我写了一个模仿我想做的小脚本,奇怪的是,这样做有用......比较两者,我找不到差异

#include <unistd.h>
#include <stddef.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

unsigned short buf_init(char* buffer) {    // -- Following are the digit       
                                // representations of each string character
char* hello   = "hello\0";      // 104, 101, 108, 108, 111, 0
char* goodbye = "goodbye\0";    // 103, 111, 111, 100, 98, 121, 101, 0
char* who     = "they\0";       // 116, 104, 101, 121, 0       

int a = 19;
float b = 20.5;
short c = 12;

char *bp = buffer;
char *bs = buffer;

// length specifier 
bp += 2; 

memcpy(bp, hello, strlen(hello));
bp += strlen(hello);

memcpy(bp, &a, 4);
bp += 4;

memcpy(bp, goodbye, strlen(goodbye));
bp += strlen(goodbye);

memcpy(bp, &b, 4);
bp += 4;

memcpy(bp, who, strlen(who));
bp += strlen(who);

memcpy(bp, &c, 2);
bp += 2;

// Terminate
memset(bp, '\n', 1);
bp += 1;

// Record Length
unsigned short len = bp - bs;
memcpy(bs, &len, 2);  

printf("rec_len: %hu\n", len);

return len;
}


int main(void) {
    char buffer[BUFSIZ];
    memset(buffer, '\0', BUFSIZ);

    unsigned short len = buf_init(buffer);

    int i = 0;
    for(i = 0; i < len; i++) {
        printf("\tbyte %d BUFFER's CONTENTS: %d\n", i, buffer[i]); 
    }

    return 0; 
}


/*  --OUTPUT
    rec_len: 29
    byte 0 BUFFER's CONTENTS: 29        // Correct Length Specifier
    byte 1 BUFFER's CONTENTS: 0
    byte 2 BUFFER's CONTENTS: 104       // "Hello"
    byte 3 BUFFER's CONTENTS: 101
    byte 4 BUFFER's CONTENTS: 108
    byte 5 BUFFER's CONTENTS: 108
    byte 6 BUFFER's CONTENTS: 111
    byte 7 BUFFER's CONTENTS: 19
    byte 8 BUFFER's CONTENTS: 0
    byte 9 BUFFER's CONTENTS: 0
    byte 10 BUFFER's CONTENTS: 0
    byte 11 BUFFER's CONTENTS: 103
    byte 12 BUFFER's CONTENTS: 111
    byte 13 BUFFER's CONTENTS: 111
    byte 14 BUFFER's CONTENTS: 100
    byte 15 BUFFER's CONTENTS: 98
    byte 16 BUFFER's CONTENTS: 121
    byte 17 BUFFER's CONTENTS: 101
    byte 18 BUFFER's CONTENTS: 0
    byte 19 BUFFER's CONTENTS: 0
    byte 20 BUFFER's CONTENTS: -92
    byte 21 BUFFER's CONTENTS: 65
    byte 22 BUFFER's CONTENTS: 116
    byte 23 BUFFER's CONTENTS: 104
    byte 24 BUFFER's CONTENTS: 101
    byte 25 BUFFER's CONTENTS: 121
    byte 26 BUFFER's CONTENTS: 12
    byte 27 BUFFER's CONTENTS: 0
    byte 28 BUFFER's CONTENTS: 10  // Terminates correctly with an new line
*/

有人愿意提出建议吗?

1 个答案:

答案 0 :(得分:1)

数据存储恰到好处。问题在于输出数据的方式。您尝试输出缓冲区,该缓冲区使用单个char

由于您使用的是小端系统,因此短路的字节顺序将被反转。意味着最不重要的字节将是第一个。从您的代码判断,类型short是您系统上的两个字节。值155将完全存储在最低有效字节中,然后它将位于缓冲区的第一个字节中,因为它首先位于内存中。

接下来输出缓冲区。此缓冲区使用单个charchar可以是签名的或未签名的,具体取决于实现。在您的实施中,它似乎已签名。 155的值在存储器中表示如下(假设2位补码为8位系统):

10011011

如您所见,第一位已设置。在2的补码系统中,这意味着对于签名类型,该值为负。由于char似乎在您的系统上签名,因此相同的序列(无符号表示为155)表示-101为有符号值。然后打印此值,结果显示-101的值。

您在支票中使用的值低于127,表示不会设置符号位,并且将按预期打印该值。如果您的测试消息长于127个字节,由于2的补码系统的工作方式,您将再次看到负值。

您可以通过将char类型包含在unsigned char中来解决此问题,以确保将它们打印为unsigned。另外,不要忘记编辑printf修饰符来表示无符号类型,而不是签名类型。这样你就会看到你的预期输出。