LINUX.ORG.RU

printf("%s", NULL)

 , , ,


1

3

Здравствуйте,

Возник вопрос по поводу вывода строки посредством семейства функций printf().

А именно: стандарт vs реализация. Что будет, если для %s в формате printf() передать NULL.

Стандарт (С99, §7.19.6.1, стр. 279) говорит:

s
If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type. Characters from the array are written up to (but not including) the terminating null character. If the precision is specified, no more than that many bytes are written. If the precision is not specified or is greater than the size of the array, the array shall contain a null character.

If an l length modifier is present, the argument shall be a pointer to the initial element of an array of wchar_t type. Wide characters from the array are converted to multibyte characters (each as if by a call to the wcrtomb function, with the conversion state described by an mbstate_t object initialized to zero before the first wide character is converted) up to and including a terminating null wide character. The resulting multibyte characters are written up to (but not including) the terminating null character (byte). If no precision is specified, the array shall contain a null wide character. If a precision is specified, no more than that many bytes are written (including shift sequences, if any), and the array shall contain a null wide character if, to equal the multibyte character sequence length given by the precision, the function would need to access a wide character one past the end of the array. In no case is a partial multibyte character written.

Т.е. «pointer to the initial element» — это неявная ссылка на разыменование указателя. А как известно, разыменование NULL вызывает UB. Но связь косвенная, и про NULL в самом описании printf ничего не сказано (sic!).

Собственно вопрос: а как на вашей libc отрабатывает следующий код:

#include <stdio.h>
#include <stdlib.h>

int main(void) { printf("%s\n", NULL); return EXIT_SUCCESS; }

Особенно интересна реализация в libc, отличных от GNU libc.

★★★★★

Последнее исправление: KennyMinigun (всего исправлений: 5)

Я думаю, что практичнее будет спрашивать «а как на вашей libc отрабатывает следующий код».

И интересоваться, соответственно, поведением на реализациях, отличных от glibc.

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 2)
Mac-mini-Tim:print_null tim$ ./print_null
(null)
Mac-mini-Tim:print_null tim$ clang -v
Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix
Mac-mini-Tim:print_null tim$ uname -a
Darwin Mac-mini-Tim.local 14.1.0 Darwin Kernel Version 14.1.0: Thu Feb 26 19:26:47 PST 2015;
root:xnu-2782.10.73~1/RELEASE_X86_64 x86_64
hibou ★★★★★
()

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

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

Вопрос возник оттуда, что есть легаси функция с почти printf подобным синтаксисом формата + расширения для локальных типов данных.

В идеале надо её отрефакторить до:

  1. Раскрытия локальных расширений
  2. Вызов v(s|f)printf

Но проблема с NULL, которые,бывает приходят, ибо функция используется для логгирования. Тут есть два способа это починить:

  • Добавить в легаси функцию проверку на NULL(что убьет совместимость с printf)
  • Починить код вокруг, чтоб он осторожно дергал эту функцию (что может быть более трудоёмкость и не гарантирует полное устранение проблемы)

Вот и раздумываю над концепцией...

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

В идеале надо её отрефакторить до:

в идеале велосипедить надо бросать..в семействе printf спецификаторы можно добавлять и расширять для «локальных типов данных»

MKuznetsov ★★★★★
()

а как на вашей libc отрабатывает следующий код

А что, у кого-то она будет писать что-то, отличное от "(null)"?

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

А что, у кого-то она будет писать что-то, отличное от "(null)"?

кстати, а является ли строковая константа "(null)" локале-зависимой ? лень ковырять исходник, но если можно задать её в каком-нить .mo было-бы весело :-)

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

в идеале велосипедить надо бросать.

Говорю же: легаси. Велосипеды с костылями...

семействе printf спецификаторы можно добавлять и расширять для «локальных типов данных»

Не слышал такого раньше. Есть что «на почитать»?

KennyMinigun ★★★★★
() автор топика
$ gcc test_null.c 
test_null.c: In function ‘main’:
test_null.c:4:1: warning: reading through null pointer (argument 2) [-Wformat=]
 int main(void) { printf("%s\n", NULL); return EXIT_SUCCESS; }
 ^
test_null.c:4:1: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void *’ [-Wformat=]

$ ./a.out
Segmentation fault

$ gcc --version
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Deleted
()
Ответ на: комментарий от Eddy_Em

отличное от "(null)"?

На rhel 6.6, glibc (непомню версию) неожиданно сегфолт. То же самое на распоследней убунте и ideone.com.

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

Не слышал такого раньше. Есть что «на почитать»?

внезапно http://www.gnu.org/software/libc/manual/html_node/Formatted-Output.html#Forma...

