How to use char **arr

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello All,

 

arr is declared as an array of pointers which are pointers to strings. 

 

char **arr = {"element1","element2"};

 

How can I intitialize and access them?

 

I receive 2 warnings:

 ( AS7 GCC 5.4.0 C project)

 

Severity    Code    Description    Project    File    Line
Warning        excess elements in scalar initializer    PointerToPointer    
Severity    Code    Description    Project    File    Line
Warning        initialization from incompatible pointer type [-Wincompatible-pointer-types]    PointerToPointer    C:\Users\User2\Documents\Atmel Studio\7.0\PointerToPointer\PointerToPointer\main.c    16
 

 

friendly regards 

Ellen

This topic has a solution.

Senior electrical engineer

Last Edited: Tue. Jun 30, 2020 - 10:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have used array/struct syntax for a variable that is neither an array nor a struct.

Yet another example that arrays and pointers are not equivalent.

Your initializer would be good for an array of pointers to char.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

char *arr[] = { "element1", "element2", };

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Program memory constants are a weak area of AVR. The obvious didn't work compile: error: initializer element is not computable at load time

const __flash char *const __flash wizards[] = { "Harry", "Ron", "Hermionne" };

That's wrong - I think the initialiser element IS computable at load time.

 

The best I could do was:

const char *const __flash wizards[] = {
    "Harry",
    "Ron",
    "Hermionne"};

char getWizardInitial (uint8_t wizard)
{
    return wizards[wizard][0];
}

 

The actual strings end up in .rodata and the tables end up in .progmem. Which isn't good.
 

  26                   .LFE1:
  28                   .global    wizards
  29                       .section    .rodata.str1.1,"aMS",@progbits,1
  30                   .LC0:
  31 0000 4861 7272         .string    "Harry"
  31      7900
  32                   .LC1:
  33 0006 526F 6E00         .string    "Ron"
  34                   .LC2:
  35 000a 4865 726D         .string    "Hermionne"
  35      696F 6E6E
  35      6500
  36                       .section    .progmem.data,"a",@progbits
  39                   wizards:
  40 0000 0000              .word    .LC0
  41 0002 0000              .word    .LC1
  42 0004 0000              .word    .LC2
  43                       .text
  44                   .Letext0:

 

Last Edited: Mon. Jun 29, 2020 - 10:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

EllenR wrote:

arr is declared as an array of pointers which are pointers to strings. 

 

char **arr = {"element1","element2"};

 

How can I intitialize and access them?

 

Um... Why do you call it "an array" while there's no array in the above declaration at all? You have a `char **` pointer. Pointer is not an array.

 

The above can be salvaged by using a compound literal on the right-hand side

 

char **arr = (char *[]) { "element1", "element2" };

but there's really no reason for that, when you can simply declare

 

char *arr[] = { "element1", "element2" };

That is an array of pointers. Not your original declaration.

 

And prefer to use `const`-qualified pointers to point to string literals

 

const char *arr[] = { "element1", "element2" };

Maybe this should even be

 

const char *const arr[] = { "element1", "element2" };

(depending on your intent)

 

EllenR wrote:
I receive 2 warnings:

Warning        excess elements in scalar initializer    PointerToPointer    

Warning        initialization from incompatible pointer type [-Wincompatible-pointer-types]    PointerToPointer    C:\Users\User2\Documents\Atmel Studio\7.0\PointerToPointer\PointerToPointer\main.c    16

 

The diagnostic message is misleading. It is distorted by a non-standard language extension implemented in GCC. Your original declaration is completely nonsensical. It is non-compilable.

Dessine-moi un mouton

Last Edited: Mon. Jun 29, 2020 - 10:44 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

>and access them?

 

 

Assuming you want strings in flash, and you want to have the array of string (char) pointers also in flash, then I think your only choice is to split out the strings-

 

#include <stdint.h>
#include <avr/io.h>

 

__flash const char str0[] = "element1";
__flash const char str1[] = "element2";
__flash const char str2[] = "element3";

 

__flash const char* //store a __flash const char* in
__flash const arr[] = { str0, str1, str2 }; //a __flash const array

//remove the second __flash if you want the array of flash pointers in ram

 

void print(__flash const char* str){ //access str address via lpm
    while( *str ) PORTB = *str++;
}

