General C programming question - Page 1
Forum section: electronics / general

author: NW27
author_rank: Regular Contributor
timestamp: July 19, 2018, 12:43:54 pm
content: Hi,Q1. In the source file that has the main() function, is there any particular reason to have the function prototypes followed by the main() followed by the actual functions?Why not just put the complete functions above the main and dispense with the prototypes completely?The only thing I have encountered is that you need to be mindful of the function ordering ie called functions must be above the calling code. Otherwise I do not see any advantage.Note - I was a pascal programmer before becoming a C programmer.2. How many people have a header file for the main() source file and what do you use it for?Thanks,Neil.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: July 19, 2018, 12:55:47 pm
content: 1. Declaring explictly is handy for clarity but also allows forward references which can be useful is a and b call each other.2. Mostly. Putting the declarations in a header file means they can be reused elsewhere and sprinkling #ifdef to prevent issues with being #included more than once.

author: Unknown
author_rank: Unknown
timestamp: July 19, 2018, 01:03:01 pm
content: Protoypes allow you to use functions before they are defined (many reasons to do this). For a simple program with everything in order you don't need to use them (unless you are using a really old pedantic compiler).Headers are there for global declarations. Declare global variables, includes and function prototypes. Again for a simple program it probably seems overkill, but when things get more complex, they are very useful.

author: Kjelt
author_rank: Super Contributor
timestamp: July 19, 2018, 01:17:15 pm
content: In a main.c file it is more readable when the first actual function is the main() itself. First digging through all the other functions is a bit PITA.Usually a main.c file has no or very little functions, it does call lots of functions, sometimes a statemachine that kind of stuff.Mostly for a good architecture you call the functions from other c files, the functions are defined in the header files which is the answer to your second question.Almost every main.c file has header files on top.Why use other c files and header files ?For a lot of reasons most important I can think of : clearity and re-usability.See it like this, if you make functions you use often like helper functions or aka service / support functions, put them in a seperate support module.You can use it each and every program you write, just include the h file and include the c file in your project.I do this all the time, if I pick a new chip for instance a I2C clock IC or whatever I write the interface code in a seperate C file with the filename = IC name.If i need the chip in some product I just copy the icname.c and h file in the new software, done.I could not code without header files, you get crazy big main.c file with spaghetti code, unreadable for anyone else and totally not re-usable for the next project.BTW this has little to do with C alone, with Pascal, C++, C#, Java just to name a few it is all the same.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 19, 2018, 01:33:57 pm
content: Delphi has single-file units, which has two parts - interface and implementation.In C, this typically corresponds to two files:.h file contains everything which you would put into the "interface" section in Delphi unit..c file contains things which you would put into the "implementation" section.When you want to use a compilation unit, such asCode:uses Something, SomethingElse;you simply include the .h fileCode:#include "Something.h"#include "SomethingElse.h"However, in C, the .h file is simply an include. You may include anything, such as a sine lookup table, for example.Functions don't need separate declaration, but if your functions include circular calls (such as A calls B and B calls A), you will need separate declarations. This is the same as "forward" declarations in Delphi.I don't usually have an .h file for the main.

author: ajb
author_rank: Super Contributor
timestamp: July 19, 2018, 05:57:21 pm
content: Quote from: NW27 on July 19, 2018, 12:43:54 pmIf you mean a "main.h" that is only included by main.c, not usually.Generally the point of moving declarations out of a .c file (really a compilation unit) and into a header is so you can make other compilation units aware of information or functionality that the first compilation unit provides. So if you have a library that your application uses, your library's public interface, which is the collection of functions and variables that your application must call or access, should be declared in a header file that you include in your application's files wherever the library functionality is used. The declarations for functions and variables used only within the library may remain in the c file if the library is simple, or for more complex multi-file libraries you might have another header file that is only included within the library.So because main.c canonically does not provide functionality to anything outside itself, it's not necessary to declare any of its contents to any other compilation unit. So the only benefit to moving main.c's declarations to main.h is to hide them from main.c. Now you might have a bunch of functions or some global variables that are used all over your application, and in that case you may well have a project-specific header that contains all of those declarations, and perhaps has its own includes for libraries that are used everywhere in the project. However, this is a bit of a code smell, indicating that the project probably does not exhibit good encapsulation or decoupling, and it's easy to wind up with circular dependencies this way.

author: apblog
author_rank: Regular Contributor
timestamp: July 19, 2018, 06:11:33 pm
content: It is very common to find C programs with main() at the bottom of the module, for the reason you describe. There is nothing wrong with this style.