кстати, не «man`ом единым жив программист» - libc комплектуется очень неплохой документацией из-коробки, просто не все про знают и уж совсем мало кто читает :-)

MKuznetsov ★★★★★
()
[~]$ vim printf_null.c
[~]$ gcc printf_null.c -o printf_null
[~]$ ./printf_null 
[1]    3753 segmentation fault (core dumped)  ./printf_null
[~]$ clang printf_null.c -o printf_null
[~]$ ./printf_null                     
(null)
[~]$ /opt/intel/bin/icc printf_null.c -o printf_null 
[~]$ ./printf_null 
[1]    3805 segmentation fault (core dumped)  ./printf_null
post-factum ★★★★★
()
Ответ на: комментарий от post-factum
[~]$ gcc --version
gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[~]$ clang --version
clang version 3.4.2 (tags/RELEASE_34/dot2-final)
Target: x86_64-redhat-linux-gnu
Thread model: posix
[~]$ /opt/intel/bin/icc --version                   
icc (ICC) 15.0.2 20150121
Copyright (C) 1985-2015 Intel Corporation.  All rights reserved.
post-factum ★★★★★
()
Ответ на: комментарий от zudwa

Да все хорошо, только в идеале наперед не знаешь, на какой позиции в va_args будет (будут) тот самый указатель (указатели). Надо парсить строку формата.

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

Может в наоборот: в генте она пропатченная? :)

KennyMinigun ★★★★★
() автор топика

Особенно интересна реализация в libc, отличных от GNU libc.

Segmentation fault (core dumped) 

PS: OpenBSD 5.6

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

gcc заменяет вызов printf(«%s\n», NULL) на puts(NULL).

И правда.. Даже с -O0:

        movl    $0, %edi
        call    puts

ЛОЛ, после такого:

printf("NULL is: %s\n", NULL);
Поставило printf нормально и напечатало (последняя убунта, gcc 4.9.2, glibc 2.21):
NULL is: (null)

Спасибо за хинт!

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

Давайте уже перейдем к следующему вопросу: стоит ли сушить кошку в микроволновке?

thesis ★★★★★
()
% ee t.c
% clang -o t t.c
% ./t
(null)
% uname -m -r -s
FreeBSD 10.0-RELEASE-p17 amd64
% clang --version
FreeBSD clang version 3.3 (tags/RELEASE_33/final 183502) 20130610
Target: x86_64-unknown-freebsd10.0
Thread model: posix
reprimand ★★★★★
()

Но связь косвенная, и про NULL в самом описании printf ничего не сказано (sic!).

Нифига не косвенная. Именно прямая - должен быть дереференс NULL и соотв. эффекты (UB).

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

Зависит от интерпретации. Там сказано «указатель на первый байт данных». Слово «валидный», например, не употребляется.

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

в одних локалях будет печатать (ноль), а в других (нуль)

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

Нифига, все четко. Написано же:

the terminating null character

Значит обязан быть. По указателю. Значит должно быть разыменование. Потому не NULL.

Pavval ★★★★★
()

Сегфолт.

watashinoshi@aokigahara /tmp $ cat zlo.c
#include <stdio.h>
#include <stdlib.h>

int main(void) { printf("%s\n", NULL); return EXIT_SUCCESS; }watashinoshi@aokigahara /tmp $ gcc -o zlo zlo.c
zlo.c: В функции «main»:
zlo.c:4:1: предупреждение: чтение по пустому указателю (аргумент 2) [-Wformat=]
 int main(void) { printf("%s\n", NULL); return EXIT_SUCCESS; }
 ^
zlo.c:4:1: предупреждение: format «%s» expects argument of type «char *», but argument 2 has type «void *» [-Wformat=]
watashinoshi@aokigahara /tmp $ ./zlo
Ошибка сегментирования
watashinoshi@aokigahara /tmp $ uname -a
Linux aokigahara 3.18.7-gentoo #4 SMP Sun Mar 22 16:35:51 MSK 2015 x86_64 Intel(R) Pentium(R) CPU B980 @ 2.40GHz GenuineIntel GNU/Linux
watashinoshi@aokigahara /tmp $ gcc -v
Используются внутренние спецификации.
COLLECT_GCC=/usr/x86_64-pc-linux-gnu/gcc-bin/4.8.3/gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-pc-linux-gnu/4.8.3/lto-wrapper
Целевая архитектура: x86_64-pc-linux-gnu
Параметры конфигурации: /var/tmp/portage/sys-devel/gcc-4.8.3/work/gcc-4.8.3/configure --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/gcc-bin/4.8.3 --includedir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.3/include --datadir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.8.3 --mandir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.8.3/man --infodir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.8.3/info --with-gxx-include-dir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.3/include/g++-v4 --with-python-dir=/share/gcc-data/x86_64-pc-linux-gnu/4.8.3/python --enable-languages=c,c++,fortran --enable-obsolete --enable-secureplt --disable-werror --with-system-zlib --enable-nls --without-included-gettext --enable-checking=release --with-bugurl=https://bugs.gentoo.org/ --with-pkgversion='Gentoo 4.8.3 p1.1, pie-0.5.9' --enable-libstdcxx-time --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --enable-multilib --with-multilib-list=m32,m64 --disable-altivec --disable-fixed-point --enable-targets=all --disable-libgcj --enable-libgomp --disable-libmudflap --disable-libssp --enable-lto --without-cloog --enable-libsanitizer
Модель многопоточности: posix
gcc версия 4.8.3 (Gentoo 4.8.3 p1.1, pie-0.5.9) 

watashinoshi
()
$ gcc -Wall test.c 
test.c: In function ‘main’:
test.c:4:1: warning: reading through null pointer (argument 2) [-Wformat=]
 int main(void) { printf("%s\n", NULL); return EXIT_SUCCESS; }
 ^
test.c:4:25: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void *’ [-Wformat=]
 int main(void) { printf("%s\n", NULL); return EXIT_SUCCESS; }

$ ./a.out 
Segmentation fault (core dumped)

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/5.0.0/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --disable-libgcj --with-default-libstdcxx-abi=c++98 --with-isl --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 5.0.0 20150319 (Red Hat 5.0.0-0.21) (GCC) 

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

а как на вашей libc отрабатывает следующий код

Как оказалось тут еще фича компилятора замешана. Выше по треду описано.

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

внезапно http://www.gnu.org/software/libc/manual/html_node/Formatted-Output.html#Forma...

Внезапно gcc-изм: http://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html

Portability Note: The ability to extend the syntax of printf template strings is a GNU extension. ISO standard C has nothing similar.

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