Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Not quite. (char *) 0 is the null pointer. The null pointer is not necessarily a binary all-zero. On some compilers in x86, the null pointer intentionally points to something which will cause a crash when written to.


Find me one contemporary example (ANSI C) with a disassembled screenshot.

This is writing sizeof(char) (== 1 almost everywhere) zero to address zero. It is not using a NULL macro or other predefined symbol.

In the real world, this would generally write a byte to address 0000:0000, leading to UB because it would fuck up the divide-by-zero IV.

PS: I used Borland C++ 3.1, Microsoft C++ 3.x and 4.5x, Watcom, and early GNU.


(void *) 0 is a null pointer constant and is not necessarily an all zeros representation. This has been defined virtually forever.

https://c-faq.com/null/null2.html

https://c-faq.com/null/machexamp.html

Actual ways to do what you want to do are described in

https://c-faq.com/null/accessloc0.html

but technically speaking the pointer with a constant zero assigned to it _is_ a null pointer (which can be implemented as whatever bit pattern), independent of the preprocessor macro.


> Find me one contemporary example (ANSI C) with a disassembled screenshot.

Here in godbolt, clang compiling C simply deletes the code in the function past and including the null pointer dereference.

https://godbolt.org/z/9aqWPazsP

> This is writing sizeof(char) (== 1 almost everywhere)

1 everywhere. sizeof's unit is "how many chars". For instance there was a cray machine that could only access 64bit words. sizeof(char) is still 1, with 64bit chars.

> zero to address zero. It is not using a NULL macro or other predefined symbol.

NULL is defined as literal 0.


make it a '*(volatile char*)0 = 0' to force the store.



> sizeof(char) (== 1 almost everywhere)

sizeof char is 1 by definition everywhere.

/pedantic


> sizeof char is 1 by definition everywhere.

Parentheses are required around char because it's a type.

/pedantic


That is incorrect :-).

sizeof is an operator in C, and does not need parenthesis any more than pointer operator *. It is true that programmers frequently think of it as a function and use parenthesis.


It's not that simple!

To begin with, sizeof has two syntaxes: the first, which is the one you seem to refer to, is simply

  sizeof expression
where expression involves variables and constants, not types. The second is

  sizeof (type)
where the parentheses are mandatory.

Then, even in the first syntax, even if sizeof is listed among the operators, even if it doesn't look any different from "pointer operator ", nonetheless it has strange priority rules. For example

  sizeof (T) *x
If it was a regular prefix operator obeying priority and right-to-left evaluation, this would mean: dereference x, cast it to T, and return its size. Instead the C standard forces the compiler to interpret it as: take the size of type T and multiply it by x.


Hilariously I've been down voted, even though you absolutely need sizeof (char) because it's a type. Given char x; sizeof x is fine. I know sizeof is an operator. I've been using C for 40 years.


That's incorrect, from GCC: error: expected parentheses around type name in sizeof expression.


In the mathematical sense of almost, a property that holds everywhere does qualify as holding almost everywhere.


Guess can be taken as a shortened explaination of what a programming language committee is tasked with making happen. Likely why Lisp so successful/useful.

-----

unless initial property is start of dynamic operation, in which case, holding almost anywhere begins at the first operation after the start of the dynamic operation. process / lambda / epsilon calculi is just symbolic math. address 0 static, everything else dynamic.

per math, dimension N is static, to be able to "change things up" in dimension N, need to to be almost everywhere higher than dimension n. Edge cases are weird in any dimension. Guess why logicians just do the equivalent of C's !0

(cast classic logic) A=1 (cast boolean logic) B=0

C statement !(!B == A) hold everywhere and almost everywhere depends on how read C spec to interpret A & B.


Regarding your PS, I used Borland's Turbo C++ 1.0, and I think you've forgotten that memory models existed. Honestly, that's a good nightmare to forget.


I hated that about DOS, real-mode and BC++. After about 6-8 months of that misery, installing linux and learning to write C code with GCC was the best thing that ever happened to me. I felt like an animal being released from a cage and into the wild.


In those days MS-DOS, Linux was barely usable, when Linux became usable Windows 95 was already around, without those limitations.

My first kernel was 1.0.9 released alongside Slackware 2.0, offering initial support for IDE CD-ROM drives and experimental support for ELF files, by the way.


It doesn't have to be the NULL macro, which is correctly defined as plain 0.

The literal 0 is treated specially, so this could indeed be one of those 'turns into a weird bit pattern NULL pointers', if such a thing existed in the wild anymore.

But you're correct in that there probably haven't been any since the turn of the century or whenever the last Univac mainframes got turned off.


Apparently according to the c-faqs link elsethread

    execl takes a variable-length, null-pointer-terminated list of character pointer arguments, and is correctly called like this:
    execl("/bin/sh", "sh", "-c", "date", (char *)0);
Due to ececl being a variadic function it can not take advantage of a prototype to instruct the compiler that one of its arguments needs to be treated as a pointer context.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: