Как получить предупреждение при приведении `int_least8_t` к `char`?

Я создаю библиотеку строк для поддержки как ascii, так и utf8.
Я создаю два typedef для t_ascii и t_utf8. ascii безопасно читать как utf8, но utf8 небезопасно читать как ascii.
Есть ли способ выдать предупреждение при неявном приведении от t_utf8 к t_ascii, но не при неявном приведении t_ascii к t_utf8?

В идеале я бы хотел, чтобы эти предупреждения (и только эти предупреждения) выдавались:

#include <stdint.h>

typedef char           t_ascii;
typedef uint_least8_t  t_utf8;

int main()
{
    t_ascii const* asciistr = "Hello world"; // Ok
    t_utf8 const*   utf8str = "你好世界";    // Ok

    asciistr = utf8str; // Warning: utf8 to ascii is not safe
    utf8str = asciistr; // Ok: ascii to utf8 is safe

    t_ascii asciichar = 'A';
    t_utf8   utf8char = 'B';

    asciichar = utf8char; // Warning: utf8 to ascii is not safe
    utf8char = asciichar; // Ok: ascii to utf8 is safe
}

В настоящее время при сборке с -Wall (и даже с -funsigned-char) я получаю следующие предупреждения:

gcc main.c -Wall -Wextra                          
main.c: In function ‘main’:
main.c:10:35: warning: pointer targets in initialization of ‘const t_utf8 *’ {aka ‘const unsigned char *’} from ‘char *’ differ in signedness [-Wpointer-sign]
   10 |         t_utf8 const*   utf8str = "你好世界";    // Ok
      |                                   ^~~~~~~~~~
main.c:12:18: warning: pointer targets in assignment from ‘const t_utf8 *’ {aka ‘const unsigned char *’} to ‘const t_ascii *’ {aka ‘const char *’} differ in signedness [-Wpointer-sign]
   12 |         asciistr = utf8str; // Warning: utf8 to ascii is not safe
      |                  ^
main.c:16:17: warning: pointer targets in assignment from ‘const t_ascii *’ {aka ‘const char *’} to ‘const t_utf8 *’ {aka ‘const unsigned char *’} differ in signedness [-Wpointer-sign]
   16 |         utf8str = asciistr; // Ok: ascii to utf8 is safe
      |                 ^

может int_least8_t это псевдоним char?

tstanisl 11.05.2022 22:46

Я думаю, вы должны обернуть их в непрозрачные структуры, которые будут отличаться от системы типов C, даже если они оба обертывают один и тот же базовый char *. Возможно (и программисты на C со мной не согласятся, я знаю), что у вас нет не строковых типов, а указателей на символы.

Alexander 11.05.2022 22:46

На самом деле, я наблюдаю предупреждение: warning: assigning to 'const t_ascii *' (aka 'const char *') from 'const t_utf8 *' (aka 'const signed char *'). Обратите внимание, что обычный char имеет «знаковость», которая не является ни подписанной, ни беззнаковой.

tstanisl 11.05.2022 22:49

Вы задаете вопрос на самом деле о приведении собственных типов, а не (как в приведенных вами примерах) о приведении указатели к этим типам? Разные вещи. Очень разные, правда.

Adrian Mole 11.05.2022 22:56

Единственное асимметричное предупреждение, о котором я знаю, - это преобразование между объектами разных размеров, например. присваивая uint8_tuint16_tне предупреждает, но присваивая uint16_tuint8_tпредупреждает. Это, конечно, не относится к указателям. Преобразование указателей в типы разного размера всегда требует явного приведения. Так что я думаю, что ответ на ваш вопрос просто "Нет".

user3386109 11.05.2022 23:58

Разумно предположить, что вопрос касается приведения указателей к этим типам, поскольку это суть предупреждения, которое он просит подавить. Но хорошо, что вы указали здесь на разные проблемы, поскольку комментарий ОП, похоже, не оценивает суть предупреждения. @user3386109 user3386109 - предупреждение предупреждает вас о приведении указателя - если вы, например, использовали его как массив или косвенно указали элемент, и у него был установлен бит знака, это может привести к непреднамеренному преобразованию без знака. Как я уже сказал в своем ответе, предупреждение касается /подписанности/, а не размера.

BadZen 12.05.2022 04:59

@BadZen Предупреждение о несовместимости типов. Система типов C несколько нефункциональна, поскольку она рассматривает char, signed char и unsigned char как 3 разных типа, независимо от того, какая подпись имеет char. Правила спецификаторов типов — одна из самых неправильных вещей в C... если бы в C были разумные правила спецификаторов типов, нам не разрешили бы писать #define very signed signed signed... very signed char ch; и тому подобную чепуху.

Lundin 12.05.2022 13:36

@Lundin - типы не являются «несовместимыми» в отношении неявного приведения типов. Вы можете неявно бросать между разными знаками. Предупреждение конкретно касается указателей на типы с другим знаком, как говорится прямо, и это /являются разными типами/, чем типы, на которые они указывают. В приведенном коде в этом месте фактически не выполняется неявное приведение типов между указанными.

BadZen 12.05.2022 17:23

Нет ничего под названием «неявное приведение» — есть неявные преобразования и явные преобразования, а приведение всегда является явным с использованием оператора приведения. Неявные преобразования разрешены только во время допустимых форм присваивания, имеющих дело с обычными переменными (арифметическими типами). Как я уже цитировал в другом комментарии, присваивание более придирчиво, когда речь идет о типах указателей — они должны быть совместимы и не могут удалять квалификаторы.

Lundin 12.05.2022 17:29
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Скомпилируйте с помощью -Wall. Всегда компилируйте с -Wall.

<user>@squall:~/src/p1$ gcc -Wall -c test2.c
test2.c: In function ‘main’:
test2.c:9:31: warning: pointer targets in initialization of ‘const t_utf8 *’ {aka ‘const signed char *’} from ‘char *’ differ in signedness [-Wpointer-sign]
    9 |     t_utf8  const*  utf8str = "你好世界";
      |                               ^~~~~~~~~~~~~~
test2.c:11:13: warning: pointer targets in assignment from ‘const t_ascii *’ {aka ‘const char *’} to ‘const t_utf8 *’ {aka ‘const signed char *’} differ in signedness [-Wpointer-sign]
   11 |     utf8str = asciistr; // Ok: ascii to utf8 is safe
      |             ^
test2.c:12:14: warning: pointer targets in assignment from ‘const t_utf8 *’ {aka ‘const signed char *’} to ‘const t_ascii *’ {aka ‘const char *’} differ in signedness [-Wpointer-sign]
   12 |     asciistr = utf8str; // Should issue warning: utf8 to ascii is not safe
      |              ^

Вы хотите, чтобы кастинг из t_ascii из t_utf8 был безопасным, но это просто не так. Подпись отличается.

Предупреждение не о том, что валидный utf8 иногда не валидный ASCII — компилятор об этом ничего не знает. Предупреждение касается знака.

Если вам нужен беззнаковый char, скомпилируйте с -funsigned-char. Но тогда ни одно предупреждение не будет выдано.

(Кстати, если вы думаете, что тип int_least8_t сможет содержать многобайтовую кодировку char/полную кодовую точку utf8 — это не так. Все int_least8_t и, следовательно, utf8_t в одной единице компиляции будут иметь точно такой же размер.)

Независимо от того, является ли тип char знаковым или беззнаковым внутри, первый бит всегда должен быть равен 0 для допустимых символов ascii. Почему кастинг из t_ascii (он же char) в t_utf8 (он же int_least8_t) небезопасен? Как можно безопасно преобразовать t_ascii в t_utf8?

Yanis.F 12.05.2022 11:55

Соответствующий компилятор должен выдавать диагностическое сообщение при назначении указателя другому указателю, и они не являются совместимыми типами. Нет необходимости в -Wall как таковом, просто используйте соответствующий компилятор. gcc не соответствует настройкам по умолчанию, вот и все. clang в этом случае подходит даже с настройками по умолчанию.

Lundin 12.05.2022 13:30

@Lundin - соответствует /чему/? Пожалуйста, укажите стандарт, если вы делаете это утверждение. (В частности, это не стандарт ANSI C, в котором говорится: "Реализация может генерировать предупреждения во многих ситуациях, ни один из которых не определен как часть настоящего международного стандарта.. Ниже приведены некоторые из наиболее распространенных ситуаций...") Какие стандартные сведения о том, какие предупреждения компилятора должны быть выданы по умолчанию?

BadZen 12.05.2022 17:12

Соответствует стандарту C ISO IEC 9899:2018, 6.5.16.1 Простое назначение — ограничения. В частности, это должно быть истинным: «левый операнд имеет атомарный, квалифицированный или неквалифицированный тип указателя, и (учитывая тип, который будет иметь левый операнд после преобразования lvalue) оба операнда являются указателями на уточненные или неполные версии совместимых типов, и тип, на который указывает левый указатель, имеет все квалификаторы типа, на который указывает правый;".

Lundin 12.05.2022 17:16

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

BadZen 12.05.2022 17:20

Просто скомпилируйте его стандартным компилятором C. Какие параметры компилятора рекомендуются для начинающих изучать C?

Результат:

<source>: In function 'main':
<source>:9:31: error: pointer targets in initialization of 'const t_utf8 *' {aka 'const unsigned char *'} from 'char *' differ in signedness [-Wpointer-sign]
    9 |     t_utf8 const*   utf8str = "你好世界";    // Ok
      |                               ^~~~~~~~~~
<source>:11:14: error: pointer targets in assignment from 'const t_utf8 *' {aka 'const unsigned char *'} to 'const t_ascii *' {aka 'const char *'} differ in signedness [-Wpointer-sign]
   11 |     asciistr = utf8str; // Warning: utf8 to ascii is not safe
      |              ^
<source>:12:13: error: pointer targets in assignment from 'const t_ascii *' {aka 'const char *'} to 'const t_utf8 *' {aka 'const unsigned char *'} differ in signedness [-Wpointer-sign]
   12 |     utf8str = asciistr; // Ok: ascii to utf8 is safe
      |             ^

but not when implicitely casting t_ascii to t_utf8 ?

Нет, у вас не может быть этого в стандартном C, так как это недопустимое преобразование указателя. Вы можете отключить компилятор с помощью явного приведения типов, но если вы это сделаете, вы вызовете неопределенное поведение.


Кроме того, вы можете использовать C11 _Generic, чтобы узнать, какой тип uint_least8_t сводится к:

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

#define what_type(obj) printf("%s is same as %s\n", #obj, \
  _Generic ((obj),                                        \
            char: "char",                                 \
            unsigned char: "unsigned char",               \
            signed char: "signed char") );
  

int main (void)
{
    typedef char           t_ascii;
    typedef uint_least8_t  t_utf8;

    t_ascii ascii;
    t_utf8  utf8;

    what_type(ascii);
    what_type(utf8);
}

Вывод на gcc x86 Linux:

ascii is same as char
utf8 is same as unsigned char

Другие вопросы по теме