Копирование строкового литерала в массив uint32_t и доступ к нему - PullRequest
1 голос
/ 08 марта 2020

Я вставил код ниже, но это было предложено как плохое решение. В стандарте сказано следующее о memcpy:

"Функция memcpy копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1. Если копирование происходит между объектами, которые перекрываются, поведение не определено. "

и это о uint32_t:

" Имя typedef uintN_t обозначает целочисленный тип без знака с шириной N и без битов заполнения. Таким образом, uint24_t обозначает такой целочисленный тип без знака с шириной ровно 24 бита. "

Есть ли проблемы с выравниванием? Я всегда использовал это на linux и никогда не сталкивался с ошибками или чем-то подобным. Я использую битовые операции доступа, когда мне нужно было беспокоиться о порядке байтов, например, при получении данных по ссылке из другой архитектуры. Просьба пролить немного света.

#include <stdio.h>
 #include<string.h>
 #include<stdint.h>
 char* pointer = "HelloWorld!Hell!";
 uint32_t arr[4];
 unsigned char myArray[16];
 int main(void) {
     memcpy(arr, pointer, (size_t)16);

     // Is this illegal ? 
     char *arr1 = (char *)arr;

     for(int i = 0 ; i < 16; i++)
     {
         printf("arr[%d]=%c\n", i, arr1[i]);
     }
 }

1 Ответ

2 голосов
/ 08 марта 2020

Звонок на memcpy в порядке. Здесь у вас есть неопределенное поведение:

printf("%s\n", arr);

Спецификатор формата %s ожидает аргумент char *, но вы передаете uint32_t *. Такое несоответствие аргумента - неопределенное поведение. Два типа указателей могут иметь одинаковое представление в вашей системе, но это не обязательно верно в общем случае.

Даже если типы совпадают, вы бы все еще имели бы UB, потому что arr недостаточно велик, чтобы содержать строку "HelloWorld!Hell!". Эта строка (включая нулевой завершающий байт) имеет ширину 17 байт, поэтому нулевой терминатор не копируется. Затем printf читает после конца массива, который является UB.

В качестве примера я изменил список переменных следующим образом:

 uint32_t x = 0x11223344;
 uint32_t arr[4] = { 1, 2, 3, 4 };
 uint32_t y = 0x55667788;

И получил следующий вывод:

HelloWorld!Hell!�wfU

Что касается этого:

char *arr1 = (char *)arr;

Это допустимо, поскольку указатель одного типа объекта может быть преобразован в указатель на другой тип объекта. Кроме того, поскольку типом назначения является char *, разрешается разыменовывать этот указатель для доступа к базовым байтам исходного объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...