LINUX.ORG.RU

В Си предложили добавить switch-case для строк

 , ,


0

3

Привет, чат!

Спустя много лет, в язык Си наконец предложено добавить возможность использовать выражение switch-case для строк. Например, можно будет писать вот такое:

const char *s = get_some_string();

switch(s) {
case "this":
  handle_this();
  break;
case "that":
  handle_that();
  break;
default:
  handle_other();
  break;
}

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

Ссылка на предложение: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3895.pdf



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

GCC появился до Linux

Это ты себе отвечай. Это же ты перевёл разговор с GCC на Linux.

И нам это известно откуда?

Оттуда, что ещё до 15-ого года были проекты по развёртыванию инсталляций на коммерческих unix. Это не считая слона в комнате.

r--r--r--
()
Ответ на: комментарий от yorshka

те страдали. А те и рады!

Вот именно, а вы, раз не хотите это принять, ошиблись дверью :)

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

Вобще из обсуждений на форумах никто не может сказать, почему с самого начала, на PDP-11, когда NULL обозначался как ((char*)0) было выбрано именно значение 0x0000, хотя, вроде как, там лежит вектор какого-то прерывания. При том, что, ЕМНИП, памяти конкретно на той PDP-11 было не по максимуму, то есть был «пустые» адреса.

По моему это очевидно. И не только для PDP-11. В процессоре есть команды только для сравнения с нулем. Для сравнения с любым другим значением мы теряем 2 байта и время.

Да и по поводу NULL какие проблемы? Достаточно допущения, что выделенная память не будет нулевым указателем. Да и strchr например не найдет символ по нулевому адресу, если в качестве аргумента мы даем ей указатель на какой-то свой буфер. А указатель на таблицу прерываний может быть и нулевым. Какая разница нулевой указатель или нет, если мы не собираемся его освобождать или использовать в качестве отрицательного ответа результата функции?

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

main – это часть системного API. Она будет вызвана с вполне определённым данным форматом данных на входе. Другого пути тут нет. Если не нравится, правь компилятор.

Вовсе нет. Функция будет вызвана с теми аргументами, с какими захочется тому, кто ее вызывает. Или не вызвана вообще. К системному API отношения не имеет, да и к компилятору тоже. Компилятор выдал объектный файл, c чем ты его линковать будешь не его забота.

uart
()
Ответ на: комментарий от r--r--r--

Это ты себе отвечай. Это же ты перевёл разговор с GCC на Linux.

Не переводил. Linux тут – просто пример открытой экосистемы. Под BSD тоже ничего не было, раз уже тебе угодно.

Оттуда, что ещё до 15-ого года были проекты по развёртыванию инсталляций на коммерческих unix.

Конечно были, бабло ж надо пилить-то как-то. Те же менеджеры сбербанка поимели просто неприличные откаты от IBM на внедрении AIX.

Это не считая слона в комнате.

Какого? Не томи же нас!

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

Какие же вы одинаковые. :) Одни и те же паттерны, по кругу. Придумали про меня какие-то «переполняющиеся буфера» и тиражируете в своей уютной секте рукожопов по простой причине - у вас в методичках «переполняющиеся буфера» есть, а на то, что в чём было дело в реальности у вас просто мозгов не хватает. Поэтому тупо заменяете реальную непонятку вызубренным «переполнением буфера» из вашей методички и тиражируете, опять же, согласно методичке.

Забавно, что в том споре мне никто так и не привёл реального примера в реальном контексте, когда read сразу после успешного open может вернуть ошибку, и при этом лютом звездеце в системе реально будут иметь существенное значение какие-то там возможные дальнейшие проблемы в некритичной софтине.

Как дело доходит до реальности, жопорукие жители манямирка внезапно сливаются. Всегда и без исключений.

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

Если подобные рукожопы составляют основную массу всяких растофилов, safety троллей, лизателей задниц копрорастов и прочих поклонников сверических коней в ваккуме, то никаких своих целей они добиться никогда не смогут.

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

