I wanted to know how could i make my code better by declaring the array 'data_pack[]' as a private .Im working in embedded field so i have limited RAM and Memory and all i wanted to do is two things.
1.I have a serial UI which gives me serial data of 50 Bytes and stores them directly to unsigned char data_pack[50]
2.And I wanted to access the array in another module to calculate and do other things.
Currently im keeping the array as global variable and can be accessed from any module in my project.If i wanted to keep data_pack[50] as private how can i access the array packets in another modules?
I hope i will be more clearer after explaining here:
I have four modules.
main.c
ui.c
display.c
display2.c
And If i wanted to keep data_pack[50] as pvt in ui.c and access it in other modules..how can i do it with out making it global.
For eg: In ui.c I would like the data_pack[] to get filled in and in
display.c and display2.c I want to access the array and read its
element.In that case how could i pass the array in to other source
files
What would be the best way to access data_pack[] by keeping it as private?
Or should i use structures instead?
You can make a global variable data_pack read-only in other compilation units by changing its forward reference to extern const .... For example, two files:
file1.c
char example[50] = {1, 2, 3};
void change() {
example[1] = 44;
}
file2.c
#include <stdio.h>
/* extern const char example[50]; array size not needed in extern declaration */
extern const char example[];
void change();
int main() {
printf("%d\n", example[1]); /* works */
change();
printf("%d\n", example[1]);
/* example[0] = 45; would cause a compile error */
return 0;
}
Therefore, file2.c will see example as an array of constant ints, meaning it can only be modified by file1.c. The code above would print 2, followed by 44.
However, you must be careful doing this. If the size of the array in the forward declaration differs from the actual size of the array in the compilation unit it is contained in, the compiler/linker will not tell you (at least it doesn't for me, using gcc -Wall -Wextra -ansi -pedantic). You can also leave the array size out of the extern declaration.
One thing to note is that the compiler may attempt to optimize the const value, as it may not expect the value to change, thus producing invalid optimizations. I've performed some tests, and GCC doesn't seem to do this, but as it is undefined behaviour you may wish to use a getter/setter solution instead.
Related
As discussed in Access symbols defined in the linker script by application, "Accessing a linker script defined variable from source code is not intuitive" - essentially, accessing their value usually isn't what you want (since they don't really have a block of memory assigned, as a true compiler variable), and only their accessed by their address. Is there an attribute that can be applied to the variable upon declaration, or perhaps a PC-Lint/static-analysis property/rule which can be applied to the variables?
/* Linker config (.icf) file */
define symbol __ICFEDIT_region_ROM_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x080FFFFF;
define symbol __ICFEDIT_region_ROM_size__ = (__ICFEDIT_region_ROM_end__ - __ICFEDIT_region_ROM_start__) + 1;
export symbol __ICFEDIT_region_ROM_start__;
export symbol __ICFEDIT_region_ROM_size__;
/* main.c */
void OS_SetROM(uint32_t start, uint32_t size){} // empty for demonstration only
int main(void)
{
extern unsigned int __ICFEDIT_region_ROM_start__;
extern unsigned int __ICFEDIT_region_ROM_size__;
// INCORRECT - both probably read as '0', depending on what's actually in those locations
// Can I get a warning or error about this usage?
OS_SetROM(__ICFEDIT_region_ROM_start__, __ICFEDIT_region_ROM_size__);
// CORRECT - *addresses of* linker-defined variables read
OS_SetROM((uint32_t)&__ICFEDIT_region_ROM_start__, (uint32_t)&__ICFEDIT_region_ROM_size__);
It would be nice to have the addresses declared and behave as pointers (as below), i.e. where you can use the value of the pointer variable to represent the address, and 'value-of' semantics make more sense (at least logically - more obvious that you wouldn't dereference in this case), but this isn't how they work - for that, the linker would have to assign a memory location as well and store the address there, or some special semantics of the compiler/linker, which doesn't appear to be possible...
void void OS_SetROM(uint32_t * const start, uint32_t size){} // empty for demonstration only
int main(void)
{
// would be nice, but not how it works
extern unsigned int * const __ICFEDIT_region_ROM_start__;
extern unsigned int const __ICFEDIT_region_ROM_size__;
OS_SetROM(__ICFEDIT_region_ROM_start__, __ICFEDIT_region_ROM_size__);
A compromise of-sorts could be to redefine these variables with an appropriate type, ala:
unsigned int * const p_rom_start = &__ICFEDIT_region_ROM_start__;
unsigned int const rom_size = (unsigned int)&__ICFEDIT_region_ROM_size__;
void OS_SetROM(unsigned int * const p_start, unsigned int size);
OS_SetROM(p_rom_start, rom_size);
which helps collect the 'unintuitive' accesses into one place and type-safe accesses thereafter, but that isn't possible in this case as the API is predefined to require uint32_t's.
I realize this is probably uncommon (and probably only used a few times within a project, if at all), and I realize this also depends on using the attribute (e.g. when creating a new project), but I'm curious if there are guards that can be put in place to protect against accidental misuse - or against incorrect 'simplification' (e.g. by some maintainer later who doesn't understand the implications)... I also can't think of another scenario where enforcing 'address-only' access makes sense, so the solution may not exist...
You can declare an identifier to be for an object of incomplete type, such as a structure whose definition is not given:
extern struct NeverDefined __ICFEDIT_region_ROM_start__;
Then you can take its address, but attempting to use or assign it will yield a compiler error.
You shouldn't put c++ and c in the same question; they are different languages for different purposes.
In C, at least, declaring them as:
extern char ROMStart[], ROMEnd[]; /* shortforms are less clumbsy */
gives you much of what you want:
OS_SetROM(ROMStart, ROMEnd, ROMEnd-ROMStart); /* not sure why the last one */
although clever by half compilers might complain if you try to treat these locations as shorts or longs about alignment, so to mollify them, you likely would:
extern long ROMStart[], ROMEnd[];
intptr_t size = (intptr_t)ROMEnd - (intptr_t)ROMStart;
OS_SetROM(ROMStart, ROMEnd, size);
In C++ you would have to consult the language standard du minute.
I have a dsPIC33 and programming it using MPLAB X.
I have three files: main.c ADC.c ADC.h
In ADC.h I do a bunch of variable initialisation as follows:
typedef struct pinPair{
unsigned CH0:5,CH123:1,STAT:8,:2;
unsigned I:16;
unsigned V0:16;
unsigned V1:16;
unsigned V2:16;
}pinPair;
typedef pinPair PINPAIRS [5];
extern PINPAIRS pinPairs __attribute__((space(dma)));
then in my main.c file I realise the pinPairs variable:
//Define pinPairs data buffers
PINPAIRS pinPairs __attribute__((space(dma),aligned(256)));
int main(void) {
...
}
Now in my ADC.c file comes the confusing part, The variable pinPair is clearly aligned somewhere in the dam memory because if I do this:
DMA0STA=__builtin_dmaoffset(pinPairs);
the compiler just does its thing but if I do it in the way I was intending:
DMA0STA=__builtin_dmaoffset(pinPairs[pairCounter]);
I get the error:
error: Argument to __builtin_dmaoffset() is not the address of an object in a dma section;
But it is though !!!
Does anyone know what it is that this error means and how I could use the variable in the way I want ?
I can imagine multiple possibilities
The subentities of the array don't have the proper attributes set,
and the compiler doesn't lift pinpairs[pairCounter] (a pinpair
struct)
due to Paircounter being a variable, the compiler can't compute a compiletime address for value.
If the latter, a fixed value would work, if the former, then a compiler limitation that is pretty hard to workaround.
I'd try something like
DMA0STA=__dmaoffset(pinpairs)+dmacounter*uint16(&pinpairs[1]-&pinpairs[0])
(plus maybe some typecasts to get it assigned)
I am dealing with the following issue in C. I use global variables for defining some global parameters in my code. I would like such global variables to be constant, even though they have to be initialized inside a routine that reads their values from an input data file. In a nutshell, I am looking for a good way to "cast away" constness during variable initialization in C (I guess in C++ this would not be an issue thanks to const_cast)
I came up with a pattern based on macros to do so, as illustrated below.
It seems to work fine, but I have the following questions.
Does anyone see any hidden flaw or potential danger in the procedure below?
Would anyone discourage the following approach in favor of a simpler one?
My approach:
I have a main header file containing the definition of my global variable (int N) like so
/* main_header.h */
#ifdef global_params_reader
#define __TYPE__QUAL__
#else
#define __TYPE__QUAL__ const
#endif
__TYPE__QUAL__ int N;
I have a file "get_global_params.c" implementing the initialization of N, which sees N as "int N" (as it includes "main_header.h" after defining global_params_reader)
/* get_global_params.c */
#define global_params_reader
#include get_global_params.h
void get_global_params(char* filename){
N = ... ; // calling some function that reads the value of N from
// the datafile "filename" and returns it
}
and the corresponding header file "get_global_params.h"
/* get_global_params.h */
#include "main_header.h"
void get_global_params(char* filename);
Finally, I have a main.c, which sees N as "const int N" (as it includes "main_header.h" without defining global_params_reader):
/* main.c */
#include "main_header.h"
#include "get_global_params.h"
int main(int argc, char **argv){
// setting up input data file //
...
// initialize N //
get_global_params(datafile);
// do things with N //
...
}
I hope my explanation was clear enough.
Thanks for any feedback.
Just contain the globals in a separate file.
globl.h:
struct Globals{
int N;
//...
};
extern const struct Globals *const globals;
init_globl.h:
init_globals(/*Init Params*/);
globl.c
#include globl.h
#include init_globl.h
static struct Globals _globals;
const struct Globals *const globals = &_globals;
init_globals(/*Init Params*/){
// Initialize _globals;
//...
}
Now you can initialize the globals at startup by including init_globl.h in whatever file needs access to that functionality, everyone else can directly access the globals just by including globl.h, and using the notation globals->N.
If I were you, I would simply avoid this kind of global variables. Instead, I would define a struct with all those program parameters, and define one function that returns a const pointer to the one and only instance of this struct (singleton pattern). That way, the function that returns the pointer has non-const access to the singleton, while the entire rest of the program does not. This is precisely what you need, it's clean and object oriented, so there is no reason to mess around with macros and casts.
The instance can be declared as a static variable within the function or it can be malloc'ed to a static pointer. It does not really matter, because that is an implementation detail of that function which is never leaked to the outside. Nor does the rest of the code need to be aware of when the parameters are actually read, it just calls the function and it gets the one and only object with all valid parameters.
"I would like such global variables to be constant, even though they have to be initialized inside a routine that reads their values from an input data file."
It is not possible to initialize a const in c during run-time. In c value either has or has not a const qualifier, and it is defined upon declaration. c does not support changing it. The semantics are fixed. But some expert with quoting the standard would be nicer and more ensuring.
I don't think this is possible in c++ either, but I won't bet on it, since c++ can do some magic here and there.
I have read this exam code.
"a.c":
int global_1 = 100;
"b.c":
extern int global_1;
int global_2 = global_1 * 2;
int main()
{
return 0;
}
gcc give “error: initializer element is not constant"
I do not write this code, but I want to know why and how to fix.
Thanks.
The problem you are facing is that in order to initialise a variable (global_2 here) it's value (or all values of what it depends on, in your case global_1) need to be known.
When you initialise a variable in a function then it's value only needs to be known at runtime and that is generally the case or you will get an undefined symbol error during compile or link time.
Globals need to be initialised by the compiler during compile time.
extern int global_1;
Says to the compiler that global_1 exists, but not in this compilation unit. It will be accessible after linking. You can use it in functions, but the linker needs to link it first with an object file that actually contains it.
Thus the compiler is unable to initialise it at compile time resulting in the error you see. Linking comes after compiling.
If you need to initialise globals that reference globals in a different compilation unit then you will need to do this in a function. For example the first stem in main().
The error says it all: Initialisers need to be constants.
To fix this you might like to modify your code like this:
int global_2 = 0;
int main()
{
global_2 = global_1 * 2;
I use a 2D array of chars that should be written & read by multiple functions in C.
This is my array:
static char array[3][6];
And let's say I have a function 'Function()' that modify this array.
If the function is defined in the main there is no problem (the the array is correctly written & then read), but if I want to have my Function in an another file, the array is correctly written but when I return in the main is magically empty!
This is my code.
main.c
#include "support.h"
int main(int argc, char *argv[])
{
Function();
unsigned i, j;
for(i = 0; i < 3; i++)
{
for(j = 0; j < 6; j++)
printf("[%c]", array[i][j]);
printf("\n");
}
system("PAUSE");
return 0;
}
support.h
static char array[3][6];
support.c
void Function()
{
char hello[6];
hello[0] = 'H';
hello[1] = 'E';
hello[2] = 'L';
hello[3] = 'L';
hello[4] = 'O';
hello[5] = '\0';
strcpy(array[0], hello);
}
No compilation error nor runtime error. One again, if I try to move everything in main.c all works, if I divide in two files it doesn't (the array is correct as soon as it returns from the Function(), then it is freed), how is it possible?
By declaring array as static in a header file, you give each source file which includes support.h its own copy. You need to change the header to
extern char array[3][6];
and add
char array[3][6];
to a single source file.
The whole point of declaring a file-level entity static is to give it internal linkage, i.e. to ensure that the entity is not visible to the other translation units and that each translation unit gets its own independent copy of such entity. That applies to both functions and objects. In your case each translation unit that includes support.h gets its own independent copy of array object. This is exactly what you achieved by declaring a static array in a header file. And that is why main.c cannot see any modifications made in support.c - these two translation units work with two completely independent arrays.
Now, when I say that the array "is not visible" from other translation units, I mean that you will not be able to refer to it by name from other translation units (i.e. you will not be able to link to it). This is not necessarily a bad thing. If you still want to declare your array as static and access it from other translation units, you can still implement this access "manually": define that array as static in one translation unit and then pass that array to all other functions as a parameter.
(Note, that in most cases passing your array around through function parameters might be a much better idea than introduction of a global variable. And that will even allow you to declare your array as local object in main.)
But if you insist on a genuine global variable, you should stop using static. Declare your arrray in the header file, per #simonc answer, and define it in one of the translation units as an object with external linkage.
Well, you have, actually, two array defined in your program: one in the compile unit main and other in compile unit support. Compiler will compile main.c by one side and support.c by the other side, typically creating object files (usually .obj or .o). The linker put both together, resolving addresses.
Because you are defined the array as static:
static char array[3][6];
you are telling to the compiler the array is private to the compile unit. And because both main.c and support.c are including support.h, both are creating its own private array. Code in main only can see the array defined in main.c, and code in support.c only can see the array defined in support.c. When you call to Function(), that is modifying the array in support.c, not the array visible in main.c.
If you remove the static keyword from the array definition in support.h, you will have a linker error, since that symbol is defined twice (in main.c and in support.c). You must decide where do you want to define the array (in support.c or main.c) and reference to it from the other source file using the extern keyword.