author: rhb
author_rank: Super Contributor
timestamp: July 19, 2018, 07:18:34 pm
content: Quote from: apblog on July 19, 2018, 06:11:33 pmQuite agree. And I've maintained a few million lines of other people's code. Generally a main.h file will be used if main.c only contains main() and perhaps some functions or variables which are declared static at file scope so that they cannot be seen outside of main.c. In large programs where main is many lines this allows interposing a debugging function between main() and another function without affecting any other files that might reference that function. You can also produce the same result by partially linking the interposed function and the function where you want to reference it, though you may generate some warnings or encounter final link problems. It's pretty system dependent.Generally, once a file exceeds a few hundred lines, it's time to consider dividing it up. And libraries should always be a single function per file unless you have functions which share a file scope global variable. If multiple functions are declared in a single file, referencing any function from that file will include *all* the functions in the file. This makes for considerable memory consumption at run time.

author: Kjelt
author_rank: Super Contributor
timestamp: July 19, 2018, 08:35:55 pm
content: Quote from: rhb on July 19, 2018, 07:18:34 pmReally? One function per file? Are you kidding?

author: rhb
author_rank: Super Contributor
timestamp: July 19, 2018, 10:05:41 pm
content: SQuote from: Kjelt on July 19, 2018, 08:35:55 pmI most certainly do. The linker doesn't include just the function you reference. It includes the entire file. It has to because of the scoping rules. All those things in the language standards that most programmers never read have profound consequences for how things *must* be done.So if someone foolishly places all the functions in a large library in one file, the system has to allocate space for the entire library to run the program. I've had to fix this when taking over million line codebases by writing a program to break the file up into pieces. I did it because people were unable to run the programs because they ran out of memory on $30-40K workstations.I'm running Hipster, the OpenSolaris derivative. The xclock clone showing the date and time consumes 80 MB of memory! On Solaris 10 u8 it takes 6 MB. I don't have a Sun 3/60 any longer, but I can assure you that xclock did not consume 30% of the16 MB memory on it.All of this is a consequence of programmers who do not actually know what is happening at all levels when they write a line of code. I've made a lot of money fixing the mistakes engendered by their ignorance.

author: NW27
author_rank: Regular Contributor
timestamp: July 19, 2018, 10:20:28 pm
content: HiThanks for all of the responses.I gotta say, coming from a Pascal environment (BP, Delphi & AVRCo), I'm quite disappointed by the embedded C compilers and their evolution.Examples1. The compilers do not optimize out unused functions.2. No startup/ shutdown code functions for individual source code files ie an I2C Display init unit.3. No further development of the language for pascal style interface and implementation areas ie local global variables that can only be seen within the local source file.The list could go on for quite a while.There have been changes to enhance functions ie c++ but not enhancements to the not specifically IDE but to the higher level code functionality.Sent from my SM-N920I using Tapatalk

