Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl unpack "S*" equivalent in C

Tags:

c

perl

I'm having a little trouble understanding Perl's unpack in some code I'm reading, specifically with the S* template.

$data = "FF";
print "$data - ", unpack("S*", $data), "\n";
# > FF - 17990
  1. What is the equivalent of this in C?
  2. Why?

Thanks very much for your help

like image 576
matthewpalmer Avatar asked Mar 17 '23 07:03

matthewpalmer


2 Answers

Your code in C would look (roughly) like this:

const char *data = "FA";
unsigned short s;
memcpy( &s, data, strlen(data) );
printf("%s = %d\n", data, s);

This only handles your case with two characters, while unpack('S*',...) will return a list of shorts corresponding to its input.

Why? One of the primary motivations for pack and unpack was to make it easier interchange binary data with C structures.

perlpacktut is a good place to start.

like image 155
Ben Grimm Avatar answered Mar 19 '23 23:03

Ben Grimm


unpack 'S' casts two bytes into a uint16_t.

#include <stdint.h>
const char *data = "\x46\x41";
uint16_t n;
memcpy(&n, data, sizeof(n));  // n = 0x4146 or 0x4641

Don't forget to check the number of bytes in data before doing this!


Notice that it can give two different results based on the system.

On a little-endian system (e.g. x86, x64), unpack 'S' is also equivalent to

uint16_t n = (data[1] << 8) | data[0];  // 0x4146

On a big-endian system, unpack 'S' is also equivalent to

uint16_t n = (data[0] << 8) | data[1];  // 0x4641

By the way, you might be tempted to do the following, but it's not portable due to memory alignment issues:

uint16_t n = *((const uint16_t *)data);
like image 20
ikegami Avatar answered Mar 19 '23 23:03

ikegami