какие проблемы

Несоответствие стандарту и UB. Про -fno-delete-null-pointer-checks уже написано, встретив if (ptr) сам компилятор не знает, что это — проверка на NULL или проверка на zero.

Достаточно допущения, что выделенная память

Ну дак пусть так в стандарте, и напишут, если это всех устраивает. А то стандарт развязывает руки писателям компиляторов, что ((void*)0) не обязан быть 0x000000. Отсюда лезут всякие советы, что если вам нужно обращаться к адресу 0x000000, то пишите asm-вставки, тогда типа всё хорошо, по NULL-указателю тогда не будет объектов Си, не будет UB, так как ассемблер не Си. Только это бредовый совет. А в ядре уже заложили, что NULL не может быть fullptr (0xFFFFFF), придумав «IS_ERR_OR_NULL».

Стандарт рисует красивую абстракцию, что: int i=0; ((void*)i) — это типа не NULL-ptr, но по факту они не различимы.

указатель на таблицу прерываний может быть и нулевым

В случае МК, особенно гарвардской архитектуры, на 0x000000 может быть не вектор прерывайний, а вполне себе код или данные.

И не только для PDP-11. В процессоре есть команды только для сравнения с нулем.

Процессоров было много разных.

мы теряем 2 байта

Откуда два байта взялось? Ограниченный взгляд. INC (инкремент) вполне может кодироваться одним байтом, а загрузка значения в регистр не везде выставляла флаги, могло быть, что флаг ноль выставлялся только после операции с содержимым регистра. Если null будет 0xFFFFFFF, то INC проверямый указатель и смотри флаг нуля.

В целом, размер указателя не int, поэтому сколько машинных операций занимает сравнение с нулём заранее не известно. А можно ещё добавить, что есть (были) машины, у которых можно читать только слово (18-64 бит) и там разименовывание char-указателя занимает много операций, и на этом фоне экономия на проверке, что указатель не NULL малозначима. Они ведь на PDP-11 перешли с PDP-7, где 36-бит было.

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

Ну ещё можно добавить, касательно того, что NULL - это такой же адрес как и все остальные. Для Си, может и как остальные, но голый Си без libc, как бы и не Си. А вот что выведет:

printf("The value of ptr is %p", NULL);
0x0000000, (nil), (nullptr) или вобще неведомо что? Замечательно, если засовываем в код отладочную печать, а потом парсим скриптами выхлоп.

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

Откуда два байта взялось? Ограниченный взгляд. INC (инкремент) вполне может кодироваться одним байтом, а загрузка значения в регистр не везде выставляла флаги, могло быть, что флаг ноль выставлялся только после операции с содержимым регистра. Если null будет 0xFFFFFFF, то INC проверямый указатель и смотри флаг нуля.

Вот лежит указатель в локальной переменной ptr, по адресу R6+смещение, Проверить на 0 можно одной командой TST, а сравнить с константой CMP + 2 байта (указатель WORD). Если надо с этим ptr что-то делать, то просто загружаем его в R0 например и уже имеем сравнение с 0, не надо делать лишнее сравнение или портить регистр с с помощью INC.

Процессоров было много разных.

Но в целом сравнение с нулем у процессором скорее правило, чем исключение. Не могу ничего сказать про PDP-7, не знаю, да и отвечал я про PDP-11.

Несоответствие стандарту и UB

Какому стандарту это не соответствует и в чем? И незачем бояться UB. Это лишь говорит об отсутствии гарантии на всех архитектурах и компиляторах. Ну и что? Вот пишу я прикладную программу под Linux, Windows, Mac. Никто мне не даст реальный указатель на 0x0000000000000000. А пишу модуль ядра, загрузчик, программу для ДОС или ДВК, так там может быть и адрес 0 вполне реальный и нужный адрес.

А то стандарт развязывает руки писателям компиляторов, что ((void*)0) не обязан быть 0x000000.

Конечно не обязательно, это может быть например DS:0x0000.

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

