了解位和偏移量的 C 要求 [关闭]

Posted

技术标签:

【中文标题】了解位和偏移量的 C 要求 [关闭]【英文标题】:Understanding C Requirements of bits and offset [closed] 【发布时间】:2017-03-26 20:36:28 【问题描述】:

我了解 C 语言的一般概念以及如何制作日志文件。读取/写入文件等。

我担心的是需要以下格式:

[![在此处输入图片描述][1]][1]

我现在已经完成了很多工作,但我关心如何在第一条记录之后附加到我的日志文件中。我增加文件的记录计数(在前 2 个字节中)并在其后写入第一条记录。然后我将如何设置添加 2nd/3rd/etc 记录以逐个显示?

//confirm a file exists in the directory
bool fileExists(const char* file)

    struct stat buf;
    return (stat(file, &buf) == 0);


int rightBitShift(int val, int space)

    return ((val >> space) & 0xFF);


int leftBitShift(int val, int space)

    return (val << space);


int determineRecordCount(char * logName)

    unsigned char record[2];
    FILE *fp = fopen(logName, "rb");
    fread(record, sizeof(record), 1, fp); 

    //display the record number
    int recordNum = (record[0] << 8) | record[1];
    recordNum = recordNum +1;

    return (recordNum);



void createRecord(int argc, char **argv)

    int recordNum;
    int aux = 0;
    int dst;
    char* logName;
    char message[30];
    memset(message,' ',30);

    //check argument count and validation 
    if (argc == 7 && strcmp("-a", argv[2]) ==0 && strcmp("-f", argv[3]) ==0 && strcmp("-t", argv[5]) ==0)
    
        //aux flag on
        aux = 1;
        logName = argv[4];
        strncpy(message, argv[6],strlen(argv[6]));
    
    else if (argc == 6 && strcmp("-f", argv[2]) ==0 && strcmp("-t", argv[4]) ==0)
    
        logName = argv[3];
        strncpy(message, argv[5],strlen(argv[5]));
    
    else
    
        printf("Invalid Arguments\n");
        exit(0);
    

    //check if log exists to get latest recordNum
    if (fileExists(logName))
    
        recordNum = determineRecordCount(logName);
        printf("%i\n",recordNum);
    
    else
    
        printf("Logfile %s not found\n", logName);
        recordNum = 1;
    


    //Begin creating record
    unsigned char record[40]; /* One record takes up 40 bytes of space */
    memset(record, 0, sizeof(record));

    //recordCount---------------------------------------------------------------------
    record[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
    record[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */

    //get aux/dst flags---------------------------------------------------------------
    //get date and time
    time_t timeStamp = time(NULL);
    struct tm *date = localtime( &timeStamp );
    if (date->tm_isdst)
        dst = 1;
    record[2] |= aux << 7;  //set 7th bit
    record[2] |= dst << 6;  //set 6th

    //timeStamp-----------------------------------------------------------------------
    record[3] |= rightBitShift(timeStamp, 24);//high byte
    record[4] |= rightBitShift(timeStamp, 16);
    record[5] |= rightBitShift(timeStamp, 8);
    record[6] |= rightBitShift(timeStamp, 0); //low byte

    //leave bytes 7-8, set to 0 -----------------------------------------
    record[7] = 0;
    record[8] = 0;

    //store message--------------------------------------------
    strncpy(&record[9], message, strlen(message));


    //write record to log-----------------------------------------------------------------
    FILE *fp = fopen(logName, "w+");

     unsigned char recordCount[4];
    recordCount[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
    recordCount[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */
    recordCount[2] = 0;
    recordCount[3] = 0;

    fwrite(recordCount, sizeof(recordCount), 1, fp);

    fwrite(record, sizeof(record), 1, fp);
    fclose(fp); 

    printf("Record saved successfully\n");

【问题讨论】:

嗨,欢迎来到 ***。我们更喜欢文字而不是图像。如果您要使用图像,请像我一样嵌入它。 总和可能是忽略进位/溢出或其补码的所有八位字节(作为无符号值)的加法。 [对于 IP,总和是 16 字节实体的总和] 你能链接到完整的规范吗?缺少一些细节。 您的格式有误。 “备用(3 个字节)”,但它只有两个字节,7 和 8。 我看到你有来自 reddit 的 cross-posted 这个。请告诉我们您这样做是为了避免重复工作。 【参考方案1】:

注意:我以前从未在 C 语言中这样做过,对此持保留态度。

这是一种非常特殊的二进制格式,其中每个位都被精确计算。它使用Least-Significant-Bit numbering scheme (LSB 0),其中位的编号从7到0。

指定“高字节”在前意味着此格式为big-endian。最重要的位首先出现。这就像我们写数字的方式,四千、三百和二十一是 4321。1234 将是小端。例如,记录数和序列都是 16 位大端数。

最后,checksum 是从记录的其余部分计算出来的数字,以验证传输没有错误。规范定义了如何进行校验和。

你的工作是精确地重现这种格式,可能使用fixed-sized types found in stdint.h 或unsigned char。例如,序列是uint16_tunsigned char[2]

生成记录的函数可能有这样的签名:

unsigned char *make_record( const char *message, bool aux );

用户只需向您提供消息和辅助标志。其余的你可以通过函数计算出来。您可能决定让它们传入时间戳和序列。重点是,该函数只需要传递数据,它负责格式化。

这种字节顺序意味着您不能只写出整数,它们可能是错误的大小或错误的字节顺序。这意味着任何多字节整数都必须先序列化,然后才能将它们写入记录。 This answer covers ways to do that,我将使用来自this answer 的那些,因为事实证明它们更方便一些。

#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

unsigned char *make_record( const char *message, bool aux ) 
    // Allocate and zero memory for the buffer.
    // Zeroing means no risk of accidentally sending garbage.
    unsigned char *buffer = calloc( 40, sizeof(unsigned char) );

    // As we add to the buffer, pos will track the next byte to be written.
    unsigned char *pos = buffer;

    // I decided not make the user responsible for
    // the sequence number. YMMV.
    static uint16_t sequence = 1;    
    pos = serialize_uint16( pos, sequence );

    // Get the timestamp and DST.
    time_t timestamp = time(NULL);
    struct tm *date = localtime( &timestamp );

    // 2nd row is all flags and a bunch of 0s. Start with them all off.
    uint8_t flags = 0;
    if( aux ) 
        // Flip the 7th bit on.
        flags |= 0x80;
    
    if( date->tm_isdst ) 
        // Flip the 6th bit on.
        flags |= 0x40;
    

    // That an 8 bit integer has no endianness, this is to ensure
    // pos is consistently incremented.
    pos = serialize_uint8(pos, flags);

    // I don't know what their timestamp format is.
    // This is just a guess. It's probably wrong.
    pos = serialize_uint32(pos, (uint32_t)timestamp);

    // "Spare" is all zeros.
    // The spec says this is 3 bytes, but only gives it bytes
    // 7 and 8. I'm going with 2 bytes.
    pos = serialize_uint16(pos, 0);

    // Copy the message in, 30 bytes.
    // strncpy() does not guarantee the message will be null
    // terminated. This is probably fine as the field is fixed width.
    // More info about the format would be necessary to know for sure.
    strncpy( pos, message, 30 );
    pos += 30;

    // Checksum the first 39 bytes.
    // Sorry, I don't know how to do 1's compliment sums.
    pos = serialize_uint8( pos, record_checksum( buffer, 39 ) );

    // pos has moved around, but buffer remains at the start
    return buffer;


int main() 
    unsigned char *record = make_record("Basset hounds got long ears", true);
    fwrite(record, sizeof(unsigned char), 40, stdout);

在这一点上,我的专业知识已经耗尽,我以前从未这样做过。我很感激人们修复编辑中的小错误并建议在 cmets 中执行此操作的更好方法,例如如何处理时间戳。也许其他人可以在另一个答案中介绍如何进行 1 的补码校验和。

【讨论】:

【参考方案2】:

由于一个字节由 8 位(从 0 到 7)组成,因此您可以使用按位运算来按照您的规范要求修改它们。查看一般信息 (https://en.wikipedia.org/wiki/Bitwise_operations_in_C)。作为预览,您可以使用 >> 或

【讨论】:

以上是关于了解位和偏移量的 C 要求 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中使用带有多级指针和偏移量的 WriteProcessMemory()?

使用具有更高偏移量的 LAG 函数

时区分钟偏移量的 TimeZoneInfo

Mongo ObjectID:即使使用 pytz,“也无法比较原始偏移量和可感知偏移量的日期时间”

[洛谷P2024/POJ1182]食物链 - 带偏移量的并查集

带偏移量的并查集