C: неожиданный результат при приведении массива в sizeof () - PullRequest
2 голосов
/ 07 апреля 2020

Мне пришлось испытать (еще одно) - для меня - неожиданное C поведение, на этот раз с sizeof.

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

У меня есть строка, определенная с помощью макроса -процессора #define - C (#define CONST "foobar" ) и использовать его в таких функциях, как:

senddata(uint8_t * data, uint32_t len).

Поскольку - в зависимости от реализации / архитектуры, но, по крайней мере, для x86 - по умолчанию подписывается char, я получаю предупреждение о «отличии [ence] в подписи», когда он называется так:

senddata(CONST, sizeof(CONST)).

Так что мне придется разыграть его (senddata((uint8_t *)CONST, sizeof(CONST))) на каждый senddata - invocation.

Поскольку все случаи использования CONST в моем коде на самом деле будут приводиться к uint8_t, я решил, что просто изменю определение:

#define CONST "foobar" -> #define CONST ((uint8_t *)"foobar")

и больше не нужно беспокоиться о дальнейшем касте.

Хотя это действительно удаляет предупреждение, и все выглядит хорошо, мне пришлось изучить сложный путь, который в этих случаях sizeof () на самом деле больше не возвращает длину строки, а размер типа данных, в данном случае uint8_t *.

Мне это не кажется очевидным.

Поэтому мой вопрос сложен в 2 раза:

  • 1) Как бы Я делаю это правильно в приведенном выше сценарии?
  • 2) Почему это так?
  • 3) Как бы я мог узнать об этом? Это не означает (пассивно) агрессивно, а скорее: с каким предварительным знанием я должен был прийти к выводу, что это не работает таким образом?

Некоторая половина знаний, которые я выбрал где-то, что может сыграть в это, но я не совсем уверен, что из этого получится: sizeof () - это не обычная функция, а оператор (например, sizeof int работает без скобок).

Еще одно предположение у меня: "foobar" - массив символов, а (char *)"foobar" - указатель.

Ответы [ 2 ]

4 голосов
/ 07 апреля 2020

Вы почти там, и вам нужно помнить, что sizeof работает с типом операнда, а не со значением.

Цитата C11, глава §6.5.3.4

Оператор sizeof возвращает размер (в байтах) своего операнда, который может быть выражением или именем типа в скобках. Размер определяется по типу операнда. Результатом является целое число. Если тип операнда является типом массива переменной длины, операнд оценивается; в противном случае, операнд не вычисляется, и результатом является целочисленная константа.

Например, sizeof ("array") совпадает с sizeof (char [6]), поскольку "array" имеет тип char[6]. Учитывая, что размер char определен как 1, он даст результат 6.

Однако, когда вы используете приведение для операнда sizeof, он считает, что приведение как тип согласно приведенному выше определению. Итак, что-то вроде sizeof ((char*)"array") будет таким же, как sizeof (char*). В зависимости от вашей платформы, он может дать значение 4 или 8, размер указателя (до char).

0 голосов
/ 07 апреля 2020

Вы приводите CONST - строковый литерал, который действует как массив const char CONST[] - в целое число . В таком контексте массивы вырождаются в указатели, поэтому вы в основном приводите указатель к строке в целое число . Это не то, что вы хотите. Ваш send не будет делать ничего разумного, если ваша архитектура не имеет 8-битных указателей (это 1 байт, а не 8 байт!).

Что вам действительно нужно, так это следующая подпись для отправки: обратите внимание, что он также является правильным:

void send(const void *, size_t);

И тогда вам понадобится вспомогательный макрос:

#define send_lit(literal) send(literal "", sizeof(literal))

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

Полный пример:

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

#define send_lit(literal) send(literal "", sizeof(literal))

inline void send_byte(uint8_t byte) {
  // just an example implementation
  printf("%c", (char)byte);
}

void send(const void *src, size_t size) {
  const char *p = (const char *)src;
  while (size)
    send_byte((uint8_t)*p++);
}

int main() {
  send_lit("Hello, world!\n");
#if 0
  const char* p;
  send_lit(p); // won't compile
#endif
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...