2016-02-15 2 views
1

У меня есть небольшая проблема в моем программном обеспечении C-Embedded. На самом деле я хотел бы преобразовать указатель на указатель на целочисленный указатель, но я сталкиваюсь с некоторыми проблемами ...Преобразование указателя в конкатенацию

Фактически я заполняю char-table и хотел бы прочитать этот буфер в целочисленной переменной. Может ли бросок разрешать конкатенацию 4-символа на одно целое?

Пример:

char tab[4] = {0x01,0x02,0x03,0x04}; 

, и я хотел бы получить целое число, содержащее значение 0x01020304. Я попытался это сделать, но я не получил желаемое значение:

val_int =*((int*)tab); 

Не могли бы вы дать мне совет? Обязательно ли бросать каждую ячейку один раз?

+0

И какое значение вы получаете? –

ответ

3

Вы не можете писать код, как это, так как это нарушает "strict aliasing" rule. Который, просто поставил, диктует, что компилятор свободен предположить, что ваш массив символов никогда не обращается через указатель на int. Из-за этого компилятор может оптимизировать большие части вашего кода.

Например, он может решить, что весь массив символов никогда не используется вашей программой и полностью удаляет его. Поэтому результат вашего кода имеет неопределенное поведение.

Это, как говорится, даже если ваш код будет работать так, как вы планировали, он все равно будет зависящим от последовательности. Если портативность/порядок байтов не касается, то вы можете использовать союз, который сделает код безопасным против указателя алиасов ошибок:

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

typedef union 
{ 
    uint32_t val32; 
    uint8_t val8 [sizeof(uint32_t)]; 
} val_t; 


int main (void) 
{ 
    val_t v = {.val8 = {0x01,0x02,0x03,0x04} }; 

    printf("%.8" PRIx32, v.val32); 

    return 0; 
} 
+0

Ну, char на самом деле является исключением: «тип символа может быть псевдонимом любого другого типа». –

+1

@AlexSkalozub Только в том случае, если бросок был _from_ int _to_ char pointer. А не наоборот. '(char *) & my_int' не нарушает сглаживание, но' (int *) char_array' делает. – Lundin

0

На машинке Маленькой Эндианки, которую можно выполнить, но вам придется повторно организовать свой начальный массив символов, чтобы получить ожидаемый результат. Вот пример на X86:

char tab[] = {0x04,0x03,0x02,0x01}; 
unsigned int *p_int = (unsigned int *)tab; 
printf("val = 0X%X \n", *p_int); 
+2

нарушая «правило строгого сглаживания»? –

+0

Да, да.Этот код, как и код в вопросе, вызывает неопределенное поведение. – Lundin

+0

@ Lundin - хорошо спасибо за ссылку, я посмотрю на это. – artm

3

Чтобы не зависеть от endiness вашей платформы:

const uint32_t val_int = (tab[0] << 24) | (tab[1] << 16) | (tab[2] << 8) | tab[3]; 
+0

'const uint32_t val_int = (вкладка [0] << CHAR_BIT * 3) | (вкладка [1] << CHAR_BIT * 2) | (вкладка [2] << CHAR_BIT) | вкладка [3]; 'может быть лучше. Встроенные устройства могут быть странными. –

1

Вы можете использовать C union Это указывает на то, что данные могут быть доступны различные типы :

#include <stdio.h> 
#include <stdlib.h> 

union data{ 
    int i; 
    char arr[4]; 
}; 

int main() 
{ 
    union data d; 
    d.arr[0] = 0x01; 
    d.arr[1] = 0x02; 
    d.arr[2] = 0x03; 
    d.arr[3] = 0x04; 

    printf("the value: %#010x\n", d.i); //outputs 0x4030201 on my little endian computer 

    return 0; 
} 

отметить также Endianess это может сделать ваш результат выглядеть на другом порядке, чем вы ожидали.

+1

Очень сомнительная практика использования подписанных типов для объединения. Не только 'int' подписан,' char' имеет определенную приложением подпись. – Lundin

+0

@ Lundin интересный момент, ради изучения, что вы подразумеваете под обозначением, определяющим реализацию символа? и как он может иметь какой-либо эффект, если я укажу шестнадцатеричное значение, которое будет храниться в символе? –

+0

'char' - дисфункциональный тип, который не соответствует тем же правилам, что и другие стандартные типы данных. Он может быть подписан или не подписан, это зависит от компилятора. Поэтому вы никогда не должны использовать 'char' для чего угодно, кроме текстовых строк. Если вам нужен 1-байтовый тип данных, используйте 'uint8_t'. – Lundin

0

Существует еще одна проблема (помимо уже упомянутых) о встроенном виде, которая может возникнуть из такого кода.

На некоторых платформах инструкции чтения (и записи) должны быть выровнены с размером считываемых данных (записано), то есть 8-битное считывание не выравнивается, 16-разрядное чтение выравнивается с границей 2 байта и 32- бит считывается с 4 байтами.

Когда вы назначаете свой массив байтов, его начало вообще не выравнивается (потому что это байты), но когда вы отбрасываете его на int* и читаете его, будет использоваться 32-разрядная инструкция чтения.

В результате вы получите случайный сбой.

Смежные вопросы