author: westfw
author_rank: Super Contributor
timestamp: July 19, 2018, 10:23:48 pm
content: >QuoteOr, you could use a more modern C compiler that doesn't do that.See the-ffunction-sectionscompiler option and-gc-sectionslinker option of gcc, for example.(If your compiler DOESN'T support such a feature, the single-function-per-file is good advice, assuming that the functions are distinct. Often a high-level library won't benefit very much from such shenanigans.)(And ... this is a way that C++ causes code bloat. Virtual functions ... blah blah ... will wreck havoc with they "I used that function" logic, and end up including the function from the base class whether it's ever called, or not...) (Unless there's a magic compiler option I missed that prevents that!)

author: glarsson
author_rank: Frequent Contributor
timestamp: July 19, 2018, 10:31:29 pm
content: Quote from: NW27 on July 19, 2018, 10:20:28 pmpPlace the variable at file scope (not inside a function) and declare it static. The variable will be seen inside functions in this file but will not be visible to other functions.

author: rhb
author_rank: Super Contributor
timestamp: July 20, 2018, 01:43:08 am
content: It's not the compiler. It's ld(1) and ar(1) in a Unix environment. I was paid very well for knowing in great detail who did what to whom.Unfortunately, Gnu/Linux is increasingly not Unix. So I won't say what it does. They keep randomly changing option flags and everything else, just because they can. It's *really* annoying to have a complex shell script blow up because some fool has changed the semantics of some option. Linux is to Unix what Windows NT is to Linux. Nice try.

author: NW27
author_rank: Regular Contributor
timestamp: July 20, 2018, 06:25:24 am
content: Quote from: rhb on July 20, 2018, 01:43:08 amSorry, I don't understand.Sent from my SM-N920I using Tapatalk

author: donotdespisethesnake
author_rank: Super Contributor
timestamp: July 20, 2018, 06:33:37 am
content: Quote from: rhb on July 19, 2018, 10:05:41 pmYes, there are a lot of those about. They are often found giving their "expert advice" on web forums.

author: Kjelt
author_rank: Super Contributor
timestamp: July 20, 2018, 07:41:52 am
content: Quote from: rhb on July 19, 2018, 10:05:41 pmI am not saying to put everything in 1 file, just all the functions that belong to a certain cluster or component otherwise you get other problems like in the next project someone will forget to include all the files.So for instance your clock example should be a clock.c file with SetClockTime(..), GetClockTime(..) perhaps SetClockTimeZone(..), GetClockTimeZone(..) etc.Those functions should all be included in the same component, eg c file. You are NOT going to make 4 or more seperate c files from those functions that is ludicrous and from the 6 companies I worked for over the past 20+ years I luckily never ever seen that. A C programmer that did that, would have been fired the same week.But seperating other functionality that in your example probably was stuck in to one bloat file that consumes masses of ram, like utils or something like that, that is also not how it should be.Sidenote 1: modern compilers can optimize unused functions out, always check esp in embedded projects or your GUI bloat example.Sidenote 2: self modifying Unix/Linux libraries can be a PITA since every new release you have to do it over and over again or not update which might be worse.Instead you should raise a ticket with the maintainers and ask for seperation of functionality in more than one library due to exhaustive memory usage.

author: newbrain
author_rank: Super Contributor
timestamp: July 20, 2018, 08:08:51 am
content: Quote from: NW27 on July 19, 2018, 10:20:28 pmI've gotta say, coming also from a Pascal environment (but that was some 35 years ago...), that a deeper knowledge of C is needed before judging.It takes some time and effort to switch thought patterns that have become habit.I would not advocate one over the other (I have my own preference) but:They mostly do at the link stage, see westfw post above. At compile time, unless the program is compiled as a whole, is very difficult (read: impossible) to know what will be used and what will not.C leaves that to your good will and discipline. C++ has of course constructors and destructors.StandardPascal does not have any of this, Extended (ISO10206) I think does (if you can find a compiler), the rest are proprietary extensions...Scope, linkage, name space and storage duration rules are described in chapters 6.2.1, 2, 3, and 4 of theC standard. They are varied and powerful enough for most tastes, and definitely cover your example.The list could go on for quite a while.

author: rhb
author_rank: Super Contributor
timestamp: July 20, 2018, 01:16:44 pm
content: Quote from: Kjelt on July 20, 2018, 07:41:52 amI think we're talking about software on very different scales. For small programs it doesn't matter. And if you're using globals with file scope they have to be in the same file.Generally I put everything in one file for small programs. At a certain size, I split things up into related pieces. If I want to use something in another program I put it in a library so there is only a single version to maintain.Having had to maintain a 500,000 line code base which had been written by copy and modify with the same bugs repeated in a dozen functions all with different names, I shudder at the idea that everything should be in one file because someone might forget to include the function. They'll find out it's missing as soon as they try to link. And if it's used in more than one place it belongs in a library.Big packages like 500,000 line seismic processing systems with a dozen libraries are a whole other kettle of fish. For things of that size you need to be very aware of how the linker operates and how the libraries are structured internally.I think there's been considerable confusion in this discussion between the compilation and linking phases. I'm an old VMS and Unix programmer. I don't use Windows for programming work and I don't use IDEs for anything. I strictly use an editor, make and the command line.If you're doing mixed language (C, C++ and FORTRAN) programming you have to understand exactly how the linker works, what options are required, etc. You also need to understand the operation of the compilers at a similar level of detail. During the workstation wars of the 80's and 90's that meant i had to know how these worked on 6 different systems for a job porting a single package from VMS to Unix.Seismic processing is both compute and I/O intensive. A common operation done on all data requires in the simplest case summing at least several hundred thousand values into each output value for many millions of output samples. I read compiler optimization books, not because I'm going to write a compiler, but because I need to know in great detail the hardware level implications of the various optimizations. I pay attention to cache associativity as a careless choice of memory access can flush the cache line on every access. So in the case of a 16 byte cache and a 4 byte variable the data transfer between main memory and the cache goes up by a factor of 4x what it should be. And the longer the cache line, the worse it gets.I learned all this in the process of fixing problems in several million lines of other people's code. Most of it written by scientists with few if any comments. In one case I had to find and read a journal article just to find out whether the function was operating in the time or frequency domain. Once I found out what it did I rewrote it. It was probably 30-40 lines.I learned about the linker behavior from a set of libraries which had every function in the library in a single file.

author: brucehoult
author_rank: Super Contributor
timestamp: July 20, 2018, 01:41:50 pm
content: Quote from: NW27 on July 19, 2018, 12:43:54 pmI've never done this. I've maintained millions of lines of code and I don't even remember seeing it done since maybe the 1980s -- I'd say since ANSI C came out.Except for actual .h files, I never write a prototype for a function separately from the function except to break actual mutual recursion. I take seeing a prototype internal for a file as a signal that there *is* mutual recursion involving that function prototype and the immediately following function -- and I'll expect to see the implementation right after that.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 20, 2018, 01:54:31 pm
content: Quote from: NW27 on July 19, 2018, 10:20:28 pmYes, Pascal (I only used BP/Delphi) is better organized than C. If you don't use strings, but rather use C-like PChar, it can do practically the same as C, but it is more convenient than C. It also much easier for the compiler, so your compiles get faster (old Delphi compiler can compile 300K lines in less than a second).However, C is more universal, so often you don't have much choice but using C.Once you get used to C quirks, it is not that much different from Pascal.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 20, 2018, 02:01:10 pm
content: Quote from: rhb on July 19, 2018, 07:18:34 pmThe GNU linker will happily include just the code that's actually called (as far as it can tell) and strip away unreachable code from the binary. To do that, you have to tell the compiler (via the "-ffunction-sections") option to put each function in its own "section" and then pass the "--gc-sections" option to the linker. That's basically the standard on any microcontroller build where memory can be at a premium.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 20, 2018, 02:12:02 pm
content: Quote from: NorthGuy on July 20, 2018, 01:54:31 pmI see a lot of people here talk about compile/build time, which seems surprising. This is an embedded forum, so the builds we're talking about aren't that big--it's not like you have to compile the linux kernel each time. Is the difference between a 30 second build and a 2-minute build really important? Seems like the impatience of youth, IMO.Sure, when you're chasing that bug at 2am and on your 2nd Red Bull, that extra 90 seconds feels like an hour.

author: rhb
author_rank: Super Contributor
timestamp: July 20, 2018, 02:26:04 pm
content: Quote from: andyturk on July 20, 2018, 02:01:10 pmDoes nm(1) show you that unused functions are absent?The widespread use of the Gnu toolchain for MCUs may well have resulted in the linker behavior changing. With MCU makers funding work on the Gnu dev tools it would make sense to do that. What actually happens when you pass those flags may be target dependent. TI only recently started supporting the Gnu toolchain for the MSP430 with internal staff. So I would not be at all surprised if it varied from device to device.The only way to be sure is to look at the symbol table with nm(1) and see whether the unused text segments are present. There may be symbol table entries created by function prototypes, but those will go away when the symbol table is stripped. What matters is are the text segments there.I ran into the problem 25 years ago. So I just adopted work habits that avoided it.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 20, 2018, 02:45:08 pm
content: Quote from: andyturk on July 20, 2018, 02:12:02 pmThe difference between 2-minutes and less than a second is huge. It allows you to compile frequently, which changes your workflow and lets you be substantially more productive.If you need to wait 2 minutes for every compile, it forces you to write more code between the compiles, and if you've got a problem, you now have to wonder which of the changes caused the bad behaviour, perhaps unroll some of your changes back, start a debugger. This all takes time and makes your work tedious. You get tired faster.If the compile time is less than a second, you can do a single change, compile and check the result immediately. If something bad happens, you know the problem was at your last change. Of course, in the embedded, there's a programming delay and then there could be a delay with starting the test, but I always try to make such delays as short as possible.

author: brucehoult
author_rank: Super Contributor
timestamp: July 20, 2018, 03:19:01 pm
content: Quote from: andyturk on July 20, 2018, 02:12:02 pmI just read on reddit (I think) about a new programming language called Lily. I downloaded it off github and build it. It took almost exactly 1 second on my i7 NUC. I see in the makefile it's using -O2.There are 26 C source files, totalling 23560 lines of code. Once preprocessed that expands to 90905 lines.That's not quite the speed claimed for Delphi, but it's still bigger than most embedded projects -- the executable created has 230453 bytes in the text section.I wasn't tooooo bored waiting for it to compile.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 20, 2018, 03:26:21 pm
content: Quote from: rhb on July 20, 2018, 02:26:04 pmDunno, I never actually searched for unused functions.QuoteValid point. But GCC for Cortex-M covers the majority of my projects (i.e., 100%), so I haven't had to worry about portability too much. I'm interested in trying out Clang/LLVM for Cortex-M one of these days, but I think both toolchains use the same approach to dead code elimination.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 20, 2018, 03:47:09 pm
content: Quote from: NorthGuy on July 20, 2018, 02:45:08 pmWell, lets chalk that up to differences in development style. Most of the times I compile something, it's to make sure its syntactically correct, or that the linker produces a binary--not to actually run or flash the code.QuoteNobody is forcing you to write more code. I had to wait 45-minutes for a build back in the 80s, so I'd go into the machine room and practice juggling.https://xkcd.com/303/QuoteLightning fast builds seem to be possible with IDEs and/or tiny programs, but neither of those apply to the projects I end up working on.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 20, 2018, 04:06:08 pm
content: Quote from: andyturk on July 20, 2018, 03:47:09 pmHow about you leave your deck to be run overnight and come back next day for the printout?Quote from: andyturk on July 20, 2018, 03:47:09 pmIt would if you had fast Delphi-style compilers.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 20, 2018, 04:19:17 pm
content: Quote from: NorthGuy on July 20, 2018, 04:06:08 pmZing!QuoteCan I run my awk scripts from within Delphi?

author: NorthGuy
author_rank: Super Contributor
timestamp: July 20, 2018, 04:52:42 pm
content: Quote from: andyturk on July 20, 2018, 04:19:17 pmI'm not talking about Delphi (as IDE), just the underlying compiler.BTW, I still use the command-line version of the Delphi compiler. I often write simple programs to generate code (tables, C code, or even Pascal code), which I use in the places where you use your awk scripts I guess. It is my "go to" compiler when I need to write something for my own use.

author: Kjelt
author_rank: Super Contributor
timestamp: July 20, 2018, 10:29:40 pm
content: Quote from: andyturk on July 20, 2018, 03:47:09 pmI wish it was the 80s, if I do a full build it will be scheduled at night since sometimes it takes two hours to completeLuckily you don't need full builds often, just compiling your files or even the entire subcomponent takes seconds.

author: westfw
author_rank: Super Contributor
timestamp: July 20, 2018, 10:58:11 pm
content: >Quoteyes, of course. We're not just making this up, you know.Arduino in particular is heavily reliant on this linker/compiler feature get get rid of most of the class methods that C++ programmers love to add to their source code, which are hardly ever all used in the same sketch.For an obvious example, compile a simple "Serial.print("Hello World");" sketch and note that the .elf file ends up with only:WWHackintosh<10025>avr-nm -SC sketch_jul20a.ino.elf | grep Print00000114 00000006 t Print::availableForWrite()00000112 00000002 t Print::flush()000000be 00000054 t Print::write(unsigned char const*, unsigned int)In spite of Print.cpp.o being much more ... bloated:WWHackintosh<10026>avr-nm -SC ../arduino_build_474806/core/Print.cpp.o | grep print00000000 000001b6 T Print::printFloat(double, unsigned char)00000000 00000092 T Print::printNumber(unsigned long, unsigned char)00000000 00000048 T Print::print(__FlashStringHelper const*)00000000 00000004 T Print::print(char const*)00000000 0000001c T Print::print(String const&)00000000 00000014 T Print::print(Printable const&)00000000 0000000e T Print::print(char)00000000 00000004 T Print::print(double, int)00000000 0000000e T Print::print(unsigned char, int)00000000 0000000e T Print::print(int, int)00000000 0000000c T Print::print(unsigned int, int)00000000 00000090 T Print::print(long, int)00000000 0000001a T Print::print(unsigned long, int)00000000 00000024 T Print::println(__FlashStringHelper const*)00000000 00000024 T Print::println(char const*)00000000 00000024 T Print::println(String const&)00000000 00000024 T Print::println(Printable const&)00000000 00000024 T Print::println(char)00000000 00000024 T Print::println(double, int)00000000 00000024 T Print::println(unsigned char, int)00000000 00000024 T Print::println(int, int)00000000 00000024 T Print::println(unsigned int, int)00000000 00000024 T Print::println(long, int)00000000 00000024 T Print::println(unsigned long, int)00000000 00000008 T Print::println()>QuoteNo, it's pretty platform-independent. And sort of obvious in retrospect - it just tells the compiler to generate object files in a way that makes it possible for the linker to treat individual functions as if they had come from individual .o files. Whether existing libraries are compiled with the necessary compiler switches is another question. AFAIK, while it's "obviously easy" for a compiler to put each function in a separate "section" (-ffunction-sections) during the compile phase, it's not possible to split up an existing .o file...Although my impression is that outside of the embedded arena, most programs simply map in huge .dll run-time libraries with no attempt to optimize which functions are actually present. "If it's not used, the pager will never read it in. Maybe. Who cares - 8GB of RAM and 64bits worth of address space!"

author: rhb
author_rank: Super Contributor
timestamp: July 21, 2018, 12:15:11 am
content: Very nice to know. Thank you. I'm glad to see that some progress is being made. Most of the time I feel as if software engineering is going backwards.I just wish the options would be used more. I can't run Hipster on a 2 GB dual core Atom machine because Firefox consumes all the memory to the point you can't enter text as I am doing now. It's like being at the end of a 300 baud dialup.

author: westfw
author_rank: Super Contributor
timestamp: July 21, 2018, 01:10:26 am
content: QuoteDesktop programs are convinced that the NEED EVERY FUNCTION, and I don't think you can blame their bloat on including unused library functions. (I'd be willing to be proven wrong, though.)Also: VM - if firefox has vast number of unused functions, they probably wouldn't get paged in, and wouldn't cause the usage you're seeing.

