Reputation: 51
I was looking at errno.h
source code, to find how the variable errno
is implemented, I was expected it to be int
or something like that, but when I look into glibc errno.h
file, I found this line:
/* The error code set by various library functions. */
extern int *__errno_location (void) __THROW __attribute_const__;
# define errno (*__errno_location ())
which means to me errno
is not int
, it is int
function, but then when I looked into glibc strtol
function, to understand how it interact with errno
, and I found this:
if (any < 0) {
acc = neg ? LONG_MIN : LONG_MAX;
errno = ERANGE;
}
based on my C understanding, errno
will be replaced to be like:
if (any < 0) {
acc = neg ? LONG_MIN : LONG_MAX;
(*__errno_location ()) = ERANGE;
}
which is illogical to me, because we cannot assign value to function
then I found this line in strtol
which confused me more
extern int errno;
now errno will be used as what, as int, or as function?
then I go to Unix implementation:
I found something similar, but without extern int errno;
so basically there is no variable called errno
Upvotes: 1
Views: 135
Reputation: 12635
errno
used to be an global int
variable, but since multithreading was implemented, it never could be implemented as such, as you cannot control that it's value not be corrupted by another thread terminating a system call between the other thread's termination and the value read.
So to implement a compatible solution to the problem and still be interoperable with old legacy code, it is implemented now as a variable that calls a function, that locates the variable in the pthread_t
object that executed the system call.
Upvotes: 0
Reputation: 67476
which is illogical to me, because we cannot assign value to function then I found this line in strtol which confused me more
It is not assigning value to the function.
extern int *__errno_location (void) __THROW __attribute_const__;
# define errno (*__errno_location ())
__errno_location()
is returning a pointer to int
(*__errno_location ()) = ERANGE;
is a logical equivalent to:
int *errno_ptr = __errno_location ();
*errno_ptr = ERANGE;
and that form is simply a "single liner". I think it would be easier for you to read if it had this form:
*(__errno_location ()) = ERANGE;
extern int errno;
now errno will be used as what, as int, or as function?
In this implementation errno
is a macro definition and it will expand to dereference the result of function call. It is not a variable and it is not a function. It can be used in expressions.
Upvotes: 3
Reputation: 222234
… which means to me errno is not a int, it is int function,…
The macro definition, # define errno (*__errno_location ())
, causes errno
to be replaced with that expression, which is:
_errno_location
with no arguments, ()
, followed by*
to dereference the pointer returned by the function.The prior line, extern int *__errno_location (void) __THROW __attribute_const__;
, declares __errno_location
to be a function that returns an int *
, not an int
.
The purpose of this is to support threads. If errno
were a single object, it would be the same throughout the program. By making it a function call, a different int
can be provided for each thread in a program.
… illogical to me, because we cannot assign value to function…
The way it works is that __errno_location()
returns a pointer to an int
, and * __errno_location()
refers to that int
. So you can assign to that int
, similarly to this code:
int *p = __errno_location(); // Set a pointer to an `int`.
*p = ERANGE; // Assign ERANGE to the `int` pointed to by `p`.
… I found this line in strtol which confused me more
extern int errno;
With the macro defined above, this becomes a harmless redeclaration of __errno_location
. With the macro replacement, external int errno;
becomes:
extern int (*__errno_location ());
The extra parentheses are unusual in a declaration but are allowed; the declaration is effectively:
extern int *__errno_location ();
And that is a declaration of __errno_location
to be a function that returns an int *
. That declaration has no prototype for the parameters, but the C standard, up to C 2018 at least, allows declaring a function both as foo(void)
and foo()
.
(Additionally, the forthcoming version of the C standard is expected to make a declaration with ()
the same as (void)
.)
Upvotes: 5