LINUX.ORG.RU

Поясните за Си

 , , , ,


0

3

Накопился ряд вопросов по Си, вываливаю сразу кучей.

1. Как посмотреть весь список типов, известных компилятору в данный момент с учётом включенных заголовочников, объявлений и т. д.?

2. Правильно ли я понимаю, что список собственно типов и структур хранятся отдельно, потому что Foo и struct Foo - разные типы данных и могут сосуществовать в рамках одного проекта?

3. В С11 появилось ключевое слово _Generic, однако ещё со стандарта С99 существует заголовочный файл tgmath.h, который реализует подобное поведение, аналогичное перегрузке функций в С++, для набора математических функций. Как это можно было реализовать в С99?

★★★★★

1. Наверное только писать свою приблуду на каком нибудь LibClang.

anonymous ()

Foo и struct Foo - разные типы данных и могут сосуществовать в рамках одного проекта?

А как объявить тип Foo?

Как это можно было реализовать в С99?

implementation-defined

anonymous ()

Как это можно было реализовать в С99?

Самому — никак. Ещё спроси, как offsetof и va_list реализовать.

anonymous ()

2. Правильно ли я понимаю, что список собственно типов и структур хранятся отдельно, потому что Foo и struct Foo - разные типы данных и могут сосуществовать в рамках одного проекта?

Тэги struct, union и enum находятся в одном обособленном пространстве имён и с остальными идентификаторами не пересекаются.

Подробнее см. раздел 6.2.3 Name spaces of identifiers стандарта C99.

unterwulf ()
Ответ на: комментарий от anonymous

То есть в 99 стандарте, по сути, разработчиков компиляторов обязали сделать возможность перегрузки, но только для внутреннего использования?

meliafaro ★★★★★ ()
Ответ на: комментарий от anonymous

Ещё спроси, как offsetof и va_list реализовать.

А что не так с va_list? Оно всегда было сделано через пропроцессорную магию, а в языке поддержка не va_list, а эллипса (...).

vodz ★★★★★ ()
Ответ на: комментарий от anonymous

Пишут что ... заставляет компилятор передавать параметры через стек.

anonymous ()
Ответ на: комментарий от anonymous

Переносимая реализация никого не волнует. stdarg.h идёт в комплекте и для большинства архитектур представляет собой тривиальщину:

/* vax, mc68k, 80*86 */
typedef char *va_list;
#  define va_start(ap, p)       (ap = (char *) (&(p)+1))
#  define va_arg(ap, type)      ((type *) (ap += sizeof(type)))[-1]
#  define va_end(ap)

vodz ★★★★★ ()
Ответ на: комментарий от vodz

Переносимая реализация никого не волнует.

ЧТД. va_list и родственники на чистом C не написать.

stdarg.h идёт в комплекте и для большинства архитектур представляет собой тривиальщину:

Не нашёл такого в своём stdarg. Нашёл примерно такое (код не полон):

typedef __builtin_va_list __gnuc_va_list;
typedef __gnuc_va_list va_list;

Что опять же доказывает, что va_list не представляет собой тривиальщину в том смысле, что он компиляторо-зависим и не реализуем на переносимом C.

anonymous ()
Ответ на: комментарий от anonymous

В MSVC в stdarg.h вот что написано

#define va_start __crt_va_start
#define va_arg   __crt_va_arg
#define va_end   __crt_va_end
#define va_copy(destination, source) ((destination) = (source))

fsb4000 ★★★★ ()
Ответ на: комментарий от anonymous

а в другом файле дальше есть дефайны этих crt

#define _ADDRESSOF(v) (&(v))

#define _INTSIZEOF(n)          ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))

#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
#define __crt_va_arg(ap, t)     (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define __crt_va_end(ap)        ((void)(ap = (va_list)0))

fsb4000 ★★★★ ()
Ответ на: комментарий от anonymous

Ну offsetof реализовать самому можно, но через небольшой UB.

С va_list тоже можно сделать кое-что небезопасное, но только на 32 битах. На 64 из-за передачи параметров в регистрах никакие UB не помогут.

KivApple ★★★★★ ()
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

С va_list тоже можно сделать кое-что небезопасное, но только на 32 битах. На 64 из-за передачи параметров в регистрах никакие UB не помогут.

Ты понимаешь, что разрядность здесь не причем и всё определяется ABI?

tailgunner ★★★★★ ()
Ответ на: комментарий от KivApple

Ну offsetof реализовать самому можно, но через небольшой UB.

Название «UB» говорит само за себя: неопределённое поведение.

offsetof должен вести себя конкретным образом.

Как можно получить конкретное, определённое поведение через неопределённое?

anonymous ()
Ответ на: комментарий от anonymous

ЧТД. va_list и родственники на чистом C не написать.
Что опять же доказывает,

Что это может доказать? Что всем надоело рисовать эту макаронную препроцессорную магию и включили не в язык, а в конкретный компилятор __gnuc встроенные алиасы? Ну да, и что? Нет никакого ЧТД. va_list не только всегда можно написать на чистом C, но и оно и написано на нём, это не элемент языка, в отличии от эллипсов.

vodz ★★★★★ ()
Ответ на: комментарий от vodz

Что всем надоело рисовать эту макаронную препроцессорную магию и включили не в язык, а в конкретный компилятор __gnuc встроенные алиасы?

Не совсем понял, что значит «надоело». В чём разница один раз определить va_* через «препроцессорную магию» или через typedef и использовать? «препроцессорную магию» кто-то руками копипастил каждый раз? Нет, конечно. Просто через препроцессорную магию оно не определяется универсально.

va_list не только всегда можно написать на чистом C