Чтобы проверить на 0 регистр на x86 можно сделать:

test eax, eax
jz error

Проверка на -1 и другие значения будет длиннее (например, придётся использовать cmp, который несёт в опкоде константу и его опкод длиннее, либо трюки с inc + test, которые добавляют инструкцию, плюс потом нужно восстанавливать исходное значение, если оно нам таки нужно).

Да, процессорных архитектур много, но практически везде есть быстрое сравнение с нулём (без необходимости этот самый ноль явно кодировать в опкоде как константу). А с -1 нет.

С учётом того, что проверка на NULL очень популярная операция, то её оптимальное кодирование важно.

Да, по 0 адресу может что-то лежать. Но, во-первых, на первых компьютерах не было защиты памяти и программа продолжит работу и при чтении/записи по адресу -1 где памяти нет. Плюс небольшая адресная арифметика (тот же +1 для доступа ко второму байту word) и вот мы опять залезли в нулевой адрес. Защита переоценена. А, во-вторых, пользовательские структуры по 0 адресу не лежат, этого достаточно. Адрес таблицы прерываний не будет хранится/передаваться в структуры, где NULL имеет значение, он будет использоваться в редких системных функциях (например, смена обработчика прерывания), которые знают, что проверять на NULL адрес не нужно.

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

Проверка на -1 и другие значения будет длиннее

Да и насрать? Я не представляю себе код, в котором узким местом будет проверка валидности указателя.

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

На PDP-11, под который писали этот ваш Евнухс на этой вашей сишечке, защита памяти таки была.

А, во-вторых, пользовательские структуры по 0 адресу не лежат, этого достаточно.

Пользовательские структуры и по адресу FFFF….FFFF не лежат.

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

Да, процессорных архитектур много, но практически везде есть быстрое сравнение с нулём (без необходимости этот самый ноль явно кодировать в опкоде как константу). А с -1 нет.

Алсо да, напомню, что на куче архитектур нулевой указатель имел битовое представление, отличное от 0x0000...0000 (к счастью, почти все эти архитектуры давно сдохли). Это вообще весьма шизофреничная фишка, я знаю, но битовое представление нулевого указателя не обязано быть нулевым.

yorshka
() автор топика
Ответ на: комментарий от mky

голый Си без libc, как бы и не Си.

Ещё как си, и это - одна из главных его фишек.

А вот что выведет:
0x0000000, (nil), (nullptr) или вобще неведомо что?

Может, стандарт тебе ещё и двоичное представление символа переноса строки определить должен?

парсим скриптами выхлоп.

У нас в треде новое требование к стандарту на язык программирования. Такого я на лоре ещё не припомню.

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

Угадать не осилили, отладочную печать отрицаете, но пытаетесь доказать божественность стандарта и Сишечк. Ну-ну.

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

Алсо да, напомню, что на куче архитектур нулевой указатель имел битовое представление, отличное от 0x0000…0000 (к счастью, почти все эти архитектуры давно сдохли). Это вообще весьма шизофреничная фишка, я знаю, но битовое представление нулевого указателя не обязано быть нулевым.

Нулевой указатель это всегда бинарный 0. Он не может быть другим. Иначе какой-же он нулевой? От архитектуры это никак не зависит.

https://en.cppreference.com/c/types/NULL

The macro NULL is an implementation-defined null pointer constant, which may be

  • an integer constant expression with the value 0
  • an integer constant expression with the value 0 cast to the type void*
  • predefined constant nullptr (since C23) A null pointer constant may be converted to any pointer type; such conversion results in the null pointer value of that type.

Notes POSIX requires NULL to be defined as an integer constant expression with the value 0 cast to void*.

Possible implementation

#define NULL 0
// C++ incompatible:
#define NULL (10*2 - 20)
#define NULL ((void*)0)
// since C23 (compatible with C++11 and later)
#define NULL nullptr
uart
()
Ответ на: комментарий от uart

