Another beginner question

Hello,

This is my first post and using the Arduino uno R3 board from Elegoo.
I'm a newbie to C++ but i want to learn it a bit as i go (tutorials with some of my own modifications to understand how the code works) Because i'm a bit 'slow' and don't have the courage to learn C++ for the first few months.
Code below:
I'm trying to understand what the void functions are used for. I suppose to create clarity and convenience in the code.
I'm trying to use the extra 'void' as some kind of a subroutine. The extra void function works fine to store a int and then use this int in the main void loop to do something with it (in this case just Serial.println to check if it works).
I'm not able to do this with a piece of text that i store as a string or as a char array. It just keeps giving two empty line or a strange character what suggests that i'm 'understanding' something fundamentally wrong about the use of voids.
Although 'Serial.print' works within the limits of the void itself (both in void loop and in void sentence. you can see the extra code after //).
In the future i want a easy&fast way to adjust a piece of text; that needs to be shown on a display; in the code. Preferable with a separate piece of code (void?) that can get repeated in the main loop.
It breaks my mind that i can use the content of the memory location for the int's but not the memory locations for the char array. I'm able to use strings instead of char array's (with the same bad result) but i read that the use of strings is very memory inefficient.
Sorry for this post but i'v been searching and trying for two days now and i can't find a answer. I'm almost done with it....and ready to throw my experimenting kit in the bin.
Please correct any blunders.
THX!!

int returnvalue_one;
int returnvalue_two;
char mysentencearray;

void setup() {
  Serial.begin(9600);
  returnvalue_one = 0;
  returnvalue_two = 0;
 }

void loop() {
  randomnumberlow();
  randomnumberhigh();
  sentence();
  Serial.println(returnvalue_one);//this works
  Serial.println(returnvalue_two);//this works
  //String mysentence = "This is a sentence";
  //Serial.println(mysentence);
  //char mysentencearray[19] = "This is a sentence";//a sentence as characters in a array
  Serial.println(mysentencearray);//this works not!
  delay(500);
}

void randomnumberlow() {
  returnvalue_one = random(0,200);//random number from to
}

void randomnumberhigh() {
  returnvalue_two = random(201,400);//random number from to
}

void sentence(){
  //String mysentence = "This is a sentence";
  //Serial.println(mysentence);
  char mysentencearray[19] = "This is a sentence";//a sentence as characters in a array
  //Serial.println(mysentencearray);
}

The void preceding a function name is the "return type", and returns nothing. Compare that to int someFunction() which has int return type, and will return an int. Read about "data types" https://docs.arduino.cc/language-reference/en/variables/data-types/

These are also called "function" or "method"

This makes a local char array, invisible from outside.
You should make it global (where you define the other two...)
char mysentencearray[19];

In your void (alias for a function that returns nothing) you should use
mysentencearray = "blabla"

Maybe I don't understand but is this what you want to do


char arr[3][10] = {"NOW", "NOT NOW", "Forever"};

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  for (int i = 0; i < 3; i++)
  {
    Serial.println(arr[i]);
  }
  Serial.println();
  delay(1000);
}

//
1 Like

functions implement code that is used multiple time in a progam rather than duplicating the code

some functions return values and are defined with a return type, for example

int returnSomething() {...}

a function that doesn't return a value is defined as void

void doSomething () {...}

a program is a collection of functions

look this over

// demonstrate use of functions

// -----------------------------------------------------------------------------
// function is passed low and high integers and returns random #
int
getRand (
    int low,
    int high )
{
    return random (low, high);
}

// -----------------------------------------------------------------------------
// function is passed a string, low & high integers an print a string
void
printSentence (
    const char *s,
    int         low,
    int         high )
{
    Serial.print   (s);
    Serial.println (getRand (low, high));
}

// -----------------------------------------------------------------------------
void loop()
{
    printSentence ("low random # ", 0, 200);
    printSentence ("high random # ", 200, 400);
    delay(500);
}

// -----------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
}
2 Likes

That's not an array. That declares a single char, like 'Z'. You know the array notation; but that by itself:

char mysentencearray[];

doesn't compile:

error: storage size of 'mysentencearray' isn't known
 char mysentencearray[];
      ^~~~~~~~~~~~~~~

The compiler is perfectly capable of counting the number of characters in a string literal, with also includes the implicit terminating NUL character. Otherwise, it is not a valid C-string:

