Weird C function declaration [duplicate]
Asked Answered
M

1

5

I was going through some code when I encountered this in one of the source files.

int st_insert(table, key, value)
register st_table *table;
register st_data_t key;
st_data_t value;
{
    unsigned int hash_val, bin_pos;
    register st_table_entry *ptr;

    hash_val = do_hash(key, table);
    FIND_ENTRY(table, ptr, hash_val, bin_pos);

    if (ptr == 0) {
        ADD_DIRECT(table, key, value, hash_val, bin_pos);
        return 0;
    } else {
        ptr->record = value;
        return 1;
    }
}

What is this style? Is it some obscure way to declare functions? Is there some reason one might use this over normal function declarations?

Meeks answered 19/11, 2015 at 23:3 Comment(17)
K&R-style. Don't use.Ice
That is old style. (K&R style) before c89/c90. Function parameters as register is still weird, though.Pantagruel
It's the old style of function definitions and is now obsolete since C99.Choriamb
Uhoh. This code was written around 2010...Meeks
@calccrypto: In that case, arrange for the author's immediate reeducation in a camp in siberia.Ice
@wildplasser: register arguments are still legal in modern C. The compiler is free to ignore it and like will if the ABI does not allow it (or usese registers anyway).Bos
@Olaf: register doesn't have anything to do with hardware-registers, only with not taking the address of the object.Ice
It would influence the calling convention. The caller really needs a prototype if the argument is actually passed in a register. (I can imagine this is used as an extention for embedded platforms, and maybe in kernel code and device drivers)Pantagruel
@EOF My mistake. The copyright dates go from 1996 to 2006.Meeks
2010? Where did you get that from? The header shows the earliest version is from 1996 (but if I recall correctly, this was already called old style even almost 20 years ago).Becalm
@EOF: Originally, register was definitively meant to tell the compiler to hold these variables in CPU registers. This just implied you cannot take its address. Just remember when this function definition-style was used (and when register was used for its original purpose). Nowadays, the side-effect is the only reason to use it, because the compiler will allocate CPU registers most times itself often better than the programmer can (although it might still take it as a hint).Bos
The linked-to code is kind of mixed style: though, bulky macros + inline functions. Plus the (IMHO) silly register arguments and old type function definitions. It appears people were afraid to touch it. But: what is it? Inode cache for xenix ;-?Pantagruel
@Becalm It was an estimate. I was paying more attention to the code than the date.Meeks
@Pantagruel This is the code for OpenSM, an Infiniband controllerMeeks
the funky do {} while() (line#299 and after) even has an error `#define ADD_DIRECT(table, key, value, hash_val, bin_pos)` (the final semicolon is not wanted!)Pantagruel
@Pantagruel It does not influence the calling convention, even when applied to a parameter (bear in mind, in K&R C, all functions were called by pushing the args on the stack, last to first; and the compiler didn't even know or care how many args the function was actually defined with; only its return type). So it means 'please copy this into a register when the function is called'. So back then it had zero effect on the calling convention. Thus IMHO it would be a dangerous idea, even in a special embedded compiler, to add that meaning. I know of no such case.Minacious
Oh my good what an ugly style!!! I hate it!Foreskin
A
8

It is an old (but still valid according to the current C Standard) syntax of function definitions using an identifier list. The types of the identifies are declared after the identifier list and before the opening brace.

It is better to use functions with parameter type lists because in this case the compiler having a function prototype can check the correct list of arguments for a function call.

From the C Standard (6.9.1 Function definitions)

6 If the declarator includes an identifier list, each declaration in the declaration list shall have at least one declarator, those declarators shall declare only identifiers from the identifier list, and every identifier in the identifier list shall be declared. An identifier declared as a typedef name shall not be redeclared as a parameter. The declarations in the declaration list shall contain no storage-class specifier other than register and no initializations.

You can meet other funny constructions in old C code as for example this

memset( ( char * )p, value, n );
        ^^^^^^^^^^  

because type void was introduced later.

Actable answered 19/11, 2015 at 23:17 Comment(5)
Vlad, do you have a date for the "later" addition of void? My dabbling with C goes back to roundabout the late 80s, and I clearly recall having difficulty envisioning a 'typeless variable type' (as I interpreted it then).Becalm
@Becalm No, I have not but if I am not mistaken it is Straustrup that suggested the type void.Actable
IIRC void and void* were introcuced in K&R2 /c89/c90/ansi/iso (inspired by C++) A few compilers already had it as an extention.Pantagruel
Yes, 'void' was in ANSI C, I recall this being discussed in net.lang.c around 1986 or 87 or so, and wasn't in e.g. PCC which was in fairly common use then.Minacious
@wildplasser: that resonates nicely with my recollection. First time I ever laid eyes on C was for my 3rd computer: an Atari ST. So yeah, must have been late 80s or possibly very early 90s. When I got started with Turbo C (the all-in compiler-and-linker, distinct dark blue editor and a menu bar), that had both void and the current style of function declarations.Becalm

© 2022 - 2024 — McMap. All rights reserved.