author: JanJansen
author_rank: Frequent Contributor
timestamp: July 22, 2018, 12:40:07 pm
content: Here is my "expert advice" :Why would you type a function prototype if you dont need to type it.So yes i organize my code that i would not need any function prototype if possible.I only place function prototypes in main.h when they need to be called by another file.If i need function prototypes that will only be used in main.c then i place the function prototype in main.c to make things more clear.Main.h includes all other files from your project, and all headers include main.h, while the .c files only include theyr own header to start with.Sometimes there are problems, i,m not sure how or what, in general it works like this with me.

author: Kjelt
author_rank: Super Contributor
timestamp: July 22, 2018, 03:22:34 pm
content: My expert advice never ever use a main.h file, it should not exist.Why?Because a header file per definition is included by another file, thereby implicitly states that the belonging *.c file is a resource for another file to use.Main.c is the highest in the hyrarchy it must and will never be included by another file, therefore it shall not have a header file and it should also not contain public functions needed by other files.

author: ajb
author_rank: Super Contributor
timestamp: July 22, 2018, 05:59:46 pm
content: Quote from: JanJansen on July 22, 2018, 12:40:07 pmFunction prototypes are inherently a violation of the DRY* principle which means they're kind of a bummer to write, but trying to structure a program to avoid them is kind of a bigger bummer. Once you've defined a function, just copy and paste the first line of the definition to the top of the file and never worry about the order of your definitions again. You also get a nice table of contents at the top of the file this way. Then if you decide later on to make that function public, you can copy the prototype to an .h file.* Don't Repeat Yourself