char mysentencearray[] = "abc";  // four chars

This won't compile:

char mysentencearray[] = "abc";  // four chars

void setup() {
  Serial.begin(115200);
  Serial.println(mysentencearray);
  mysentencearray = "second choice";
  mysentencearray = "3rd";
  mysentencearray[0] = '4';
  mysentencearray[1] = 't';
  mysentencearray[2] = 'h';

  Serial.println(mysentencearray);
}

void loop() {}

Two separate errors:

error: incompatible types in assignment of 'const char [14]' to 'char [4]'
   mysentencearray = "second choice";
                     ^~~~~~~~~~~~~~~
error: invalid array assignment
   mysentencearray = "3rd";
                     ^~~~~
  • "You declared a char array, which must have a specified size. And the new value is a different size."
  • "Oh never mind: you can initialize an array with its values, but you can't assign one the same way"

You can assign the elements individually. If you comment-out those two invalid statements, it works. Of course, there is a better way; but first, this also works:

char mysentencearray[] = "abc";  // four chars

void setup() {
  Serial.begin(115200);
  auto mysentence = mysentencearray;
  Serial.println(mysentence);
  mysentence = "fifth";
  Serial.println(mysentence);
  Serial.println(mysentencearray);
}

void loop() {}

What type is mysentence? Using auto to let the compiler figure it out, you might think it is the same as mysentencearray. But the point is: not quite. If you're using the IDE, you can hover to see the type. Or if you can force an error, the compiler tell you; e.g. by adding:

  mysentence = 0.5;

which fails to compile with error: cannot convert 'double' to 'char*' in assignment. The * is for multiplication, but that's not that common, generally. It also indicates a pointer to a memory location to hold that type.

An array knows its size, but is also a pointer to its content; more specifically the first element, at offset zero. So initially, it points to the 'a' in that "abc". You can also reverse it, and refer to any memory location relative to a pointer, using the same square brackets and numeric index value.

C and C++ are (in)famous for not checking whether that indexed location is "inside" the array. That is a thing you must code manually. Many functions take a separate size or length argument for this purpose. This is a major reason why C/C++ is considered "unsafe". If you read or write outside the array at a random location, that will cause a variety of unwanted effects:

  • harmless and pointless: your program "does nothing" instead of what you intended
  • immediate crash/reboot
  • some subtle error that corrupts data or causes improper behavior that may never be noticed

In the IDE, hovering over the println will popup an info box showing details of the function:

instance-method println
→ size_t
Parameters:

const char *
// In Print
public: size_t println(const char[])

The function signature says char[]: an array of unspecified size. For some reason, C allows a size in the declaration, but it has no effect. Again, referencing an array directly, you can get its size -- sizeof(mysentencearray) -- but otherwise, no one knows because no one is checking automatically.

Despite being an array, the info box says the parameter is a char*: a pointer. They're effectively equivalent, and it doesn't give the impression that there is a size in effect.

But what does the const mean? The parameter is actually a "pointer to const char" (or array of const char). That's a guarantee, enforced by the compiler, that the function will not alter any individual char that is referenced. This means that if a function does want to modify the content of an array, then the parameter cannot be const. For example:

void makeUppercase(char *s)

A string literal is actually an array of const char. One specific and useful exception is initializing a non-const array: you want to be able to modify the array content, but have it start with those characters. Otherwise, you get a warning:

warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
 char* mysentence = "abc";
                    ^~~~~

To see these, open the Preferences in the File menu, and set the Compiler warnings to Default instead of None.

You can start with something like:

const char* mysentence = "nothing yet";

void setup() {
  Serial.begin(115200);
}

void updateSentence() {
  if (millis() % 2) {
    mysentence = "odd";
  } else {
    mysentence = "even";
  }
}

void loop() {
  Serial.println(mysentence);
  delay(random(1000, 1100));
  updateSentence();
}

Your attempts look OK to me. The classic error would be to fail to provide space for a terminator character in character array but you do not appear to have made that mistake. If the following doesn't work the problem is elsewhere, say a serial baud rate mismatch.

void setup() {
  Serial.begin(9600);
  Serial.println("hello world") ;
}

void loop() { }
1 Like

