[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Counterexamples in C programming and library documentation (was: [PA
From: |
Alejandro Colomar |
Subject: |
Re: Counterexamples in C programming and library documentation (was: [PATCH v3] NULL.3const: Add documentation for NULL) |
Date: |
Wed, 3 Aug 2022 01:54:18 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.1.0 |
Hi Branden,
On 8/2/22 21:06, G. Branden Robinson wrote:
[content warning: yet another long software engineering rant]
[this one took me many hours to read completely, including links, but
was funny]
At 2022-08-02T13:38:22+0200, Alejandro Colomar wrote:
On 7/27/22 15:23, Douglas McIlroy wrote:
Incidentally, I personally don't use NULL. Why, when C provides a
crisp notation, 0, should one want to haul in an extra include file
to activate a shouty version of it?
While I don't endorse the shoutiness of the name selected for the macro
(and also don't endorse the use of a macro over making the null pointer
constant a bona fide language feature as was done in Pascal[1]), as
stronger typing has percolated into the C language--slowly, and against
great resistance from those who defend its original character as a
portable assembly language--distinctions have emerged between ordinal
types, reference types (pointers), and Booleans.
I'd like NULL being part of the language, as I'd like bool.
_Bool was necessary when it was introduced, to avoid collision with many
handmade booleans, but reallistically, we're a few decades after C99,
and it's hard to find redefinitions of bool in current code. And the
few code bases that redefine it because they were written before I was
even born, I don't think anyone is going to compile them with a
hypothetical -std=c3x; and if they do, bool is the very least of the
serious problems they'll find.
However, there's not much difference between the Standard C library and
the language. Both are defined by the same ISO document, and both have
the same status of mandatory for an implementation (except for a few
optional libc features). You are required by the Standard to treat libc
macros and other identifiers as if they were language keywords, in the
sense that you are not allowed to #undef them or hide functions with a
macro, etc.
And the reason for that is that the Standard recognizes that compilers
might decide to ignore the Standard, and go further and define a bona
fide language keyword, like GNU cc and libc do with NULL and bool:
$ grepc -ktm bool /usr/include
/usr/include/curses.h:288:#define bool NCURSES_BOOL
/usr/include/ruby-3.0.0/ruby/internal/stdbool.h:45:# define bool _Bool
/usr/include/x86_64-linux-gnu/ruby-3.0.0/rb_mjit_min_header-3.0.4.h:21075:#define
bool _Bool
$ grepc -ktm NULL /usr/include | grep -v ruby
/usr/include/php/20210902/ext/mbstring/libmbfl/mbfl/mbfl_defs.h:36:#define
NULL (0L)
/usr/include/php/20210902/ext/mbstring/libmbfl/mbfl/mbfl_defs.h:38:#define
NULL (void *)(0L)
/usr/include/tirpc/rpc/types.h:54:# define NULL 0
/usr/include/unicode/utypes.h:186:#define NULL nullptr
/usr/include/unicode/utypes.h:188:#define NULL ((void *)0)
So, in practice, it's a language feature in GNU C. Not in ISO C, but
who cares? Today, GCC and Clang cover 99 % (and that's likely an
understatement) of the C implementations I care about.
I feel this is unequivocally a good thing.
Yes, people will say that a zero (false) value in all of these types has
exactly the same machine representation, even when it's not true[2], so
a zero literal somehow "tells you more".
But let me point out two C programming practices I avoid and advocate
against, and explain why writing code that way tell us less than many of
us suspect.
(1) Setting Booleans like this.
nflag++;
Not only this is unreadable. This is dangerous.
If the variable is declared as _Bool, then the compiler should emit the
same code as for 'nflag = 1'. If it doesn't, it is unconforming to ISO
C. See 6.3.1.2 Arithmetic operands::Boolean type:
[
When any scalar value is converted to _Bool, the result is 0 if the
value compares equal to 0; otherwise, the result is 1.
]
(Of course, if the compiler can guarantee that no-one will read the
value as an integer, or that it won't overflow, it is allowed to produce
non-conforming code.)
If the variable is declared as an integer type, it's likely that someone
will slightly change the code without realizing a tiny detail, and the
++ will result in an overflow/wrap around.
[...]
But I'm not done. The above exhibit abuses the incrementation
operator. Firstly this makes your data type conceptually murky.
What if `nflag` (or whatever you called it) was already `1`? Now
it's `2`. Does this matter? Are you sure? Have you checked every
path through your code? (Does your programming language help you to
do this? Not if it's C. <bitter laugh>) Is a Boolean `2`
_meaningful_, without programming language semantics piled on to
coerce it? No. Nobody answers "true or false" questions in any
other domain of human experience with "2". Nor, really, with "0" or
"1", except junior programmers who think they're being witty. This
is why the addition of a standard Boolean type to C99 was an
unalloyed good.
And 2 would still be not so bad. Consider if it is n, for an unbounded
n. bool can't overflow or wrap around; other integers can.
[...]
In my own experience the best optimizations I've done are those
which dropped code that wasn't doing anything useful at all.
Eliminating _unnecessary_ work is _never_ a "premature"
optimization. It removes sites for bugs to lurk and gives your
colleagues less code to read. (...says the guy who thinks nothing
of dropping a 20-page email treatise on them at regular intervals.)
Heh :)
And another thing!
It was funny to read this and check that I still had more than half
email below.
[...]
I submit that using `0`
as a null pointer constant, whether explicitly or behind the veil of
a preprocessor macro, hides _necessary_ information from the
programmer.
For me personally, this fact alone is enough to justify a
within-language null pointer constant.
ISO C defines NULL as a black-box macro. They call it macro, but it has
all the same requirements that a keyword like int has: You can't #undef
any of them, nor redefine them in any way. I don't understand what do
you miss so much from a keyword that NULL doesn't have. The only real
difference is that the ISO document uses the word macro instead of keyword.
[...]
I don't think it is an accident that there are no function pointer
literals in the language either (you can of course construct one
through casting, a delightfully evil bit of business).
Could you show an example? I'm curious.
The lack
made it easier to paper over the deficiency. Remember that C came
out of the Labs without fully-fledged struct literals ("compound
literals"), either. If I'd been at the CSRC--who among us hasn't
had that fantasy?--I'd have climbed the walls over this lack of
orthogonality.
As I've said several times, K&R C was excellent for their time, but very
deficient compared to the latest ISO C. I wouldn't blame them (nor the
programmers that have abused it). C++ was bad when released, and is
even worse some decades after. K&R (and the [ab]users of their
language) have the merit of having developed a language that could be
polished to the beautiful language we have today.
I will grant that the inertia against the above two points was, and
remains, strong. C++, never a language to reject a feature[6], resisted
the simple semantics and obvious virtues of a Boolean data type for
nearly the first 20 years of its existence, and only acquired `nullptr`
in C++11. Prior to that point, C++ was militant about `0` being the
null pointer constant, in line with Doug's preference--none of this
shouty "NULL" nonsense.
And I don't understand why C++ added nullptr. NULL was already in use
for where programmers are now expected to write nullptr. C++ could have
just said: from now on, NULL is a language keyword that creates a null
pointer. You don't need to include a header to get it anymore.
Programs would have just started using it without even noticing it, with
the added safety of using a keyword.
And they didn't seem too shy when making bool be a keyword, nor when
they gave a completely new meaning to auto. Backwards compatibility
with old code defining stuff or with C are clearly not a problem, when
bool and auto could be made keywords.
[...]
Because I don't know what foo(a, b, 0, 0) is, and I don't know from
memory the position of all parameters to the functions I use (and
checking them every time would be cumbersome, although I normally do,
just because it's easier to just check, but I don't feel forced to do
it so it's nicer).
Kids these days will tell you to use an IDE that pops up a tool tip with
the declaration of 'foo'. That this is necessary or even as useful as
it is discloses problems with API design, in my opinion.
Boy, don't remind me of that. I still remember certain "senior"
programmer recommending me to use an IDE to "understand" his
incomprehensible code. And the worse thing is I'm not talking about a
kid. (Actually, I'm the Millenial.)
Was the third parameter to foo() the pointer and the fourth a length,
or was it the other way around? bcopy(3) vs memcpy(3) come to mind,
although of course no-one would memcpy(dest, NULL, 0) with hardcoded
0s in their Right Mind (tm) (although another story is to call
memcpy(dest, src, len) with src and len both 0).
The casual inconsistency of the standard C library has many more
manifestations than that.
Knowing with 100% certainty that something is a pointer or an integer
just by reading the code and not having to go to the definition
somewhere far from it, improves readability.
I entirely agree.
Even with a program[...] that finds the definitions in a big tree of
code, it would be nice if one wouldn't need to do it so often.
Or like Atom[7], which has been a big hit among Millennial programmers.
I haven't used Atom, but it seems like an IDE, isn't it? If so, no thanks.
grepc(1) is a sh(1) script that I wrote around pcregrep(1) and basic
Unix commands. It's not another plugin for an editor; it's a
Unix-friendly command.
It is a one-shot program with no configuration nor interactivity. It's
quite fast, and can search in any unknown code base (including a whole
OS file tree).
See an example of typical use (and actually a real use case that I
needed a few weeks ago[1]):
$ time grepc SYS_pivot_root / 2>/dev/null
/usr/include/x86_64-linux-gnu/bits/syscall.h:1599:
# define SYS_pivot_root __NR_pivot_root
real 0m8.204s
user 0m7.510s
sys 0m1.954s
I included the time to run it to show that it can find anything in the
OS really fast (see a larger example at the end of this email[2]). You
may want to give it a try. It even comes with a manual page! And it
uses .MR (in a way that you won't miss the text if you lose it)!
[1]: <https://github.com/nginx/unit/issues/737>
Too bad, thanks to who acquired GitHub, it's being killed off and
replaced by Visual Studio now. We call this market efficiency.
Kind of the argument that some use to object to my separation of
man3type pages, but for C code.
Here I disagree,
I was more referring to Ingo's comments, who said repeatedly that types
should be documented in the function pages, because one shouldn't need
to open a separate page to search for the type definition when reading
about a function. I admit it's still not a perfect match with my
argument of not wanting to look at the definition of foo() to know if a
0 is an int or a pointer, but kind of, I thought when I wrote it.
because while we are clearly aligned on the null
pointer literal...er, point,
And I think Ingo also agrees.
I will continue to defend the position that
a man page for a header file--where an API has been designed rather than
accreted--is a preferable place to document the data types and constants
it defines, as well as either an overview or a detailed presentation of
its functions, depending on its size and/or complexity.
And in this context, I also agree with you. If I were creating my own
library, I'd write exactly one manual page for every header file. And
if a header file's manual page is too big, it's because I should split
the header file.
In fact, something like 4 years ago, much before I ever learned how
manual pages were written (that Debian has compressed manual pages
didn't help), I attempted to write (in .rst files) manual pages for my
own library, and I decided to write one for each header. Each header in
that library usually contains between 1 and 10 functions, depending on
complexity. And there are just a dozen or so types in the library, so
they could be described in the manual page for the header that defines them.
But of course, if I were to define my own library, each symbol would be
provided only in one header (and of course, any other header would be
allowed to provide such a symbol by inclusion, in an undocumented way)
But libc is a mess, with many headers redefining stuff, many others not
being allowed to provide a given symbol, and similar weird rules.
I think the overwhelming importance of the C library to your documentary
mission in the Linux man-pages project is obscuring the potential of
elegant man pages for _other_ libraries. What passes for the C standard
library on _any_ system is quite a crumbcake.
Yeah, mtk was more focused on documenting the syscalls (his job is all
about the kernel). I've allways been more of a libc user, and only used
syscalls in few cases in the past (now more often, but still I find
myself using library calls much more often than kernel interfaces).
That inevitably results in that I end up documenting much of the libc
stuff that had never been documented before.
The fact that you can't cram the C library into the shape I espouse
without making the pages huge and unwieldy is the fault of the library,
It certainly is.
I humbly(!) suggest, not the fault of the principle.
We agree.
But I'd be a long
way from the first person to note that the C library is schizophrenic
upon even cursory inspection.
Still, I hope that in the long term the language can be fixed, and we
don't need to go so far as to invent a new language, say C+=2.
Is it true that Plauger never wrote another book after his (dense but
incredibly illuminating, A+++, would read again) _The Standard C
Library_? Did writing his own libc and an exposition of it "break" him
in some way? I imagine that he got perhaps as good a look as anyone's
ever had at how much better a standard library C could have had, if only
attention had been paid at the right historical moments. He saw how
entrenched the status quo was, and decided to go sailing...forever.
Just an irresponsible guess.
I would like to find a good place to state the recommendation that
documentors of _other_ libraries should not copy this approach of
trifurcating presentations of constants, data types, and functions.
In a well-designed API, these things will be clearly related,
mechanically coupled, and should be considered together.
As you hinted, you shouldn't aim documentors, but designers.
If the design is good, it will ask for a header-file documentation
model, I think.
What do you think?
As always, spirited contention of any of my claims is welcome.
Regards,
Branden
[1] I say that, but I don't see it in the 1973 "Revised Report" on the
language. I guess "nil" came in later. Another Algol descendant,
Ada 83, had "null". And boy am I spoiling to fight with someone
over the awesomeness of Ada, the most unfairly maligned of any
language known to me.
I admit I've never seen Ada, but I've used VHDL, which is heavily based
on Ada (AFAIK). I hate it, sorry. It's unnecessarily verbose. To
begin with, who needs 'begin' and 'end' when '{' and '}' can do?
[3] Pop quiz: what assembly language did Branden grow up on? It's hard
to escape our roots in some ways.
I'm too young to know this one :)
[4] I won't go so far as to say speculative execution is _stupid_,
though I wonder if it can ever actually be implemented without
introducing side channels for attack. Spec ex is performed, as I
understand it, because Moore's Law is dead, and CPU manufacturers
are desperately trying to satisfy sales engineers and certain
high-volume customers who are only willing to pay for cores that the
premium prices (profit margins) that the industry has been
accustomed to for 50 years. Purchasing decisions are made by suits,
and suits like to get to the bottom line, like "number go up".
https://davidgerard.co.uk/blockchain/2019/05/27/the-origin-of-number-go-up-in-bitcoin-culture/
[5] https://ubiquity.acm.org/article.cfm?id=1513451
[6] Stroustrup would disagree and can support his argument; see his
writings on the history of the language.
[7] https://en.wikipedia.org/wiki/Atom_(text_editor)
I think
[2]:
$ time grepc -ktm ARRAY_SIZE / 2>/dev/null
/home/alx/src/alx/libalx/include/alx/base/compiler/size.h:40:#define
ARRAY_SIZE(arr) (__arraycount((arr)) + alx_must_be_array(arr))
/home/alx/src/bsd/freebsd/freebsd-src/contrib/libevent/test/regress_dns.c:75:#define
ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/home/alx/src/bsd/freebsd/freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h:351:#define
ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/contrib/llvm-project/compiler-rt/lib/scudo/standalone/internal_defs.h:20:#define
ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/contrib/ntp/sntp/libevent/test/regress_dns.c:1675:#define
ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/home/alx/src/bsd/freebsd/freebsd-src/contrib/ofed/opensm/opensm/osm_torus.c:64:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/contrib/wpa/src/utils/common.h:556:#define
ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/contrib/xz/src/common/sysdefs.h:189:#
define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/arm/ti/omap4/omap4_prcm_clks.c:446:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/compat/linuxkpi/common/include/linux/kernel.h:302:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/contrib/edk2/Include/Base.h:1312:#define
ARRAY_SIZE(Array) (sizeof (Array) / sizeof ((Array)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/contrib/ncsw/inc/ncsw_ext.h:170:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h:124:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/contrib/openzfs/include/os/freebsd/spl/sys/sysmacros.h:69:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/contrib/openzfs/lib/libspl/include/os/linux/sys/sysmacros.h:43:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/axgbe/xgbe_osdep.h:197:#define
ARRAY_SIZE(x) nitems(x)
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/bxe/bxe.h:121:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/bxe/bxe_elink.h:107:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/cxgb/cxgb_osdep.h:195:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/cxgbe/cudbg/cudbg_entity.h:106:#define
ARRAY_SIZE(_a) (sizeof((_a)) / sizeof((_a)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/cxgbe/osdep.h:94:#define
ARRAY_SIZE(x) nitems(x)
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/drm2/drm_os_freebsd.h:109:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/ice/ice_osdep.h:197:#define
ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/isci/scil/sci_util.h:64:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/ixl/i40e_osdep.h:95:#define
ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/ocs_fc/ocs_os.h:90:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/qlnx/qlnxe/bcm_osal.h:140:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/smartpqi/smartpqi_defines.h:643:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/bsd/freebsd/freebsd-src/usr.sbin/bhyve/hda_codec.c:135:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/bsd/openbsd/src/gnu/gcc/include/libiberty.h:582:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
/home/alx/src/bsd/openbsd/src/gnu/lib/libiberty/include/libiberty.h:459:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
/home/alx/src/bsd/openbsd/src/gnu/llvm/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h:334:#define
ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
/home/alx/src/bsd/openbsd/src/gnu/llvm/compiler-rt/lib/scudo/standalone/internal_defs.h:20:#define
ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
/home/alx/src/bsd/openbsd/src/gnu/usr.bin/binutils-2.17/bfd/elf32-ppc.c:4965:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
/home/alx/src/bsd/openbsd/src/gnu/usr.bin/binutils-2.17/include/libiberty.h:592:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
/home/alx/src/bsd/openbsd/src/gnu/usr.bin/binutils/bfd/elf32-ppc.c:1600:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
/home/alx/src/bsd/openbsd/src/gnu/usr.bin/binutils/include/libiberty.h:299:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
/home/alx/src/bsd/openbsd/src/sys/dev/pci/sv.c:125:#define
ARRAY_SIZE(foo) ((sizeof(foo)) / sizeof(foo[0]))
/home/alx/src/gnu/glibc/math/gen-auto-libm-tests.c:164:#define
ARRAY_SIZE(A) (sizeof (A) / sizeof ((A)[0]))
/home/alx/src/gnu/gnulib/lib/nproc.c:62:#define ARRAY_SIZE(a) (sizeof
(a) / sizeof ((a)[0]))
/home/alx/src/gnu/gnulib/lib/physmem.c:92:#define ARRAY_SIZE(a) (sizeof
(a) / sizeof ((a)[0]))
/home/alx/src/gnu/gnupg/tpm2d/intel-tss.h:208:#define ARRAY_SIZE(A)
(sizeof(A)/sizeof(A[0]))
/home/alx/src/gnu/groff/gnulib/lib/nproc.c:62:#define ARRAY_SIZE(a)
(sizeof (a) / sizeof ((a)[0]))
/home/alx/src/gnu/groff/gnulib/lib/physmem.c:92:#define ARRAY_SIZE(a)
(sizeof (a) / sizeof ((a)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/cmd/bhyve/hda_codec.c:135:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/cmd/mdb/common/modules/stmf_sbd/stmf_sbd.c:41:#define
ARRAY_SIZE(a) (sizeof (a) / sizeof (*a))
/home/alx/src/illumos/illumos-gate/usr/src/cmd/stat/common/acquire.c:36:#defineARRAY_SIZE(a)
(sizeof (a) / sizeof (*a))
/home/alx/src/illumos/illumos-gate/usr/src/grub/grub-0.97/netboot/r8169.c:76:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/tools/ctf/common/ctf_headers.h:81:#define
ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
/home/alx/src/illumos/illumos-gate/usr/src/tools/smatch/src/lib.h:43:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/tools/smatch/src/validation/sm_array_overflow2.c:3:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/tools/smatch/src/validation/sm_array_overflow3.c:3:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/tools/smatch/src/validation/sm_array_overflow4.c:4:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/uts/common/io/arn/arn_core.h:50:#define
ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
/home/alx/src/illumos/illumos-gate/usr/src/uts/common/io/bnxe/577xx/drivers/common/lm/device/lm_pf.c:559:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/uts/common/io/bnxe/577xx/hsi/hw/include/clc.h:86:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/home/alx/src/illumos/illumos-gate/usr/src/uts/common/io/cxgbe/t4nex/cudbg_entity.h:126:#define
ARRAY_SIZE(_a) (sizeof((_a)) / sizeof((_a)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/uts/common/io/cxgbe/t4nex/osdep.h:146:#define
ARRAY_SIZE(x) (sizeof (x) / sizeof ((x)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/uts/common/io/qede/579xx/drivers/ecore/bcm_osal.h:561:#define
ARRAY_SIZE(_arr) (sizeof(_arr) / sizeof((_arr)[0]))
/home/alx/src/illumos/illumos-gate/usr/src/uts/common/sys/sysmacros.h:377:#define
ARRAY_SIZE(x) (sizeof (x) / sizeof (x[0]))
/home/alx/src/linux/linux/arch/mips/boot/tools/relocs.h:32:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/arch/powerpc/boot/types.h:7:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/arch/um/include/shared/user.h:17:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/arch/x86/boot/boot.h:31:#define ARRAY_SIZE(x)
(sizeof(x) / sizeof(*(x)))
/home/alx/src/linux/linux/arch/x86/tools/relocs.h:23:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/fs/orangefs/orangefs-debug.h:21:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/fs/unicode/mkutf8data.c:63:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/include/linux/kernel.h:55:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
/home/alx/src/linux/linux/samples/bpf/cookie_uid_helper_example.c:34:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
/home/alx/src/linux/linux/samples/bpf/xdp_sample.bpf.h:139:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/samples/bpf/xsk_fwd.c:33:#define ARRAY_SIZE(x)
(sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/samples/seccomp/user-trap.c:24:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
/home/alx/src/linux/linux/scripts/dtc/util.h:27:#define ARRAY_SIZE(x)
(sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/scripts/genksyms/genksyms.c:406:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/scripts/kallsyms.c:28:#define ARRAY_SIZE(arr)
(sizeof(arr) / sizeof(arr[0]))
/home/alx/src/linux/linux/scripts/kconfig/preprocess.c:15:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/scripts/mod/file2alias.c:737:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/scripts/selinux/mdp/mdp.c:40:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/tools/arch/x86/kcpuid/kcpuid.c:10:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/tools/gpio/gpio-utils.h:19:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/tools/iio/iio_utils.h:19:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
/home/alx/src/linux/linux/tools/include/linux/kernel.h:102:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
/home/alx/src/linux/linux/tools/lib/traceevent/plugins/plugin_futex.c:15:#define
ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0]))
/home/alx/src/linux/linux/tools/lib/traceevent/plugins/plugin_xen.c:103:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/tools/power/cpupower/utils/cpupower.c:23:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/home/alx/src/linux/linux/tools/spi/spidev_test.c:26:#define
ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/home/alx/src/linux/linux/tools/testing/selftests/bpf/bpf_util.h:31:#
define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/tools/testing/selftests/bpf/progs/netif_receive_skb.c:27:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/tools/testing/selftests/bpf/progs/profiler.inc.h:136:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
/home/alx/src/linux/linux/tools/testing/selftests/bpf/progs/test_sysctl_loop1.c:13:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/tools/testing/selftests/bpf/progs/test_sysctl_loop2.c:13:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/tools/testing/selftests/bpf/progs/test_sysctl_prog.c:19:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/linux/tools/testing/selftests/kselftest.h:53:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/tools/testing/selftests/kselftest_harness.h:683:#define
ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/home/alx/src/linux/linux/tools/usb/usbip/libsrc/usbip_device_driver.c:30:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/linux/tools/virtio/linux/kernel.h:52:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/home/alx/src/linux/linux/tools/vm/page-types.c:211:#define
ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/home/alx/src/linux/man-pages/man-pages/tmp/src/man2/seccomp.2.d/seccomp.c:13:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/man-pages/man-pages/tmp/src/man2/seccomp_unotify.2.d/seccomp_unotify.c:25:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/src/linux/man-pages/man-pages/tmp/src/man2/sysctl.2.d/sysctl.c:11:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/tmp/man-pages/tmp/src/man2/seccomp.2.d/seccomp.c:13:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/tmp/man-pages/tmp/src/man2/seccomp_unotify.2.d/seccomp_unotify.c:25:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/home/alx/tmp/man-pages/tmp/src/man2/sysctl.2.d/sysctl.c:11:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/usr/include/compel/common/compiler.h:10:#define ARRAY_SIZE(x)
(sizeof(x) / sizeof((x)[0]))
/usr/include/xorg/wacom-util.h:26:#define ARRAY_SIZE(a)
(sizeof(a)/sizeof((a)[0]))
/usr/share/doc/git/contrib/credential/wincred/git-credential-wincred.c:12:#define
ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
/usr/src/linux-headers-5.18.0-1-common/include/linux/kernel.h:55:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
/usr/src/linux-headers-5.18.0-2-common/include/linux/kernel.h:55:#define
ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
/home/alx/src/bsd/freebsd/freebsd-src/sys/dev/wtap/plugins/visibility.h:41:#define
ARRAY_SIZE (32) //We support a maximum of 1024 nodes for now
/home/alx/src/bsd/openbsd/src/sys/dev/pci/drm/include/linux/kernel.h:43:#define
ARRAY_SIZE nitems
/home/alx/src/illumos/illumos-gate/usr/src/lib/libdwarf/common/gennames.c:87:#define
ARRAY_SIZE 300
real 0m9.586s
user 0m9.117s
sys 0m2.061s
You can see that it found things in Linux, FreeBSD, OpenBSD, Illumos,
glibc, ... and all in one run. I still don't know any real competition
for this program :)
And for the more common case of searching for the definition of a symbol
in a single code base, the times are almost instantaneous:
alx@asus5775:~/src/nginx/unit$ time grepc nxt_getpid
./src/nxt_process.h:21:
#define nxt_getpid()
\
syscall(SYS_getpid)
./src/nxt_process.h:24:
#define nxt_getpid()
\
getpid()
real 0m0.069s
user 0m0.090s
sys 0m0.020s
Cheers,
Alex
--
Alejandro Colomar
<http://www.alejandro-colomar.es/>
OpenPGP_signature
Description: OpenPGP digital signature