author: NorthGuy
author_rank: Super Contributor
timestamp: July 22, 2018, 06:42:54 pm
content: Quote from: ajb on July 22, 2018, 05:59:46 pmMy function declarations may change as I go. If I have it in two different places, I need to synchronize them - and that's a lot of worryingAbsolutely no way I would write redundant declarations unless I have to.

author: Kjelt
author_rank: Super Contributor
timestamp: July 22, 2018, 09:31:50 pm
content: Quote from: ajb on July 22, 2018, 05:59:46 pmAlthough this works (compiles) it is actually better IMO to not use the variable names in the declaration.The declaration is what the compiler needs and only has to contain the function name and data types for the parameters.The definition (implementation) is what the linker needs so only there the variable names are relevant.It is then also easier to change you're variable names.Quote from: NorthGuy on July 22, 2018, 06:42:54 pmLet the compiler worry for you , it will throw an error on any significant difference between declaration and implementation. That is you changed data types eg from uint16 to uint32 for instance, but not if you changed the variablename of any parameter if you follow the above advice.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 22, 2018, 10:24:20 pm
content: Quote from: Kjelt on July 22, 2018, 09:31:50 pmCompiler finding the differences, me fixing them - looks like useless work to me.

author: sleemanj
author_rank: Super Contributor
timestamp: July 22, 2018, 10:28:20 pm
content: Quote from: Kjelt on July 22, 2018, 09:31:50 pmThe declaration forms part of your headers (be they in a .h file, or at the top of your .c), which can be considered documentation,int foo(char, char, char);is not helpful to the developer who wants to use function foo. If that is all that is in the header file/section, then they have to then dig through to find the implementation of this function just so they can see which order the operands are in.Not naming your parameters in a declaration is lazy, it costs time, and has the potential to introduce errors - subtle ones caused by a developer using foo switching operands which just happened to work out the same way in their initial testing.If you are using declarations, make them verbose and proper, don't take short cuts, not using declarations is preferable to using lazy ones.