Нулевой указатель это всегда бинарный 0. Он не может быть другим. Иначе какой-же он нулевой? От архитектуры это никак не зависит.

Нет. Ты сам не очень понял что ты написал тут.

The macro NULL is an implementation-defined null pointer constant, which may be

an integer constant expression with the value 0

an integer constant expression with the value 0 cast to the type void*

predefined constant nullptr (since C23) A null pointer constant may be converted to any pointer type; such conversion results in the null pointer value of that type.

Это значит, что когда ты пишешь void *p = 0; или void *p = (void*)0;, в p будет записан нулевой указатель. Но это не значит, что все биты p будут нулевыми. Можешь считать нулевой указатель этакой абстрактной сущностью, а не просто указателем на адрес 0x00...00.

Вот тебе выдержка из прекрасного C FAQ по теме:

Q: Seriously, have any actual machines really used nonzero null pointers, or different representations for pointers to different types?

The Prime 50 series used segment 07777, offset 0 for the null pointer, at least for PL/I. Later models used segment 0, offset 0 for null pointers in C

Some Honeywell-Bull mainframes use the bit pattern 06000 for (internal) null pointers.

The CDC Cyber 180 Series has 48-bit pointers consisting of a ring, segment, and offset. Most users (in ring 11) have null pointers of 0xB00000000000.

The Symbolics Lisp Machine, a tagged architecture, does not even have conventional numeric pointers; it uses the pair <NIL, 0> (basically a nonexistent <object, offset> handle) as a C null pointer.

Стандарт сишечки написан таким образом, чтобы идеально соответствующий стандарту код работал на вот этом всём говнище.

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

Можешь считать нулевой указатель этакой абстрактной сущностью, а не просто указателем на адрес 0x00…00.

Это не указатель на адрес 0. Это просто 0 (integer constant expression with the value 0). На что он будет указывать вообще не интересно. Это просто число 0.

bool test()
{
  union
  {
    uintptr_t v;
    void *p;
  };

  v = 20;
  for (int i = 0; i < 20; ++i)
    v--;

  if(p)
    return false;

  return true;
}

Где эта функция вернет false?
uart
()
Ответ на: комментарий от uart

Это не указатель на адрес 0.

Ну, да. Связь между указателями как типом и адресами достаточно опосредованная, о чём я тут и пишу.

Это просто 0 (integer constant expression with the value 0). На что он будет указывать вообще не интересно. Это просто число 0.

Нет. В выражении void *p = 0; число 0 – это rvalue. p – это уже не число и не 0.

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

Нет. В выражении void *p = 0; число 0 – это rvalue. p – это уже не число и не 0.

Тогда где будет false?

void *p = 0
return (uintptr_t)p == 0;
uart
()
Ответ на: комментарий от uart

Тогда где будет false?

На одной из архитектур, упомянутых выше? У меня нет к ним доступа, не могу протестировать, сорян.

Другой вопрос, что ни в одном из твоих примеров не происходит реального сравнения при запуске. Компилятор просто выкидывает весь код и ставит true.

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

На одной из архитектур, упомянутых выше?

С чего вдруг? Потому что там есть сегменты? В DOS тоже были сегменты, это ничему не мешало. Другое дело, если ты нулевой near указатель преобразуешь в far указатель, то к нему добавится DS и он уже не будет нулевым. Но к обычным указателям это не относится.

Другой вопрос, что ни в одном из твоих примеров не происходит реального сравнения при запуске. Компилятор просто выкидывает весь код и ставит true.

Ну да, оптимизация называется, можно собрать и без нее…

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

Вот пример:

bool test3()
{
    int *p = 0;
    return (uintptr_t)++p == sizeof(*p);
}

А вот он без оптимизации:

	.globl	_test3                          ; -- Begin function test3
	.p2align	2
_test3:                                 ; @test3
	.cfi_startproc
