LINUX.ORG.RU

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

 , ,


0

3

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

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

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

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

★★★★★

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

7.21 String handling <string.h> 7.21.1 String function conventions 1 The header <string.h> declares one type and several functions, and defines one macro useful for manipulating arrays of character type and other objects treated as arrays of character type.268) The type is size_t and the macro is NULL (both described in 7.17). Various methods are used for determining the lengths of the arrays, but in all cases a char * or void * argument points to the initial (lowest addressed) character of the array. If an array is accessed beyond the end of an object, the behavior is undefined. 2 Where an argument declared as size_t n specifies the length of the array for a function, n can have the value zero on a call to that function. Unless explicitly stated otherwise in the description of a particular function in this subclause, pointer arguments on such a call shall still have valid values, as described in 7.1.4. On such a call, a function that locates a character finds no occurrence, a function that compares two character sequences returns zero, and a function that copies characters copies zero characters. 3 For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value). 7.21.2 Copying functions 7.21.2.1 The memcpy function Synopsis 1 #include <string.h> void *memcpy(void * restrict s1, const void * restrict s2, size_t n); Description 2 The memcpy function copies n characters from the object pointed to by s2 into the object pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined. Returns 3 The memcpy function returns the value of s1.

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

Есть. Читай про «treated as array of character type».

Конкретно вот эту часть:

For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value)

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

То, что оно «threated as» и «as if it had» не то же самое, что «ты можешь обращаться в своём коде с любым объектом как массивом символов».

В скобках написано, для чего нужны эти as if: это гарантия, что если у объекта есть trap representation, то при копировании с помощью функций ты не получишь UB, как при копировании объекта со значением, являющимся trap representation, присваиванием.

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

То, что оно «threated as» и «as if it had» не то же самое, что «ты можешь обращаться в своём коде с любым объектом как массивом символов».

Ты можешь привести ситуацию, в которой ты не можешь рассматривать объект как unsigned char *?

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

Ты можешь привести ситуацию, в которой ты не можешь рассматривать объект как unsigned char*?

Не знаю, что ты понимаешь под «мочь».

Стандарт не даёт права рассматривать произвольный объект как массив unsigned char. Он только говорит, что если их скопировать в массив unsigned char соответствующего размера, то набор байтов в этом массиве будет называться object representation-ом.

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

если их скопировать

если его скопировать

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

Стандарт не даёт права рассматривать произвольный объект как массив unsigned char.

Что это значит? Стандарт утверждает, что любой указатель может быть приведен к void *. void * может быть приведен к unsigned char *, для которого специальным пунктом идет отсутствие trap representation. Более того, char - единица адресации. Так что я не вижу проблемы.

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

Стандарт утверждает, что любой указатель может быть приведен к void *. void * может быть приведен к unsigned char *

Допустим. Но он не говорит, что этот указатель является (или может считаться для целей адресной арифметики) указателем на первый элемент массива unsigned char[sizeof(объект)].

Так что я не вижу проблемы.

А я вижу. Ты додумываешь то, чего нет.

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

Допустим. Но он не говорит, что этот указатель является (или может считаться для целей адресной арифметики) указателем на первый элемент массива unsigned char[sizeof(объект)].

Эм... char — это минимальный дискрет. Поэтому первый char в массиве по указателю это первый char объекта. Просто потому, что объект адресуется char'ами. Что в этом утверждении противоречит стандарту?

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

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

/dev, который мы заслужили.

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

#define offsetof(x,y) (&((x)0)->y)

И сразу же аж три ошибки, две из которых школьные.

#define offsetof(x,y) (size_t)(&((((x)*)0)->(y)))

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

Стандарт говорит, что любой тип, который не является bit field - непрерывныйнабор байт.

Он говорит:

Values stored in non-bit-field objects of any other object type consist of n × CHAR_BIT bits, where n is the size of an object of that type, in bytes.

