Pointer to Array of Strings as CONST? - c

i program a application with mikroC PRO for AVR on the Hardware mikromedia for XMEGA.
I have different languages to show on a TFT Display.
I must switch the languages at runtime.
This works the following way, as example for the languages german and english.
I have two Pointer Arrays for the languages:
char* deu_hilfe[] = {
"START = Startet die Messung",
"STOPP = Stoppt die Messung",
"SETUP = Aufruf des Setups",
};
char *eng_hilfe[] = {
"START = Start the measurement",
"STOPP = Stop the measurment",
"SETUP = Open the setup",
};
I define a pointer to a pointer:
char **sprache_hilfe;
Then i allocate the pointer to the german or english array:
sprache_hilfe = eng_hilfe;
sprache_hilfe = deu_hilfe;
And use it:
TEXT_HI_HITXT_1.Caption = sprache_hilfe[SP_HILFE_ALG_BUTTON];
This works fine, but my problem is, that the arrays are in the RAM and my RAM is full now.
So i tried to make the arrays CONST.
But when i cast my array to the pointer with:
char **sprache_hilfe = (char*)eng_hilfe;
I see no text on the display.
I think something is wrong with my cast.
Has anyone a better solution or knows what is wrong with my code?
Greetings from Germany
Patrick

To move the arrays and strings into ROM, I would declare them as follows
const char * const deu_hilfe[] = {
"START = Startet die Messung",
"STOPP = Stoppt die Messung",
"SETUP = Aufruf des Setups",
};
const char * const eng_hilfe[] = {
"START = Start the measurement",
"STOPP = Stop the measurment",
"SETUP = Open the setup",
};
These declarations specify that the strings are const, and the array of pointers is also const. You can then access the strings with code like this
const char * const *sprache_hilfe;
sprache_hilfe = eng_hilfe;
printf( "%s\n", sprache_hilfe[1] );
sprache_hilfe = deu_hilfe;
printf( "%s\n", sprache_hilfe[1] );
If that still doesn't work, then you'll have to look at the MAP output of the linker, to see where the linker decided to put the strings and the arrays. Then use the debugger to look at those memory addresses to verify that the strings and arrays were properly burned into the read-only memory.

The actual characters of the string literals end up in flash anyway. Where you can save RAM is the two arrays, which occupy 24 bytes (2*3 pointers) in case of a 32-bit controller. So insert the const qualifiers like this. GCC has moved the arrays to the .rodata section after this.
char *const deu_hilfe[] = {
"START = Startet die Messung",
"STOPP = Stoppt die Messung",
"SETUP = Aufruf des Setups",
};
char *const eng_hilfe[] = {
"START = Start the measurement",
"STOPP = Stop the measurment",
"SETUP = Open the setup",
};
Your original code should work now, without any casting. You probably get 2 warnings from the compiler. GCC complains:
warning: assignment discards ‘const’ qualifier from pointer target type
To avoid that, you can either do a cast at assignment:
sprache_hilfe = (char**)eng_hilfe;
Or declare sprache_hilfe to point to const arrays only:
char *const *sprache_hilfe;
You original assignment
sprache_hilfe = (char*)eng_hilfe;
is wrong, as eng_hilfe points to char pointers, not chars.

Related

Error in string array initializer: initializer element is not constant