Ты нам этого почему-то демонстрировать не хочешь.

это не элемент языка

Стандартная библиотека — часть языка. Описана в том же стандарте. И некоторые вещи в ней через core language не выразишь.

Кстати, в дополнение к offsetof и va_*: memcpy и memmove тоже невозможно написать на чистом C.

anonymous ()
Ответ на: комментарий от anonymous

Кстати, в дополнение к offsetof и va_*: memcpy и memmove тоже невозможно написать на чистом C.

setjmp, longjmp в ту же степь.

meliafaro ★★★★★ ()
Ответ на: комментарий от anonymous

Ещё спроси, как offsetof и va_list реализовать.

Эти штуки вообще-то есть в ашниках от gcc например. Идешь туда и смотришь как оно реализовано

sergej ★★★★★ ()
Ответ на: комментарий от anonymous

А чем это, например, не memcpy на чистом С?

void *memcpy(void *destination, const void *source, size_t num) {
  char *dst = destination;
  const char *src = source;
  for (size_t i = 0; i != num; ++i)
    dst[i] = src[i];
  return destination;
}

Softwayer ★★ ()
Ответ на: комментарий от sergej
#define myoffsetof(t,m) (size_t)&(((t*)0)->m)

с va* чуть сложнее из-за всяких извратов со стеком в современных компиляторах и реализовано через builtin функции, но вообще обычная адресная арифметика.

sergej ★★★★★ ()
Ответ на: комментарий от anonymous

в дополнение к offsetof и va_*: memcpy и memmove тоже невозможно написать на чистом C.

Про offsetof и va_* я ответил чуть выше

memcpy и memmove невозможно написать максимально эффективно на Си, поэтому их обычно пишут на асме с развесистыми условиями под каждый вариант набора команд каждой архитектуры.

sergej ★★★★★ ()
Ответ на: комментарий от tailgunner

За стандарт я хз, а компиляторы это хавают. И когда-то давно оно именно так и было реализовано.

Более того 0 - это вполне валидный указатель, если никто не ограничивает доступ к ОЗУ.

sergej ★★★★★ ()
Ответ на: комментарий от sergej

За стандарт я хз, а компиляторы это хавают.

Наличие __builtin_offsetof в gcc/clang намекает, что не все это хавают.

clang свалится на данном тобой определении offsetof со включённым UB-сатанайзером.

anonymous ()
Ответ на: комментарий от sergej

компиляторы это хавают

Компиляторы хавают весь код с UB, причем обычно делают это молча.

0 - это вполне валидный указатель

А, окей.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

0 - это вполне валидный указатель

А, окей.

Кажется на лоре же был эпичный тред на эту тему по мотивам LKML. Разработчики ядра сделали что-то похожее.

sergej ★★★★★ ()
Ответ на: комментарий от sergej

Линус неоднократно ныл о том, что компиляторы ломают код, который по стандарту работать не обязан, но раньше работал.

tailgunner ★★★★★ ()
Ответ на: комментарий от kirk_johnson

so? Определение для va_* на якобы C тоже выше привели.

То, что во всех или почти всех компиляторах возможно написать код с поведением как у memcpy/memmove (хотя это только проявление неопределённого поведения), говорит только о том, что авторы библиотеки в курсе, как проявится формально неопределённое поведение в данном компиляторе.

anonymous ()
Ответ на: комментарий от anonymous

во всех или почти всех компиляторах возможно написать код с поведением как у memcpy/memmove

Можно написать код memmove, который работает на любом компиляторе - в частности, приведенный выше код и код из musl. А вот va_* написать и в самом деле нельзя, потому что это зависит от ABI.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от anonymous

Тут фишка в том, что когда стандарт говорит UB, компилятор обычно делает вполне defined behaviour. А ашники с подобными макросами идут обычно вместе с компилятором.

Я слегка погуглил - народ находит подобную лажу во всяких STL от MSVC.

sergej ★★★★★ ()
Ответ на: комментарий от tailgunner

Линус неоднократно ныл о том, что компиляторы ломают код

Я считаю, что после ядра, гита и приложения для дайверов, он обязан написать свой компилятор!

sergej ★★★★★ ()
Ответ на: комментарий от sergej

что после ядра, гита и приложения для дайверов

каша_из_топора.txt

он обязан написать свой компилятор!

Он попробовал. Не потянул или потерял интерес.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Можно написать код memmove, который работает на любом компиляторе - в частности, приведенный выше код и код из musl.

Да. Но этот код будет содержать UB с точки зрения стандарта.

anonymous ()
Ответ на: комментарий от anonymous

этот код будет содержать UB с точки зрения стандарта.

Где именно? Покажи конкретное место.

tailgunner ★★★★★ ()
Ответ на: комментарий от Softwayer

Арифметика указателей определена только в массиве.

Соответственно, чтобы не возникало UB с приведённым тобой кодом, в dst и src нужно передавать указатель на элементы массива символов. Иначе будет UB.

anonymous ()
Ответ на: комментарий от kirk_johnson

У memcpy, если ты не нарушаешь его требований, перечисленных в стандарте — никакого.

В коде на C, который попытается делать то, что делает memcpy, кастуя к char* (или к int* для оптимизации) будет UB в случаях, если попытаться копировать не массивы char (int).

anonymous ()
Ответ на: комментарий от anonymous

В коде на C, который попытается делать то, что делает memcpy, кастуя к char* (или к int* для оптимизации) будет UB в случаях, если попытаться копировать не массивы char (int).

Щитоа?

Copies count bytes from the object pointed to by src to the object pointed to by dest. Both objects are reinterpreted as arrays of unsigned char.

kirk_johnson ★☆ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.