author: Kjelt
author_rank: Super Contributor
timestamp: July 23, 2018, 07:05:39 am
content: Agree, I am just saying it is not necessary not that from a readability point of view is recommendable.Still if you document your function with lets say doxygen you can read the documentation for the function.Also you can use good comments you need to go to the definition anyway.

author: Kjelt
author_rank: Super Contributor
timestamp: July 23, 2018, 07:07:02 am
content: Quote from: NorthGuy on July 22, 2018, 10:24:20 pmFrom that point almost 3/4 a good SW programmer does is "useless work" untill someone else needs to take over to read or modify the code

author: JanJansen
author_rank: Frequent Contributor
timestamp: July 23, 2018, 02:02:03 pm
content: Quote from: Kjelt on July 22, 2018, 03:22:34 pmThe main.h you could rename to allfiles.h, it includes all files, so you only have to include main.h in your other headers.*i am repeating myself, lets say clarify, now i stop repeating this, i hope you see the logic now.+ i had made a game engine with many many files, you are suggesting that main.c only has a main function in it,i learned from that mistake, i now have a few files for my latest game engine, less then 100, placing many things in 1 file, else i would have more then 100 files, i think the compiling takes also longer, + you cant have all files open at once in screen.I cant perfectly describe how good this simple change is.

author: Kjelt
author_rank: Super Contributor
timestamp: July 23, 2018, 02:06:59 pm
content: A compile will usually take less time when multiple files unless they all are referencing each other.Only modified files will be recompiled at build time unless you perform a clean build ofcourse.Sorry but I have to ask what is your experience as profesionall software engineer ?

