Amr Alasmer
Amr Alasmer

Reputation: 51

Understanding errno Codes: Need Assistance

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:

https://opensource.apple.com/

I found something similar, but without extern int errno;

so basically there is no variable called errno

Upvotes: 1

Views: 135

Answers (3)

Luis Colorado
Luis Colorado

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

0___________
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

Eric Postpischil
Eric Postpischil

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:

  • a call to the function _errno_location with no arguments, (), followed by
  • application of * 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

Related Questions