I need to define some strings and an array initialized with those strings available to use by different pieces of the software. I thought in defining them in a header file like this:
//.h file
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
const char *serviceStateKindScheduledMaintenanceDown = "ScheduledMaintenance (down)";
const char *serviceStateKindScheduledMaintenanceAvailable = "ScheduledMaintenance (available)";
const char *serviceStateKindMajorIncidentInProgress = "MajorIncidentInProgress";
const char *serviceStateKindPartialService = "PartialService";
const char *serviceStateKindOverloaded = "Overloaded";
const char *serviceStateKindGoingDown = "GoingDown";
const char *serviceStateKindDown = "Down";
const char *serviceStateKind[9] = {
serviceStateKindNormal,
serviceStateKindUnmanned,
serviceStateKindScheduledMaintenanceDown,
serviceStateKindScheduledMaintenanceAvailable,
serviceStateKindMajorIncidentInProgress,
serviceStateKindPartialService,
serviceStateKindOverloaded,
serviceStateKindGoingDown,
serviceStateKindDown
};
but the compiler shows
error: initializer element is not constant
serviceStateKindNormal
what exactly is the problem here and what choices do I have to define my variables?
Everything you put in an intialized of a variable that is declared at file scope must be a constant expression or a string literal, from initialization. There is a list what is a constant expression and variable value is not among them. So:
// there is an array of char[2]{'a',\0'} somewhere in the memory
// we store the pointer value to that array inside the variable a
static const char *a = "a";
// now we want to statically initialize variable b with the same value
static const char *b = a;
will not work, because b is initialized with the pointer a value, which is not a constant expression. You need a constant expression when initializing a variable with static storage duration.
What can you do? The following:
The good way: Why do you store the pointers to the string literals "Normal"? Why not store the data itself inside the array? Happily, variable address is a constant expression, so we can use it in initialization! Note that (almost) all use cases and semantics stay the same, except for some corner usage like sizeof(serviceStateKindNormal) operator results.
const char serviceStateKindNormal[] = "Normal";
const char serviceStateKindUnmanned[] = "Unmanned";
const char *serviceStateKind[] = {
serviceStateKindNormal,
serviceStateKindUnmanned,
};
A strange way: store the pointers to the pointers to strings inside the array. As variable addresses are constant expressions, this will work. Note that makes the serviceStateKind a three star variable. You need to double dereference the array on usage. The serviceStateKind is an array of pointers of pointers to strings. Note that it is very confusing on how to use such array, so I suggest you go with a structure then.
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
const char **serviceStateKind[] = {
&serviceStateKindNormal,
&serviceStateKindUnmanned,
};
int main() {
// question which one is correct?
printf("%s\n", *serviceStateKind[1]);
printf("%s\n", (*serviceStateKind)[1]);
printf("%s\n", serviceStateKind[0][1]);
}
but as i don't think of myself as a three star programmer, I would try to make it at least to two stars:
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
struct {
// points to a string
const char **pnt;
// an array of pointers to string
} serviceStateKind[] = {
&serviceStateKindNormal,
&serviceStateKindUnmanned,
};
int main() {
// more verbose
printf("%s\n", *serviceStateKind[0].pnt);
}
The old fashioned way - macros. Don't do it. And what is bad about it, you can abuse preprocessor concatenation of string literals, to concatenate them together.
#define serviceStateKindNormal "Normal"
#define serviceStateKindUnmanned "Unmanned"
const char *serviceStateKind[] = {
serviceStateKindNormal,
serviceStateKindUnmanned,
};
int main() {
# magic
printf(serviceStateKindNormal " " serviceStateKindUnmanned "\n");
}
I thought in defining them in a header file like this:
If you are defining the variables in a header file, you want to mark them with static, so that you will not get duplicate symbol errors on linking with different .c files that use that .h file. Also it's nice to mark the variable as const as a hint to other programmers that they are somewhat not changable.
In C language constant refers to literal constants, like (3.14, 2.718, etc).
Const-qualified objects (of any type) are not constants in C language terminology.
For creating constants in C language terminology use #define directive, for example:
#define ServiceStateKindNormal "Normal"
etc.

Array Element is a Variable in Array of Strings in C

I'm trying to initialize an array of strings in C. I want to set one of the elements of the array from a variable, but I'm getting a compiler error. What's wrong with this?
char * const APP_NAME = "test_app";
char * const array_of_strings[4] = {
APP_NAME,
"-f", "/path/to/file.txt",
NULL
};
The error is error: initializer element is not constant.
The standard distinguishes const-qualified variables and compile time constants.
Evaluating a variable (APP_NAME) is not considered to be a compile time constant in the sense of the C standard. This
char const app_name[] = "test_app";
char const*const array_of_strings[4] = {
&app_name[0],
"-f", "/path/to/file.txt",
0,
};
would be allowed, since this is not evaluating app_name but only taking its address.
Also, you always should treat string literals as if they had type char const[]. Modifying them has undefined behavior, so you should protect yourself from doing so.
I was able to get it to compile with this syntax using gcc 4.6.3:
char* const APP_NAME = "test_app";
char* const array_of_strings[4] = {
APP_NAME,
"-f", "/path/to/file.txt",
NULL
};
You could also try casting to a const (at your own risk) if the compiler rejects everything else:
char* const APP_NAME = "test_app";
char* const array_of_strings[4] = {
(char* const)APP_NAME,
"-f", "/path/to/file.txt",
NULL
};