//indirection so compiler cannot optimize and we can then see the flash address

//being read from the const char* array (in flash)
void printStr(uint8_t i){
    print( arr[i] );
}

 

int main(){

    //compiler will have to get char* address from flash (just to show is all correct)

    for(uint8_t i = 0; i < 3; i++) printStr( i );

 

    //compiler knows the address, so no flash access needed for the string addresses in the array

    //this will be the normal method used, and compiler most likely does not end up generating code

    //like above where it first needs to look up the arr element for the string address

    for(uint8_t i = 0; i < 3; i++) print( arr[i] );

   

    //or can just access the string you want

    print( str0 );
    print( str1 );
    print( str2 );

 

}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:

Program memory constants are a weak area of AVR. The obvious didn't work compile: error: initializer element is not computable at load time

const __flash char *const __flash wizards[] = { "Harry", "Ron", "Hermionne" };

That's wrong - I think the initialiser element IS computable at load time.

 

The best I could do was:

const char *const __flash wizards[] = {
    "Harry",
    "Ron",
    "Hermionne"};

char getWizardInitial (uint8_t wizard)
{
    return wizards[wizard][0];
}

 

The fundamental problem here is that the literal strings are not being used to initialize arrays,

hence they represent arrays of unqualified chars that ought to be treated as const.

As with most expressions representing entire arrays, its value gets converted into a pointer to element 0.

I do not have a compiler handy, but I think that constructions like (__flash char *)PSTR("Harry") will do the trick.

If not, xmacros might be in order.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 

Hello All,

many thanks for all good answers. I understand. I have to do this in 2 steps.

1. Declaring strings

2. Declaring and initializing an array of pointers to these strings.

 

This works

friendly regards

Ellen

 

 

/*
 * PointerToPointer.c
 *
 * Created: 07.03.2019 15:25:11
 * Author : User2 
 *
 */ 

#include <avr/io.h>
#include <stdio.h>

const char str1[] = {"String1"};
const char str2[] = {"String2"};	
const char str3[] = {"String3"};
const char *arr[] = {str1,str2,str3};
volatile uint8_t len;

int main(void)
{
	
	len = strlen(arr[0]);
	// access string by index 0,1,2,....
	printf(arr[0]);
	printf(arr[1]);

	return(0);
}

 

 

Senior electrical engineer

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

EllenR wrote:
I have to do this in 2 steps

Well, you only have to do it in 2 steps if you want to put everytihng into flash.

If it is all in RAM, you can do

const char *arr[] = {"String1", "String2", "String3"};

In this case the result is the same either way.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MrKendo wrote:

EllenR wrote:
I have to do this in 2 steps

Well, you only have to do it in 2 steps if you want to put everytihng into flash.

If it is all in RAM, you can do

const char *arr[] = {"String1", "String2", "String3"};

In this case the result is the same either way.

 

 

Hello MrKendo,

 

very good

Thank You

Ellen

Senior electrical engineer

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to point out that a fairly common debug technique (on PC perhaps more so than AVR) is when you have something like:

typedef enum {
    OK,
    WORRYING
    LOOKING_BAD,
    NOT_GOOD_AT_ALL,
    DISASTOROUS,
    FUBAR
} state_t;

void handle_state(state_t state) {
    // stuff
}

and then you want to debug it:

void handle_state(state_t state) {
    char * stateText[] = {
        "OK",
        "WORRYING",
        "LOOKING_BAD",
        "NOT_GOOD_AT_ALL",
        "DISASTOROUS",
        "FUBAR"
    };
    printf("handling %s\n", statText[state]);
    // stuff
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

EllenR wrote:

many thanks for all good answers. I understand. I have to do this in 2 steps.

1. Declaring strings

2. Declaring and initializing an array of pointers to these strings.

 

No, you don't. You can easily do it "in one step", as has been clearly shown in multiple answers above.

 

The matter of doing it "in 2 steps" has arisen in connection with attempts to put these strings in flash memory, which has no direct relevance to your original question.

Dessine-moi un mouton

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:

The matter of doing it "in 2 steps" has arisen in connection with attempts to put these strings in flash memory, which has no direct relevance to your original question.

 

I'm afraid that was my fault - but the original definition looked so much like a prime candidate for program memory;  I assumed that was the OP's intention.