author: JanJansen
author_rank: Frequent Contributor
timestamp: July 23, 2018, 02:13:54 pm
content: Quote from: NorthGuy on July 22, 2018, 06:42:54 pmSuppose you need a function prototype i have a small tip for you when things get hectic : use a construction parameter struct to pass instead of many arguments.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 23, 2018, 02:53:42 pm
content: Quote from: JanJansen on July 23, 2018, 02:13:54 pmThis makes things more complicated when you call a function. You either need to create a structure to pass to the function, or you need to use compound literals.You can use a macro which contains the whole declaration, but for some reason it doesn't sit well with me.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 23, 2018, 03:17:24 pm
content: If when you open a main.c, main() isn't the last thing at the bottom, that's a bad smell, means something fishy is going on.

author: JanJansen
author_rank: Frequent Contributor
timestamp: July 23, 2018, 04:10:32 pm
content: Quote from: NorthGuy on July 23, 2018, 02:53:42 pmTrue,i had 23 arguments in the class init + internal init + create class function ( create class & class init with overload ).I spended so much time on the arguments, now all good.Yes have to create the struct, only once, then can adjust.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 23, 2018, 04:50:02 pm
content: Quote from: JanJansen on July 23, 2018, 04:10:32 pmClass is already a structure. In the constructor, you initialize its variables the best you can (or not at all), then, once the class is created, you set the member variables as you please.

author: JanJansen
author_rank: Frequent Contributor
timestamp: July 23, 2018, 04:54:09 pm
content: I do my best to set variables only once.

author: Kjelt
author_rank: Super Contributor
timestamp: July 23, 2018, 05:27:10 pm
content: There is the culprit you are a C++ programmer.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 23, 2018, 07:05:48 pm
content: Quote from: NorthGuy on July 23, 2018, 04:50:02 pmMinor nitpick...An "instance" (or "object") is created at runtime via a C++ constructor. But a "class" is something created by the programmer.Also, constructors are a great thing because they're a systematic way to ensure that everything is initialized. However, a good rule of thumb for embedded C++ is to avoid doing real "work" in a constructor and only set member variables to values that are constant at link-time. Compile-time constants are perfectly fine and so are addresses of things that the linker will determine. But calling code that does computation within a constructor can be risky because it's difficult to predict the initialization order of static C++ objects, and constructors of static instances are called *before* main.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 23, 2018, 07:07:19 pm
content: Quote from: Kjelt on July 23, 2018, 05:27:10 pmPiffle. C++ is awesome for embedded work. Better than straight C, IMO.

author: Kjelt
author_rank: Super Contributor
timestamp: July 23, 2018, 08:47:30 pm
content: Quote from: andyturk on July 23, 2018, 07:07:19 pmWhatever, I am not starting that endless debate again.I was saying it since we are in a C topic

author: JanJansen
author_rank: Frequent Contributor
timestamp: July 24, 2018, 01:28:33 pm
content: Ok the debate is on, it dont matters C or c++, you can use a struct, you can use the same trick in C, i should not have mentioned the word class to avoid debates.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 24, 2018, 02:14:32 pm
content: There was a C with objects, before C++.

author: brucehoult
author_rank: Super Contributor
timestamp: July 24, 2018, 04:46:05 pm
content: Quote from: GeorgeOfTheJungle on July 24, 2018, 02:14:32 pmReally? Which one?Stroustrup's own "C with Classes" was started in 1979, and C++ in 1983 was a rename rather than a new language.Cox's Objective-C was officially started in 1984 I believe. I don't know if it was unofficial before that.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 24, 2018, 08:02:07 pm
content: Lightspeed C/Think C, it was a thing for the Mac:https://en.wikipedia.org/wiki/THINK_C

