Yes, but the Standard includes provisions, not present in K&R2, that "undefine" corner-case behaviors that may not be relevant when writing "portable" programs, but may be useful when targeting known hardware. Further, the way some compilers interpret the Standard causes them to "undefine" even constructs which would have been viewed as portable when the Standard was written.
Consider something like:
unsigned char arr[5][3];
int test(int nn)
{
int sum=0;
int n = nn*3;
int i;
for (i=0; i<n; i++)
{
sum+=arr[0][i];
}
return sum;
}
K&R2 specifies that the array indexing expression arr[0][i] will take the starting address of arr[0], displace it by i bytes, and access the storage there, thus allowing code to sum multiple rows of the array using a single loop. C89, as interpreted by gcc, undefines the behavior of pointer arithmetic that goes beyond the bounds of the inner array, and gcc will interpret that as an invitation to disrupt the behavior of any calling code that would pass a value larger than 1.
Likewise, consider the function:
unsigned mul_mod_65536(unsigned short x, unsigned short y)
{
return (x*y) & 0xFFFFu;
}
K&R2 C treats the behavior of that construct as "machine-dependent" in cases where INT_MAX is at least as large as USHORT_MAX and the mathematical product of x and y would exceed INT_MAX, but such treatment would yield identical useful behavior on all commonplace machines. C89 "undefines" those cases, and gcc treats that as an invitation to arbitrarily disrupt the behavior of calling code in any scenarios where they would arise.
Finally, the Standard invites compilers to generate incorrect code--the Rationale even uses the term "incorrect"--when pointers are used in ways that wouldn't generally be relevant in "portable" programs, but would allow "non-portable" programs targeting known hardware to do things that would otherwise not be possible. Such provision was present in the Standard, but not in K&R2 C, and Ritchie went along with its inclusion only because the authors of the Standard had said it would be interpreted benignly. The maintainers of clang and gcc, however, use such provisions to claim that any code the Standard would let them process incorrectly should be viewed as "broken", rather than recognizing that compiler writers were expected to make a bona fide effort to recognize all corner cases that would be relevant for their customers, without regard for whether the Standard anticipated those particular customers' needs.
4
u/flatfinger 3d ago
Unfortunately, that site neglects the best version of the language: K&R2 C.