Programming question - Page 1
Forum section: electronics / general

author: Ax_6
author_rank: Contributor
timestamp: April 01, 2013, 02:42:40 pm
content: Hello everybody!I wanted to make a little library for PICs in mplab x and I was wondering how can I make definitions (#define) able to be recognized by the library.For instance if I write in the main fileCode:#define _XTAL_FREQ 'number'then the file pic.h know its value and makes its counts. Starting from that, I've tried to do as followIn the main.c file I wrote:Code:#define MYVALUE 'number'And in the file mylibrary.h I wrote:Code:#define NICEVALUE MYVALUE*2 (just an example)As soon as I use NICEVALUE in mylibrary.c the compiler says that it does not recognize MYVALUE.. so, what am I missing?Thanks!

author: IanB
author_rank: Super Contributor
timestamp: April 01, 2013, 02:58:01 pm
content: You are missing how the pre-processor works. Read up on the C pre-processor.Also read up on header (.h) files and how they are used.

author: kfitch42
author_rank: Frequent Contributor
timestamp: April 01, 2013, 03:08:11 pm
content: When dealing with the C preprocessor (i.e. all that #... stuff) order matters.e.g.Code:/* main.c */#define MYVALUE 1234#include <mylibrary.h>In this case mylibrary.h would know MYVALUE, but in the following case it wouldn'tCode:/* main.c */#include <mylibrary.h>#define MYVALUE 1234By VERY careful though! in the first case mylibrary.h knows the value of MYVALUE when compiling main.c. BUT, mylibrary.h would not know the value of MYVALUE when compiling mylibrary.c (or any other file). This is why, as a rule of thumb*, #define's only live in header files. E.g.Code:/* myvalue.h */#define MYVALUE 1234Code:/* mylibrary.h */#include <myvalue.h>#define NICEVALUE (MYVALUE*2)And then both main.c and mylibrary.c can include mylibrary.h, and get the same value for MYVALUE.P.S.* as a rule of thumb you should always think before applying rules of thumb.

author: Ax_6
author_rank: Contributor
timestamp: April 01, 2013, 03:35:42 pm
content: Got it! Thank you really much.At this point what would be the best way (if there is any) to set some constant values for the library from the main.c without allocating any space in the RAM?For example I say in my main "constant value X is 23" and then mylibrary.c knows and uses it.

author: kfitch42
author_rank: Frequent Contributor
timestamp: April 01, 2013, 04:19:45 pm
content: I would suggest having an initialization function in the library. e.g.Code:/*main.c*/...mylibrary_initialize(23);...Code:/*mylibrary.c*/static int mylib_X;void mylibrary_initialize(int X) {mylib_X = X;/* ... configure some registers ... */}Generally the only reason to resort to the preprocessor is if you can't achieve good enough performance due to the extra memory reference for reading X ... something that is very unusual/unlikely.

author: AlfBaz
author_rank: Super Contributor
timestamp: April 01, 2013, 04:25:59 pm
content: Quote from: Ax_6 on April 01, 2013, 03:35:42 pmI don't think so. Defines are used by the pre-processor. It goes and substitutes all of the defined placeholders with whatever you have defines it as, then it passes it to the compiler.You could define your value x as an "extern" variable which you can declare and initialise in main. the linker will take care of the restEdit: You could declare your value X as an "extern" variable within your lib, the instantiate and assign the appropriate value in main

author: Ax_6
author_rank: Contributor
timestamp: April 01, 2013, 04:42:42 pm
content: Quote from: kfitch42 on April 01, 2013, 04:19:45 pmYeah, that was the solution I knew and tried to avoid for the reason that the compiler is allocating 4 byte for X that finally is just useless.The solution of AlfBaz is interesting after all, declaring an extern const value seems to be a possible way. Thank you all!

author: IanB
author_rank: Super Contributor
timestamp: April 01, 2013, 05:01:23 pm
content: Remember that pre-processor macros like #define don't necessarily save space.Suppose you do this:/* program.h */#define MYCONST 0x4433FF1EThis defines a numeric constant that is 4 bytes long (it requires 4 bytes of storage).Now suppose you reference this constant in 10 different places in your main program. That constant will appear 10 times, and thus take up 40 bytes of storage in total, since each copy is separately and individually allocated. Using a pre-processor macro like this is not necessarily more (or less) efficient than other techniques.

author: kfitch42
author_rank: Frequent Contributor
timestamp: April 01, 2013, 05:03:26 pm
content: Quote from: Ax_6 on April 01, 2013, 04:42:42 pmThe extern doesn't save space. Using the preprocessor would probably be the only way to avoid using those 4 bytes of RAM. The way the C spec is written an extern const still needs to take up space in RAM. In fact the C spec explicitly allows for modifying a const value at runtimeSo the initialization function is a slightly cleaner approach (IMNSHO). And, it actually gives the library control of how constant the constant is. The only downside is the increased code size (for the initialization function and a call to it), but it seems you already prefer larger code to larger RAM usage.I think the most DRY (Don't Repeat Yourself) approach that avoids allocating 4 'useless' bytes of RAM would be to have the user of mylibrary to create myvalue.h like I mentioned before.

author: kfitch42
author_rank: Frequent Contributor
timestamp: April 01, 2013, 05:16:00 pm
content: Quote from: IanB on April 01, 2013, 05:01:23 pmThe key here (when thinking about microcontrollers) is which storage is it taking up. Code space or RAM space? For many micros code space is much less constrained than RAM space.So the form of reference matters:Code:const int myconst = MYCONST; /* <= Probably 4 bytes of RAM space for myconst */bar = foo * myconst; /* But this multiply *might* take fewer instructions than the next example */Code:bar = foo * MYCONST; /* MYCONST may exist only as immediate values in a few (more?) instructions */Of course the proof of the pudding is in the eating ... so if that RAM (or code) space is precious in your application you would have to try each of these and see what the compiler produces.

author: Ax_6
author_rank: Contributor
timestamp: April 01, 2013, 06:18:49 pm
content: Quote from: IanB on April 01, 2013, 05:01:23 pmThat's true, but as kfitch42 said in this case I don't care how much space the code will take, I'm interested only in the RAM.Quote from: kfitch42 on April 01, 2013, 05:03:26 pmYou are right, my bad. I won't save RAM space anyway. And yes I agree, a function is a bit easier to understand.So... yeah thanks again for the help I'm going to try those different solutions and see which one take less RAM space and It's easier to modify. May the init will be the best...If you have other ideas keep posting them! This topic is becoming really interesting =D

author: andersm
author_rank: Super Contributor
timestamp: April 01, 2013, 06:36:29 pm
content: Quote from: kfitch42 on April 01, 2013, 05:03:26 pmThe standard does not mandate it. The C99 spec explicitly states that non-volatile const objects may be placed in read-only storage, and that storage does not even have to be allocated if the object's address is never used.

author: kfitch42
author_rank: Frequent Contributor
timestamp: April 01, 2013, 07:10:55 pm
content: Quote from: andersm on April 01, 2013, 06:36:29 pmGenerally, the compiler doesn't know if the object's address will ever be used for global scope objects (i.e. not 'static') since it doesn't know how the object will be used in other compilation units. Maybe some fancy compiler/linker could figure it out and unallocate the space, but I have never seen that happen(not that I have looked very hard).Hmmm, it seems my memory of the spec is not as good as I thought it was. I made a little program to try it out, and the results weren't quite what I expected. Firstly it seems the only way in C to cast away const'ness is to take an address (C++ has const_cast for this purpose). Secondly it seems modern compilers do put const values in read-only memory (at least with gcc on 64-bit linux).Code:/* consttest.c */const int x = 1;void deviousFunc(int y) {*((int*)&x) = y;}Code:/* constmain.c */#include <stdio.h>void deviousFunc(int y);extern const int x;int main(int argc, char** argv) {printf("X at start %d\n",x);deviousFunc(2);printf("X at start %d\n",x);return 0;}Code:$ gcc constmain.c consttest.c -Wall -o consttest$ ./consttestX at start 1Segmentation fault (core dumped)Digging in the seg fault occured at the assignment in deviousfunc.

author: andersm
author_rank: Super Contributor
timestamp: April 01, 2013, 07:48:26 pm
content: The C99 standard says (§6.7.3) "If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined." and the C++03 standard says (§7.1.5.1) "Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior." so modifying a const object may in fact not be allowed at all.

author: kfitch42
author_rank: Frequent Contributor
timestamp: April 01, 2013, 08:11:33 pm
content: Looks like that language is even in the C89 spec. So my memory is WAY off.Shame on gcc for not warning on the obvious naughtyness in my program.When all else fails ... blame the tools

author: Rufus
author_rank: Super Contributor
timestamp: April 01, 2013, 08:29:43 pm
content: Quote from: kfitch42 on April 01, 2013, 08:11:33 pmYou use a cast to tell the compiler you know what you are doing then expect the compiler to tell you you don't?

author: IanB
author_rank: Super Contributor
timestamp: April 01, 2013, 09:11:29 pm
content: Compilers will help you if you declare your intent. For example:Code:const int MyGlobalValue = 10;int main(){int *ptr = &MyGlobalValue;}Code:1>------ Build started: Project: ConstExperiment, Configuration: Debug Win32 ------1> main.cpp1>c:\visual studio 2010\projects\constexperiment\main.cpp(5): error C2440: 'initializing' : cannot convert from 'const int *' to 'int *'1> Conversion loses qualifiers