Это, конечно, educated guess, но я думаю, что «n × CHAR_BIT bits» использовано вместо «n bytes» не случайно, а для того, чтобы сильнее подчеркнуть то, что массивом из bytes это не является.

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

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

Алсо гонишь:

A pointer to an object or incomplete type may be converted to a pointer to a different
object or incomplete type. If the resulting pointer is not correctly aligned57) for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

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

будет UB в случаях, если попытаться копировать не массивы char (int)

Неа. POD -> char[] - корректный type aliasing

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

Так... ты стандарт вообще читал?

3.6
1 byte addressable unit of data storage large enough to hold any member of the basic character set of the execution environment 2 NOTE 1 It is possible to express the address of each individual byte of an object uniquely. 3 NOTE 2 A byte is composed of a contiguous sequence of bits, the number of which is implementation- defined. The least significant bit is called the low-order bit; the most significant bit is called the high-order bit. 3.7 1 character 〈abstract〉 member of a set of elements used for the organization, control, or representation of data 3.7.1 1 character single-byte character 〈C〉 bit representation that fits in a byte

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

Ладно. Значит, в C можно реализовать memcpy без UB.

А в C++даже гарантии, что представление объекта хранится непрерывно, кроме как для trivially copyable types, нет.

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

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

Вся разница только в эстетическом удовлетворении.

Просто через препроцессорную магию оно не определяется универсально.

Нельзя ВСЁ сделать универсально. Иначе был бы одна архитектура (пусть и виртуальная). Речь о том, что сделано на уровне языка, на уровне библиотеки и на уровне просто развесистых заголовочных файлов. Так вот, va* - это последнее, то есть оно делается на чистом C.

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

Мне то зачем вам демострировать содержимое stdarg.h ?

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

К чему это банальщина?

memcpy и memmove

Элементарно делается через копирование побайтно, например в uclibc. Да, медленно, но речь была не об скорости.

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

У него были претензии к представлению объекта как массива байт. Говорит что в плюсовом стандарте про возможность индексирования не сказано. В сишном про это есть.

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

В C++, с которым я лучше знаком, такой гарантии нет.

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

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

Ещё совсем недавно в стандарте была написана странная фраза: «If an object of type T is located at an address A, a pointer of type cv T* whose value is the address A is said to point to that object, regardless of how the value was obtained. [Note: For instance, the address one past the end of an array would be considered to point to an unrelated object of the array's element type that might be located at that address.»

Конечно, это бред и это приводило к противоречиям. Это выкинули, а Note переписали как: «A pointer past the end of an object is not considered to point to an unrelated object of the object's type that might be located at that address.»

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

Со всеми исключениями плюсов, я не могу придумать такого случая. По-моему, такое возможно только с reference types, но там sizeof невозможно определить разумно.

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

Со всеми исключениями плюсов, я не могу придумать такого случая. По-моему, такое возможно только с reference types, но там sizeof невозможно определить разумно.

Это так, легкая шпилька в сторону монструозности дизайна плюсов. Я тоже не могу представить себе такой ситуации в реальности.

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

Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

То есть, разрешены только инкременты. Но не прибавления произвольного числа и не декременты.

Ещё кто-то говорит, что C++ — это нагромождение исключений...

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

Стандарт ISO — это близкий к юридическому документ. По языку, которым он должен писаться («shall» вместо «must» и т.п.) и т.д. Наверняка есть правила писать требования, разрешения и запреты явно, а не оставлять читателю стандарта их вывод по аналогии или ещё как-то.

Ты можешь точно сказать, что это просто неаккуратный wording и подразумевается возможность совершать другие арифметические операции или комитет хотел, чтобы это понимали буквально и декремент — это неопределённое поведение?

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

Ты можешь точно сказать, что это просто неаккуратный wording

Я могу точно сказать, что прибавление произвольного числа выраждается через инкремент (если считать «инкрементом» строго увеличение на единицу).

и подразумевается возможность совершать другие арифметические операции

Естественно.

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

То есть, разрешены только инкременты. Но не прибавления произвольного числа и не декременты.

Ой все.

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

Я могу точно сказать, что прибавление произвольного числа выраждается через инкремент (если считать «инкрементом» строго увеличение на единицу).

Под «точно сказать» я имел в виду «притащить ссылку на Defect Report с ответом от комитета», а не демонстрацию твоих познаний в арифметике.

и подразумевается возможность совершать другие арифметические операции

Естественно.

If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint or runtime- constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition of behavior.

Посмотри, что значит «explicit». Explicit definition для инкремента указателя после каста есть. То, что декремент даст предыдущий байт объекта — явно не сказано.

Так что тут куча вопросов. Если позиция комитета «байты, образующие представление объекта, это массив символов» — почему они просто и ясно про это не написали? А написали только про то, что после каста можно инкрементом адресовать следующие байты.

Если можно выполнять любые арифметические операции, а эти слова про инкремент — просто напоминание, что при инкременте получается следующий байт, то почему это напоминание — не в (foot)note, как полагается всем информативным, а не нормативным утверждениям?
Такое нормативное явное описание какого-то поведения означает отсутствие явного описания для остального поведения, т.е. делает декременты и проч. операции неопределёнными.

Стандарт C, конечно, написан очень неаккуратно, так что это вполне может быть просто напоминанием, а комитет считает представление объекта массивом, но хотелось бы увидеть эту позицию комитета в ответе на какой-нибудь defect report.

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

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

Под «точно сказать» я имел в виду «притащить ссылку на Defect Report

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

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

Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

Возвращаемся к нашим баранам. Ок, ходить по байтам объекта можно (только «вперёд», используя оператор инкремента — ++, но нам этого достаточно).

Но функция memcpy не только итерирует по объектам, она ещё должна копировать байты. Наверное присваиванием? В memcpy должно быть что-то вроде цикла, который делает число итераций, равное количеству, переданному в третьем аргументе и в теле цикла выполняет что-нибудь вроде

*dst++ = *src++;
, где dst и src это переменные типа unsigned char* и const unsigned char*, полученные из первого и второго аргумента функции, соответственно.

Посмотрим, что стандарт говорит про присваивание:

Draft N1570, 6.5.16.1 Simple assignment

(Constraints я пропущу, там ничего интересного, а копипастить много)

Semantics

2 In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

3 If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have qualified or unqualified versions of a compatible type; otherwise, the behavior is undefined.

(Дальше идут Examples, которые тоже не важны)

Присваиванием можно копировать только в объекты (replaces the value stored in the object designated by the left operand.), значит тут есть 2 стула:

1. Не считать байты в представлении объекта объектами. Тогда с помощью оператора присваивания копировать эти байты нельзя, семантика не определена.

2. Считать эти байты объектами, но тогда тут проблема с третьим параграфом. Даже если не обращать внимания на «compatible types», а посмотреть на требование «the overlap shall be exact», то ясно, что у объекта размером больше одного байта не будет exact overlap ни с каким байтом в его представлении. Короче, нельзя попеременно писать и читать в объект с помощью присваивания то через выражение типа этого объекта, то через unsigned char, указывающий на какой-то из его байтов.

Если не существует других способов копировать байты в core language, кроме присваивания, то с реализуемостью memcpy на conformant C проблемы.

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

Тут нет проблемы с третьим параграфом, потому что по стандарту и мануалам в memcpy запихиваются non-overlapping объекты. Так что мимо:

is read from another object that overlaps in any way the storage of the first object

Вот не-UB-ID memcpy: https://github.com/codemeow/bixi/blob/master/code/libbixi/utils/bximemutils.c...

По стандарту мы можем присваивать от void * к u8 * и обратно. Тут нарушения нет.

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

Тут нет проблемы с третьим параграфом, потому что по стандарту и мануалам в memcpy запихиваются non-overlapping объекты.

Вобще я не это имел в виду, но ок, я не корректно понял третий параграф.

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