Thank you, Ryan, an very nice elaboration! On Fri, Mar 14, 2025 at 8:56 PM Ryan Carsten Schmidt < ryandes...@macports.org> wrote:
> I see you already received a reply while I was writing my response but I > may as well send it in case it explains anything in a way that is more > understandable. > > On Mar 14, 2025, at 17:14, Kenneth Wolcott wrote: > > > I was compiling a C example of a Rosetta Code task recently.. > > > > https://rosettacode.org/wiki/Vampire_number#C > > > > Here's my clang error(s): > > clang -lm -o ./vampire_number ./vampire_number.c > > Undefined symbols for architecture arm64: > > "_dtally", referenced from: > > _fangs in vampire_number-2cd7e6.o > > _fangs in vampire_number-2cd7e6.o > > _fangs in vampire_number-2cd7e6.o > > "_max", referenced from: > > _fangs in vampire_number-2cd7e6.o > > "_min", referenced from: > > _fangs in vampire_number-2cd7e6.o > > "_ndigits", referenced from: > > _fangs in vampire_number-2cd7e6.o > > ld: symbol(s) not found for architecture arm64 > > clang: error: linker command failed with exit code 1 (use -v to see > invocation) > > All of the undefined symbols are for functions that use the "inline" > keyword: > > > inline xint max(xint a, xint b) { return a > b ? a : b; } > > inline xint min(xint a, xint b) { return a < b ? a : b; } > > inline int ndigits(xint x) > > { > > int n = 0; > > while (x) n++, x /= 10; > > return n; > > } > > > > inline xint dtally(xint x) > > { > > xint t = 0; > > while (x) t += 1<<((x%10) * 6), x /= 10; > > > > return t; > > } > > The "inline" keyword was introduced to the C language standard in c99. In > c99 and later, it tells the compiler what code to use *if* it chooses to > inline the function, but it *does not* tell the compiler *to* inline the > function. Some compilers treat it as a *suggestion* to inline the function > but they might ignore that suggestion. If the compiler chooses *not* to > inline the function, then it will look for a non-inline definition of the > function. Since this code doesn't contain a non-inline definition of the > function, the result is an undefined symbol error. > > One solution is to remove the "inline" keyword, e.g.: > > xint max(xint a, xint b) { return a > b ? a : b; } > xint min(xint a, xint b) { return a < b ? a : b; } > > etc. The compiler can still choose to inline the function. > > Another solution is to keep the "inline" keyword (because you want to make > the suggestion) and to tell the compiler that you want the same code to be > used if the function is not inlined. There are two ways to do that: one is > to precede "inline" with the keyword "extern" (meaning you want the > function to be reachable from outside this translation unit (i.e. outside > of this source code file)) and the other is to precede it with the keyword > "static" (meaning you will only be using this function within this > translation unit / source code file). There are some caveats to both > "extern" and "static" when a project consists of more than one translation > unit, as discussed more in the wikipedia article linked below. Since this > project is only a single source code file, "static" is probably the best > choice, e.g.: > > static inline xint max(xint a, xint b) { return a > b ? a : b; } > static inline xint min(xint a, xint b) { return a < b ? a : b; } > > etc. It would also be reasonable to precede all of the other functions > other than main with "static" since they are not being called from other > translation units either. > > Another solution that doesn't require any changes to the code is to > compile in gnu89 mode. Although "inline" was added to the standard in c99, > it appeared as an extension in gcc compilers in gnu89 mode, which was the > default mode along time ago, but per the wikipedia article below "inline" > behaves differently in gnu89 than it does in c99 and later. In gnu89, > "inline" is equivalent to c99's "extern inline". You can downgrade to this > mode by adding the compiler flag "-std=gnu89", e.g.: > > clang -std=gnu89 -o vampire_number vampire_number.c > > Another "solution" that doesn't require any changes to the code but which > is fragile is to tell the compiler to inline functions, for example by > using an optimization flag: > > clang -O3 -o vampire_number vampire_number.c > > This (by itself) is fragile because if the compiler still chooses not to > inline the function, you'll be back to an undefined symbol error. Different > compilers, or different versions of a compiler, might make different > choices about whether to inline functions at a particular optimization > level. Adding an optimization flag in addition to one of the other fixes > suggested above is fine. > > There's a lot of good information about inline functions here: > > https://en.wikipedia.org/wiki/Inline_function > > >