; %bb.0:
	sub	sp, sp, #16
	.cfi_def_cfa_offset 16
                                        ; kill: def $x8 killed $xzr
	str	xzr, [sp, #8]
	ldr	x8, [sp, #8]
	add	x8, x8, #4
	mov	x9, x8
	str	x9, [sp, #8]
	subs	x8, x8, #4
	cset	w0, eq
	add	sp, sp, #16
	ret
	.cfi_endproc
                                        ; -- End function
uart
()
Ответ на: комментарий от uart

На одной из архитектур, упомянутых выше?

С чего вдруг? Потому что там есть сегменты? В DOS тоже были сегменты, это ничему не мешало. Другое дело, если ты нулевой near указатель преобразуешь в far указатель, то к нему добавится DS и он уже не будет нулевым. Но к обычным указателям это не относится.

Потому что смотри выше. Но, к слову, даже в последнем твоём примере указатель преобразуется в число, и это опять же нифига не значит про его битовое представление. Корректнее было бы вот так.

bool is_zero(void *p) {
  uintptr_t z = 0;
  return !memcmp(&p, &z, sizeof(p));
}
yorshka
() автор топика
Ответ на: комментарий от yorshka

Потому что смотри выше.

Что смотреть? Пока только нашел, что «компилятор C для CDC Cyber 180 не соответствовал ни одному из общепринятых стандартов C (ни K&R C, ни ANSI C (C89), ни тем более поздним версиям)». Ну напридумывали там своего, захотелось им так. Ограничений архитектуры не нашел. Делали бы сейчас вполне могли сделать uintptr_t в 48 бит и чистый 0.

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

Что смотреть? Пока только нашел, что «компилятор C для CDC Cyber 180 не соответствовал ни одному из общепринятых стандартов C (ни K&R C, ни ANSI C (C89), ни тем более поздним версиям)».

Ненене, ты всё ещё не понял. Повторю: в стандарте нет ни слова про битовое представление указателей. Это значит, что даже если нулевой указатель в памяти выглядит как 0x12345678, такая реализация всё равно будет соответствовать стандарту. Главное, чтобы сравнение с константами NULL, (void*)0 и 0 работало корректно.

Из этой же серии, два указателя могут иметь одинаковое битовое представление но при этом не будут равны – оператор == вернёт false.

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

Ненене, ты всё ещё не понял. Повторю: в стандарте нет ни слова про битовое представление указателей. Это значит, что даже если нулевой указатель в памяти выглядит как 0x12345678, такая реализация всё равно будет соответствовать стандарту. Главное, чтобы сравнение с константами NULL, (void*)0 и 0 работало корректно.

Забавное прочтение стандарта. Мне лень искать опровержение. C - продвинутый макроассемблер, а не высокоуровневая абстракция. В нем нет никаких строк, никаких преобразований типов, никакой такой хрени. Если void *p = 0, то не никакого отличия в коде от uintptr_t p = 0; Если кто-то домысливает за тебя, пишет вместо нуля 0x12345678, то стирать надо такую поделку, это не C, а python, rust, go, прочая хрень…

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

Забавное прочтение стандарта. Мне лень искать опровержение.

Потому что опровержений этого нет.

C - продвинутый макроассемблер, а не высокоуровневая абстракция

Тебя обманули. Си – высокоуровневый язык. Если бы это был просто макроассемблер, компилятор бы не выкинул весь код, которым ты пытался что-то доказать.

В нем нет никаких строк

Потому что он убогий, но всё ещё высокоуровневый.

никаких преобразований типов

що?

Если void *p = 0, то не никакого отличия в коде от uintptr_t p = 0;

А потом мы удивляемся, почему же компилятор выкинул казалось бы работающий кусок кода и вообще всё испортил.

Если кто-то домысливает за тебя, пишет вместо нуля 0x12345678

Ссылку на примеры железок, где нулевой указатель имел ненулевое представление, я привёл выше.

yorshka
() автор топика
Ответ на: комментарий от mky

голый Си без libc, как бы и не Си.

Как раз наоборот, это и есть Си в чистом виде, ещё не засорённый посторонними сущностями.

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

Нет, компиляторы для тех архитектур, если и выкинут весь код, то поставят false, т.к. (uintptr_t)(void*)0 там не равно нулю.

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

ещё не засорённый

Ещё? Может вам не Си, а NB нужен (New B, ЯП B с типами). А то уже в исходниках, писаных на Си 1972 года, который ещё не очень K&R 1978 года, есть printf().

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

В исходниках, писанных на Си, бывает и printf(), бывает и deflate() или например CreateWindow(), но это совершенно не значит что все эти функции являются частью языка.

Если ты пишешь на каком-нить фреймворке (или, что примерно аналогично, пишешь гуи на winapi), то вполне вероятно, что ни printf, ни какие-либо другие функции libc тебе явно вызывать не потребуется. И даже main() у тебя может не быть, но язык очевидно всё равно будет Си.

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

Я же уже писал, что сишечка – крайне убогий язык, авторы которого ненавидят разработчиков?

Зато чистая Сишечка отгоняет плюсовиков. Адепты того прошлого, неполиткорректного Линуса Торвальдса помнят:

C++ - ужасный язык. Всё становится ещё хуже только от того факта что многие «нестандартные» программисты используют его, так что получается полная ахинея. Откровенно говоря выбрая язык С, который ничего не делает, но держит С++ программистов подальше от С, это само по себе огромный плюс.

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

голый Си без libc, как бы и не Си.

Как раз наоборот, это и есть Си в чистом виде, ещё не засорённый посторонними сущностями.

Я предполагаю вы знаете, что стандарт языка Си (любой) описывает не только семантику языка (голый С в вашей терминологии), но и библиотеку языка (часто называется libc). Поэтому части языка Си действительно содержатся в libc. Например printf.

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

Я же уже писал, что сишечка – крайне убогий язык, авторы которого ненавидят разработчиков?

Зато чистая Сишечка отгоняет плюсовиков.

Да никого она не отгоняет и никогда не отгоняла.

Адепты того прошлого, неполиткорректного Линуса Торвальдса

Торовалтос никогда не был неполиткорректным. Он был просто грубым, но таких персонажей просто вагоны. Тот же Тео чего только стоит.

yorshka
() автор топика
Ответ на: комментарий от VIT

Я предполагаю вы знаете, что стандарт языка Си

Верно, знаю.

Поэтому части языка Си действительно содержатся в libc. Например printf.

А это уже неверно. Части ISO Cxx содержатся в libc, части Си - нет. ISO Cxx это не Си, а комбайн из языка, библиотеки и фантазий авторов.

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

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

Хорошо, тогда каждый раз, когда вы произносите фразу «язык Си», вы вводите собеседника в заблуждение, потому что собеседник - не телепат, читать ваши мысли не может. Чтобы вас понимали, следует говорить «язык Си согласно стандарту ISO”, или «язык Си в синтаксическом понимании», или ещё какое-то уточнение, что вы каждый раз понимаете под «языком Си».

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

Если что-то в языке с самого начала, разрабатывалось вместе с ним, то оно его существенная часть. Первый компилятор Си, написаный на Си, содержал printf().

Если ты пишешь на каком-нить фреймворке

Вот именно, это уже писанина на фреймворке, а не на Си. Вы лучше покажите такой учебник по Си, где с первых примеров не было printf() и прочих функций libc.

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

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

Это не так. Это часть стандарта ISO Си. Ну и чтобы два раза не вставать, конкретно стандарта С89. C99 доопределил llu. C11 ввёл printf_s.

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

Просмотрел по диагонали исходники четвёртой редакции Юникс. Там не так чтобы очень часто проверка на NULL, и, что интерестно, там просто сравнение с NULL, замени в #define значение, будет другой NULL, не указывающий на нулевой адрес:

#define       NULL    0
....
unlink()
{
        register *ip, *pp;
        extern uchar;

        pp = namei(&uchar, 2);
        if(pp == NULL)
                return;
....

mky ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.