How do I set values inside a global, fixed-size array, in C (in Visual Studio)?

A part of my VS2012 Windows Phone project is in C. I've been struggling during one day trying to initialize an array to put stuff inside it.
Whenever I try to initialize it as global (outside any function), then I get a message telling me that I can't initialize it with a value that isn't a const.
const char* myArray = (const char*)malloc(256);
// Bad code: this isn't initialized with a const
If I don't initialize it with a value, then I'll have a message telling me to give it a value. So I assign a NULL value to the array.
const char* myArray = NULL;
Then I need to set a size somewhere, so I set the size within my main, or first function:
int myFirstFunctionInTheCode()
{
myArray = (char*)malloc(256);
}
Then I get something like:
';' expected before type
So I'm searching on forum and read that C in Visual Studio is C89, thus, I need to declare then to assign on two separate line, which isn't true elsewhere in my code, so I'm completely mixed-up about -real- standards. But I still get the same error when doing it on two lines.
I then decide to use some other tools from the available VS libraries to find out that in C, I can't include sstream, streambuf, etc, otherwise my whole project fails with thousands of bugs. So I could use boost to get a real stream libraries, but it's not compatible with Windows Phone because of some thread usage.
How do I set values inside a global, fixed-size array, in C (in Visual Studio)?
What I want to achieve is similar to something in C# like it:
static byte[] gentleCSharpArray = new byte[256];
private void easyShotCSharpFunction()
{
gentleCSharpArray[0] = 0x57;
gentleCSharpArray[1] = 0x54;
gentleCSharpArray[2] = 0x46;
}
I never spent so much time trying to assign a value to an array, so I guess I'm totally wrong with my global char* arrays?
PART ONE
const char* myArray = (const char*)malloc(256);
This doesn't work because global variables in C are divvied into two spaces: the data segment and the bss segment. For example:
#include <stdio.h>
#include <stdlib.h>
int* myArray; // uninitialized, represented by bss segment
const char* myArray2 = "abc"; // initialized, goes into data segment
int main ()
{
myArray = malloc(3*sizeof(int));
myArray[0] = 111;
myArray[1] = 222;
myArray[2] = 333;
int i;
for (i=0; i<3; i++)
printf("%d, %c\n", myArray[i], myArray2[i]);
return 0;
}
When this is compiled, the const char* myArray2 = "abc"; does not translate into machine instructions. Instead, an image of what "abc" looks like in memory is created and put into the data segment, along with every other initialized global variable. Later, the program loader picks up that entire data segment and sticks it in memory, before your program even starts to run.
Uninitialized variables, like myArray in the example, don't even have that much happen. Rather, it is represented in the BSS segment as the compiler says, "we're going to need n bytes of memory reserved for uninitialized variables." Later, the program loader takes note of this and reserves those n bytes, before your program even starts to run.
Thus, it doesn't make sense to try and malloc when you initialize global variables, because when the globals are created your program isn't running yet. The machine instructions for malloc may not even be in memory yet!
PART TWO
static byte[] gentleCSharpArray = new byte[256];
private void easyShotCSharpFunction()
{
gentleCSharpArray[0] = 0x57;
gentleCSharpArray[1] = 0x54;
gentleCSharpArray[2] = 0x46;
}
Okay, let's translate this bit by bit from C# to C. Are you using const in C because constant and static are (almost) synonyms in standard English? Because they're very different in programming.
The keyword const in C and C# means that the variable cannot be an L-value.
The keyword static in object-oriented languages (like C#) means that a function or variable is unchanging with respect to the object instance of its class. C has no objects and thus no analog.
The keyword static is used in plain C to mean that a variable is unchanging with respect to its invocation, or a function is unchanging with respect to where it can be seen (similar to private in C#, you can read more here).
But what are you really wanting to do there? Just reserve a huge chunk of memory for the program, right? C has no byte data type, but char is one byte in size; you can use that instead. The unsigned keyword makes it clear to program inspectors that this will not be used for a string:
// Compiled and ran with gcc -O0 -g -Wall on Ubuntu
#include <stdio.h>
#include <stdlib.h>
int* myArray;
const char* myArray2 = "abc";
unsigned char gentleCArray[256]; // <-- here's the declaration you want
static void easyShotCFunction()
{
gentleCArray[0] = 0x57;
gentleCArray[1] = 0x54;
gentleCArray[2] = 0x46;
}
int main ()
{
myArray = malloc(3*sizeof(int));
myArray[0] = 111;
myArray[1] = 222;
myArray[2] = 333;
easyShotCFunction();
int i;
for (i=0; i<3; i++)
printf("%d, %c, 0x%x\n", myArray[i], myArray2[i], gentleCArray[i]);
return 0;
}
When the program starts, gentleCArray will already be a pointer to 256 bytes of memory, most likely all zeroes. This is a consequence of the BSS segment I mentioned in part 1. Useful for doing your own memory management without malloc.
You either:
const char my_array[] = "abcdef";
or:
char *my_array;
int main(void)
{
my_array = malloc(some_size);
/* initialize elements of my_array */
}
Example 1 makes no sense because you are attempting to initialize a static variable at runtime.
Example 2 makes no sense because you are attempting to modify a const object. Essentially, you did the opposite of what could work in either situation.
What I want to achieve is similar to something in C# like it:
static byte[] gentleCSharpArray = new byte[256];
private void easyShotCSharpFunction()
{
gentleCSharpArray[0] = 0x57;
gentleCSharpArray[1] = 0x54;
gentleCSharpArray[2] = 0x46;
}
Ok, then you want;
unsigned char arr[256];
void c_version(void)
{
arr[0] = 0x57;
arr[1] = 0x54;
arr[2] = 0x46;
}

getting a single const char * in c

I'm trying to get the individual chars from the day of the week. Here is what I have.
const char *daysOfTheWeek[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
const char *tc = daysOfTheWeek[dayOfWeek];
text_layer_set_text(&dow_one, &tc[0]);
text_layer_set_text(&dow_two, &tc[1]);
text_layer_set_text(&dow_three, &tc[2]);
what is showing up is
&dow_one = ...
&dow_two = ed
&dow_three = d
What I want is
&dow_one = W
&dow_two = e
&dow_three = d
If i remove the & and just use tc[0] i get a compile error.
83:9: error: passing argument 2 of 'text_layer_set_text' makes pointer from integer without a cast [-Werror]
/Users/myke/pebble_dev/pebble-sdk-release-001/watches/iWatch/include/pebble_os.h:677:6: note: expected 'const char *' but argument is of type 'char'
This is an embeded system that does not have access to sprintf. Pure C only
With your code you are pointing to a place "in the middle of the string" (for example, &tc[1] is a pointer to "this string called tc, but starting at location [1]"), and from there you get "everything until the end (the '\0' terminator)". If you want just one character, you should copy the individual strings to a null terminated string of length 1+1:
static char dow1[2];
static char dow2[2];
static char dow3[2];
char tc[]="Wed";
dow1[0] = tc[0]; dow1[1]='\0';
dow2[0] = tc[1]; dow2[1]='\0';
dow3[0] = tc[1]; dow3[1]='\0';
This is assuming that tc contains the characters you are interested in. Now you can do
text_layer_set_text(&dow_one, dow1);
text_layer_set_text(&dow_two, dow2);
text_layer_set_text(&dow_three, dow3);
Taking advantage of the fact that dow1 is the name of the char array, and also the pointer to the first element. Per your own comment, I am declaring them as static - this means that the variable continues to exist after the function in which it was declared returns, and I imagine that's important since the value might be referenced later for display purposes.
Of course, if you really just need a character, it is simply
char c1, c2, c3;
c1 = tc[0];
c2 = tc[1];
c3 = tc[2];
It depends a bit on how you want to use it - not clear from your question what your text_layer_set_text() function does. I googled around a bit and I think the above should be OK for you - but I've not used Pebble so I can't be 100% sure.

Why can't I initialize an array of cstrings like this?

char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Why does this return the error expected expression before ‘{’ token? The reason I want to initialize my array like this is so that I can change its contents like this later:
sXSongBuffer = {"New Song", "More Music From Me"};
You can't assign to arrays in C. C allows initializing arrays with values that are compile-time constants. If you want to change the values later, or set values that are not compile-time constants, you must assign to a particular index of the array manually.
So, your assignment to sXSongBuffer is disallowed in C. Moreover, since sXSongBuffer[0] to sXSongBuffer[19] are arrays too, you can't even say: sXSongBuffer[0] = "New Song";
Depending upon what you want, this may work for you:
/* declare sXSongBuffer as an array of pointers */
char *sXSongBuffer[30] = {
"Thriller",
"Don't Stop Till You Get Enough",
"Billy Jean",
NULL /* set the rest of the elements to NULL */
};
size_t i;
/* and then later in your code */
sXSongBuffer[0] = "New Song";
sXSongBuffer[1] = "More Music From Me";
for (i=2; i < sizeof sXSongBuffer; ++i)
sXSongBuffer[i] = NULL;
But the above only works if you know all your strings at compile time. If not, you will have to decide if you want "big-enough" arrays, or if you need dynamic memory for the strings and/or the number of strings. In both cases, you will want to use an equivalent of strcpy() to copy your strings.
Edit: To respond to the comment:
You're declaring an array of 30 char pointers with the first three elements pointing to buffers the size of the strings, ie the buff pointed to by sXSongBuffer[0] won't hold any string larger than "Thriller" and if he does sXSongBuffer[0] = malloc(32); He'll get a minor memory leek. Also, he'll have to malloc memory for each of the rest of the slots in the array. He should either use 2d char arrays like in the OP + a designated init, or malloc each buffer at run time and copy in the values. He'll also need to remember to free any memory he mallocs.
sXSongBuffer in char *sXSongBuffer[30]; is an array of size 30, with each element being a char *, a pointer to char. When I do:
char *sXSongBuffer[30];
each of those 30 pointers is uninitialized. When I do:
char *sXSongBuffer[30] = { "Thriller", ... };
I set the pointers to different read-only locations. There is nothing preventing me to then "re-point" the pointers somewhere else. It is as if I had:
char *data = "Hello";
printf("%s\n", data);
data = "Hello, world";
printf("%s\n", data);
In the above snippet, I assign data to "Hello" first, and then change it to point to a longer string later. The code I had above in my answer did nothing more than reassign sXSongBuffer[i] to something else later, and since sXSongBuffer[i] is a pointer, the assignment is OK. In particular, sXSongBuffer[0] is a char *, and can point to any valid location that has a char in it.
As I said later in my answer, if the strings aren't known at compile-time, this scheme doesn't work, and one has to either use arrays with "big enough" sizes, or dynamically allocate memory that's big enough.
C does not have general-purpose array literals. The {} list syntax only works when initializing, i.e. when assigning the value in the same statement that declares the variable.
You cannot just write
char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
You must either initialize array at once (but it will containt only 3 items):
char * sXSongBuffer[]= {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Or either use stnrcpy on every item:
char sXSongBuffer[20][30];
strncpy(sXSongBuffer[0],"Thriller",29);
strncpy(sXSongBuffer[1],"Don't Stop Till You Get Enough",29);
strncpy(sXSongBuffer[2],"Billy Jean",29);
Take a look at Designated Initializers.
#include <stdio.h>
int main (void) {
char a[6][6] = { [2] = "foo", [4] = "bar" };
for (int i=0; i<6; ++i)
printf("%d == %s\n", i, a[i]);
return 0;
}
This is a c99 feature. Compile with:
gcc -W -std=c99 2dInit.c -o 2dInit
This outputs:
0 ==
1 ==
2 == foo
3 ==
4 == bar
5 ==
In your case you want to do:
char sXSongBuffer[20][30] = {
[0] = "Thriller",
[1] = "Don't Stop Till You Get Enough",
[2] = "Billy Jean"
};

Resources