author: brucehoult
author_rank: Super Contributor
timestamp: July 24, 2018, 09:57:59 pm
content: Quote from: GeorgeOfTheJungle on July 24, 2018, 08:02:07 pmI used it, although I used THINK Pascal and MPW C and Pascal more. And CodeWarrior years later when the PowerPC hit.THINK C came out in mid 1986 several years after C++ and Objective C (which both existed before the Mac was even released).

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 25, 2018, 09:08:48 am
content: Quote from: brucehoult on July 24, 2018, 09:57:59 pmExisted before, but only in Stroustrup's head :-)https://en.wikipedia.org/wiki/C++#HistoryQuote from: wikipediaThe Mac's birthdate is 22, January 1984.But yeah, my bad:in the Mac worldC with objects came before C++.

author: brucehoult
author_rank: Super Contributor
timestamp: July 25, 2018, 10:36:17 am
content: Quote from: GeorgeOfTheJungle on July 25, 2018, 09:08:48 amNo, cfront was available as open source software from (and used within) AT&T.QuoteThat first commercial implementation of C++ was Comeau C++, a supported version of cfront.But open source, unsupported, existed before it. You could get it if you asked AT&T (or Stroustrup).Apple's first C++ implementation in MPW was also a licensed version of cfront.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 25, 2018, 03:51:28 pm
content: Quote from: brucehoult on July 25, 2018, 10:36:17 amBut that came later, after LightSpeed/Think C.The first MPWs did not come with any C compiler. I still have the complete docs in binders somewhere. Remember Commando? The toolbox was written in Pascal, and the calling convention was Pascal, and the strings were Pascal style, it made not much sense to use anything else in the Mac, back then.

author: andyturk
author_rank: Frequent Contributor
timestamp: July 25, 2018, 04:18:46 pm
content: Quote from: GeorgeOfTheJungle on July 25, 2018, 03:51:28 pmYep, the original AppKit for the Mac was written in Object Pascal. One of my early projects (~1990) was writing a transpiler that converted Mac-specific Object Pascal code to "portable" C++ (which was still using cfront).

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 25, 2018, 05:54:35 pm
content: I knew I had seen that name before... (you know who) now I know where: c.s.m.p !!!

author: brucehoult
author_rank: Super Contributor
timestamp: July 25, 2018, 09:52:40 pm
content: Quote from: GeorgeOfTheJungle on July 25, 2018, 03:51:28 pmI didn't say otherwise. MPW C++ was just mentioned as another example of using cfront, which was available in both commercial and non-commercial versions. MPW C++ was much later .. it waited for cfront 2.0.Your claim was that THINK C (with objects) was before the first *commercial* C++. Which is true enough. But it was well after C++ was available non-commercially (on Unix).The first C++ that wasn't based on cfront was Zortech C++ in I think late 1988 or early 1989. I probably still have the box and floppies somewhere. It was done by Walter Bright based on his earlier Lightspeed C. Symantec later bought it and ruined it. Walter of course later created D.

author: brucehoult
author_rank: Super Contributor
timestamp: July 25, 2018, 10:05:58 pm
content: Quote from: GeorgeOfTheJungle on July 25, 2018, 05:54:35 pmMany years ago.I'm amazed there are still a lot of copies of this floating around:http://www.ralentz.com/old/mac/humor/mpw-c-errors.htmlhttps://groups.google.com/forum/#!original/comp.sys.mac.programmer/UkCjFPL4zEM/-lM87bJxPUcJA slightly different list had independently been posted by Alan Rosenthal a year earlier but I think I wasn't yet on usenet at that time and BIX was still my main internet forum.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 26, 2018, 06:39:53 pm
content: Quote from: brucehoult on July 25, 2018, 10:05:58 pm

author: westfw
author_rank: Super Contributor
timestamp: July 28, 2018, 07:10:11 am
content: I remember... buying a triple-threat package containing BDS C, MINCE, and Scribble.Alas, I don't think I ever used any of them: the CP/M system that they were supposed to run on never quite came together, as I over-estimated my abilities to hack both mainframes and microcomputers at the same time...

author: aandrew
author_rank: Frequent Contributor
timestamp: July 28, 2018, 01:50:20 pm
content: Quote from: rhb on July 19, 2018, 10:05:41 pmThis hasn't been true for quite some time now. Your experience is valid but dated.gcc has -ffunction-sections -fdata-sections which break every function and data into separate linker sections, and then the linker option -Wl,--gc-sections which removes all unused sections. I've used this for at least the last 5 years to keep my binaries small without resorting to one-func-per-file which is a practice I abhor because it leads to the hundred-file-project which is impossible to keep in my headspace.