First of all. Thx for the reply and the help! This is a common methode to Serial.print a array and read all about it 100 times (i'm slow but persitent). I've used this method already several times. But i want to have a methode that i can seperate blocks of code that i can access wherever in the code i want (and store results to acces later). 25 years ago i made a graduation project with a PIC microntroller (Microchip) in heart (concept of cnc drilling machine for printed boards with Ultiboard exporting to my pc software. Plexyglass box with prints, I2C bus for steppers, serial bus for pc coms, 3 diskdrive steppers , print with recuperated keyboard keys for precise manual manipulation, pc monitor as coördinate display, blablabla...lots of work and dedication). Writing code with a RISC instruction set is much more simple then C++ and the compiler is very clear about code errors (code that doesn't do what you want is a smaller 'risk' :wink:). Downside: No libraries at the time and nesting everything! But: very little memory use!
To the point; i'm going try the tip above about declaring mysentencearray[19] globally.
thx again!

Atmel is now owned by Microchip and you can program all the AVRs with MPLAB.

thx for your answer. The problem is in filling the char array (propably ok) in the separate void en accessing the array in the main void loop (not ok). This problem does not appear with the ints filled with data the other separate void functions.
I've tried the test to be sure and it works fine.

Please stop annoying the majority of readers of this topic by referring to "voids" when the correct way to refer to them is as functions

Sorry, i just replied to the wrong post.
Just tried it and i realise that i'v tried this before. I read that one needs to 'force' the array in a specific memory location with adding a number between [] otherwise the array is stored randomly and can cause memory overwriting problems. ...and it doens't work with the 'global' declaration :unamused:. Although the length (and thereby memory locations) is defined it seems to be a memory problem because the Serial.print displays something like two empty lines.
Still, thx!

int returnvalue_one;
int returnvalue_two;
char mysentencearray[19];

void setup() {
  Serial.begin(9600);
  returnvalue_one = 0;
  returnvalue_two = 0;
 }

There is so much incorrect there that I don't know where to start ....

How wrong can you be ?

The number in the square brackets tells the compiler how many elements there are in the array and hence how much memory to allocate to hold it. It has nothing at all to do with where in memory the data is actually held although it will be in contiguous locations in memory

I gave up at that point

Thanks for the code and does do what i described it had to do (a easy adaptable sentence with the sentence and Serial.print is a different function)!! Did struggle to get my mind around it.
It's a intriguing approach for me because it's done; in comparison to my code; the other way around (and a bit confusing with the loop and setup function @ the bottom; but the compiler off course doesn't mind). Also the printSentence function uses the getRand function :+1:. I couldn't come up with this. At least not in the first coming months now (maybe even year). But now with this example it's a big step forward for me.
I feel like a dumbass in comparison to you girls & guys.
thx again everybody!

Works great thx! Learning as i go. Maybe using a pointer could do the trick in my sketch.

Like i said i'm slow but this is slowly sinking in. A shame that i don't really know how the compiler in the IDE deals with it. The only conclusion i can make so far is that the microcontroller just does one calculation at a time (in order of writing) en constantly repeats the code in the void loop function. Right? But i don't know if; with te c++ coding; everything outside the void loop function is accounted for so that different functions don't interfere with each other. After all the reading i have done so far i know that C++ coding is not fail proof if you don't know the limitations and inner working of what you are programming.

kenb4 said:

Also:

i'm still struggling with the 'return type' and 'return' in the code (bleu). I can see what is does but i can't form a way of thinking where what comes from and how it can be used. i've read a few times about 'returning it to the calling function' but i don't really get it.

upside conclusion: I feel already that i'm repeatedly going use some of the same coding methods.

And it does!! So 'pointing' to the memory works better.

The C Programming Language provide a succinct explanation of lanaguage features that may help

the compiler processes lines of source, usually a function at a time. Memory is allocated and the code compiled for the addresses it occupies.

when one function calls another, the processor jumps to the address of the other function and begins executing code at that location. It passed arguements and returns values on a stack, which is RAM (memory that holds variables). The stack grows as each function is called and shrinks when it returns

return values, if not a void function are returned on the stack.

there are different types of variables. they differ in size, the # of bytes and format: ASCII characters, binary values representing integers, floats which can represent values with limited resolution over a wide range of values, as well as use defined multi-element variables.

1 Like

Still grabbing and looking at this sketch. That someone is pulling this out of the sleeve in a minute. Respect. Reverse logic because its easier to have the needed data to print out in the same function instead of having to use 'tricks' to transfer the data from one function to an other.