How to pass "char string" to function

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

How to pass a char string I changed to a function?

 

This code doesn't work:

 

char AT_Send [] = "AT$SF=1122\r"; // 11x

void Send_AT_CMD(char str[], int n_chars) {
    for (int n = 0; n_chars; n++) {
        USART_0_write(str[n]);
        while (!(USART0.STATUS & USART_TXCIF_bm)); //wait for USART TX complete
        USART0.STATUS = USART_TXCIF_bm; //Clear TXCIF flag
    }
}

Send_AT_CMD(AT_Send, 11);

 

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

This is all standard C stuff.

Within a function parameter list, you can do either

void Send_AT_CMD(char str[], int n_chars) {

or

void Send_AT_CMD(char *str, int n_chars) {

They mean the same thing (the first way is just an alternative syntax that is allowed within function parameter list).

So I don't see anything wrong with how you are passing in parameters.

Your for loop, however, looks highly suspect

for (int n = 0; n_chars; n++) {
                 ^^^^^

 

 

 

 

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

MrKendo wrote:

for (int n = 0; n_chars; n++) {

  change this to:

MrKendo wrote:

for (int n = 0; n < n_chars; n++) {

Note however C strings, ie those enclosed with "", are null terminated, is your array large enough for this extra character?

Normally string functions test for the null to end, rather then count characters!

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Kevil wrote:
This code doesn't work

What do you mean, "doesn't work" ?

 

  • What did you expect it to do ?
  • What is it actually doing ?
  • What testing / investigation / debugging have you done to find the problem ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:
is your array large enough for this extra character?

Kevil wrote:

char AT_Send [] = "AT$SF=1122\r"; // 11x

So, yes - it is automatically the correct length.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Appreciate your help. I forgot to set a condition for n:

 

n < n_chars instead of n_chars

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

If your function isn't going to be modifying the string you really should declare it as const:

void Send_AT_CMD( char const str[], int n_chars );
void Send_AT_CMD( char const * str, int n_chars );

This has a variety of benefits including letting you pass string literals to the function without getting compilation errors/warnings, and letting users and the compiler know that strings passed to the function won't be modified.

 

As ki0bk pointed out, quoted strings are null-terminated and you can always null-terminate strings you create at runtime. If all strings are null-terminated, you can rewrite your function like so:

void Send_AT_CMD(char const * str ) {
    char c;
    while (( c = *str++ )) {
        USART_0_write( c );
        while (!(USART0.STATUS & USART_TXCIF_bm)); //wait for USART TX complete
        USART0.STATUS = USART_TXCIF_bm; //Clear TXCIF flag
    }
}

Send_AT_CMD( "AT$SF=1122\r" );

Depending on the AVR variant you are using you probably want to write the function so that it can also handle strings that have been placed in ROM.

github.com/apcountryman/build-avr-gcc: a script for building avr-gcc

github.com/apcountryman/toolchain-avr-gcc: a CMake toolchain for cross compiling for the Atmel AVR family of microcontrollers

github.com/apcountryman/avr-libcpp: a partial implementation of the C++ standard library (C++17 only) for use with avr-gcc/avr-libc

github.com/apcountryman/picolibrary: a C++ microcontroller driver/utility library targeted for use with resource constrained microcontrollers

github.com/apcountryman/picolibrary-microchip-megaavr: picolibrary Hardware Interface Layer (HIL) for Microchip megaAVR microcontrollers

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

Thank you. I am changing the string before calling the function e.g. writing two bytes (uint16_t measured ADC voltage converted to ASCII e.g 11.22 Volts *100 = 1122 = 0x0244.  "AT$SF=1122\r" will be "AT$SF=4402\r".

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

Kevil wrote:
I am changing the string before calling the function

That has nothing to do with making the input parameter to the function 'const' !

 

As  apcountryman  says, making the input parameter to the function 'const' tells you that the function does not modify it.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Below is part of a code I just saw above in this post. As a C++ beginner I just cannot figure out how this line is a valid code.

               while ( ( c = *str++ ) ) 

Here is my interpretation:

++ is incrementing by 1 step the address pointed by the str pointer

*str will return the value stored in address location pointed by the str+1 pointer

c = *str will transfer the content of that address into c

The result is that now the c char contain the value of the character stored in *str+1

Furthermore, why the two pair of parenthesis ((  ))  ? one pair is not sufficient ?

Also, how or when is the result become TRUE or FALSE ? cause, obviously, this is not a TRUE or FALSE test, rather, it is an assignment.

A bit of teaching would be greatly appreciated smiley

void Send_AT_CMD(char const * str ) {
    char c;
    while (( c = *str++ )) { // Can someone please explain that line for me ?
                             // How does it work ?

           Do Some Stuff
    }
}

Send_AT_CMD( "AT$SF=1122\r" );

 

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

FredCailloux wrote:

Below is part of a code I just saw above in this post. As a C++ beginner I just cannot figure out how this line is a valid code.

               while ( ( c = *str++ ) ) 

Here is my interpretation:

++ is incrementing by 1 step the address pointed by the str pointer

*str will return the value stored in address location pointed by the str+1 pointer

c = *str will transfer the content of that address into c

The result is that now the c char contain the value of the character stored in *str+1

Furthermore, why the two pair of parenthesis ((  ))  ? one pair is not sufficient ?

Also, how or when is the result become TRUE or FALSE ? cause, obviously, this is not a TRUE or FALSE test, rather, it is an assignment.

A bit of teaching would be greatly appreciated smiley

void Send_AT_CMD(char const * str ) {
    char c;
    while (( c = *str++ )) { // Can someone please explain that line for me ?
                             // How does it work ?

           Do Some Stuff
    }
}

Send_AT_CMD( "AT$SF=1122\r" );

 

 

That is the post-increment operator (foo++), not the pre-increment operator (++foo), so due to order of operations the pointer is dereferenced before it is incremented.

 

The double parenthesis are suppressing a compiler warning for a common programming error (accidentally assigning when you meant to do an equality comparison).

 

Strings are terminated by the null character (which is zero). When c contains the null character, the expression evaluates to false and iteration halts.

github.com/apcountryman/build-avr-gcc: a script for building avr-gcc

github.com/apcountryman/toolchain-avr-gcc: a CMake toolchain for cross compiling for the Atmel AVR family of microcontrollers

github.com/apcountryman/avr-libcpp: a partial implementation of the C++ standard library (C++17 only) for use with avr-gcc/avr-libc

github.com/apcountryman/picolibrary: a C++ microcontroller driver/utility library targeted for use with resource constrained microcontrollers

github.com/apcountryman/picolibrary-microchip-megaavr: picolibrary Hardware Interface Layer (HIL) for Microchip megaAVR microcontrollers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
while (( c = *str++ ))
{
    ...
}

Look from the inside out:

 

*str returns the data pointed to by str. Because str is a pointer to char, it returns a char.

c = assigns this data to c.

++ increments str after this has happened (post increment) by the size of the object to which str points.

while () tests the value of the contained expression; in this case, the value that has been assigned to c. It breaks when it sees false, a value of zero

 

So the loop iterates along a series of characters, doing something with each character in sequence, until it gets to a character with value 0 (or as a char, '\0' which is the same thing).

 

This is a common construct to deal with strings in C (which doesn't have strings, just series of characters terminating with '\0') whether outputting them, moving them, copying them, changing their case and so on.

 

While it's legal C, I'm not overly fond of it and prefer to make the implicit cast (to boolean, for the 'stop' condition) explicit:

while ('\0' == ( c = *str++ ))
{
    ...
}

though some (many?) would consider that unnecessarily verbose. However, it makes it obvious that the parenthesised expression is being considered alone, that the '\0' is the terminating character, and I suspect it keeps a MISRA checker happy (I can't recall if MISRA requires the explicit comparison).

 

Neil

 

EDIT: the '==' in the code should of course be '!='; I have left it as I originally incorrectly typed it as correcting it would invalidate comments below.

Last Edited: Fri. Jan 1, 2021 - 11:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I generally do this with a do... while so the code can just follow the natural reading order without runic compound statements.

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

apcountryman wrote:

FredCailloux wrote:

    char c;
    while (( c = *str++ )) { // Can someone please explain that line for me ?
                             // How does it work ?

           Do Some Stuff
    }

As already noted, it's stepping through the string until the NUL termination is found.

 

The use of the char variable c is superfluous; I think the more common idiom would be:

    while( *str++ ) 
    {
           Do Some Stuff
    }

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
while ('\0' == ( c = *str++ ))
{
    ...
}

>and I suspect it keeps a MISRA checker happy

 

I wonder if a MISRA checker would know you wanted to actually check for the opposite ( != ). If that is a misra necessity, then it would appear they are introducing the possibility to get the equality test wrong. Sometimes it is helpful to spell it out ( == 0, etc. ), sometimes it just seems out of place, but in each case there usually seems to be a way that 'looks right'. The == 0 is probably used more than a != 0 as the != does not add anything useful when used with 0.

 

return len == 0; //return true when len == 0 (looks better in this case to use == 0)

return ! len; //same thing, but now requires mental gymnastics

 

return len; //return true when len != 0

return len != 0; //not any clearer than above

 

while( *str ) usart_write( *str++ ); //looks ok to me- while *str is 'something', print it

while( *str != 0 ) usart_write( *str++ ); //not any better, to my eyes

 

Use whatever makes the most sense to you.

 

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

wrt MISRA, can do "too much" (imply)

 

#include <stdlib.h>

void put(const char ch);
void Send_AT_CMD(char const * str );
void Send_AT_CMD(char const * str ) {
    char c;
    while (( (c = *str++) )) { // Can someone please explain that line for me ?
                             // How does it work ?

           // Do Some Stuff
        put(c);
    }
}

int main () {
    Send_AT_CMD( "AT$SF=1122\r" );
    return EXIT_SUCCESS;
}
  • the boolean issue that Neil mentions
  • notes on effects

 

#include <stdlib.h>

void put(const char ch);
void Send_AT_CMD(char const * str );
void Send_AT_CMD(char const * str ) {
    char c;
    while ('\0' == ( c = *str++ )) {
        put(c);
    }
}

int main () {
    Send_AT_CMD( "AT$SF=1122\r" );
    return EXIT_SUCCESS;
}
  • notes on effects
  • advisory on the assignment

 


PC-lint Plus Online Demo - Gimpel Software - The Leader in Static Analysis for C and C++ with PC-lint Plus

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1
while ('\0' == ( c = *str++ ))
{
    ...
}
    while ('\0' == ( c = *str++ )) {
        put(c);
    }

 

I'm not sure if I pointed this out adequately, but since it shows up again- creating clarity by adding '\0' ==, whether by 'rule' or not, just turned the while loop into something opposite of what is wanted (unless you intend to print a string of 0's that is terminated by a non-0). A simple test turned into failure by the addition of rules designed to prevent problems. Something like this would be discovered easily (why aren't my strings printing), but if forced by rule to do this kind of thing where it doesn't help just leads to logic errors in places that may not be as obvious.  Even correcting the above to != does not lead to anything better as you have to mentally turn on your logic part of the brain to figure it out (each time you look at it).

 

while( something() ) do_this(); //obvious, and we don't care what 'something()' is, it just has to be 'something'

vs

while( nothing != something() ) do_this(); //not so obvious, will take some thought

 

 

 

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

curtvm wrote:
Something like this would be discovered easily (why aren't my strings printing), ...
During unit testing though likely to be found during code review (you thought, I didn'twink

A linter may not catch the defect; a static analyzer should (flow analysis, possible dead code)

 

edit :

#include <stdlib.h>

void put(const char ch);
void Send_AT_CMD(const char * str );
void Send_AT_CMD(const char * str ) {
    char c = *str++;
    while (c != '\0') {
        put(c);
        str++;
        c = *str;
    }
}

int main () {
    Send_AT_CMD( "AT$SF=1122\r" );
    return EXIT_SUCCESS;
}

MISRA advisories on str++

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Thu. Dec 31, 2020 - 10:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mea culpa - I typed == for != :o

 

Neil

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

smiley

 

"Dare to be naïve." - Buckminster Fuller

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

>I typed == for != :o

 

At least you have a keyboard that puts out what you typed- all my keyboards seem to remap their keys on the fly and I never know what I'm going to get (me->keyboard->[enigma machine]->pc, although I guess there is a possibility the keyboards are ok-  me->[enigma machine]->keyboard->pc).

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

The MISRA recommendation for the ordering of parameters around == is to prevent accidentally zapping a variable by mistyping '=' for '=='. By putting a constant to the left, it *can't* have a value set. It's all part of the MISRA philosophy of observing which forms and styles have caused errors in the past, and trying to avoid them.

if (apples == 12)       // ok, but MISRA doesn't like it
if (12 == apples)       // ok, but now MISRA likes it
if (apples = 12)        // legal, undetectable by the compiler, and probably not what you wanted
if (12 = apples)        // compiled error; trying to assign value to a constant

Using the same order for != is merely style consistent and not required. Other comparisons are probably best used in the most convenient semantic order:

if (j > 17)     // is semantically and syntactically the same
if (17 <= j)    // whereas this isn't

Neil

Last Edited: Fri. Jan 1, 2021 - 11:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

barnacle wrote:

if (apples = 12)        // legal, undetectable by the compiler, and probably not what you wanted

Compilers do detect it.  Warnings get issued.

No reson compilers could not have a flag that converts that warning into an error.

I expect some compilers have just such a flag.

Moderation in all things. -- ancient proverb

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

>No reson compilers could not have a flag that converts that warning into an error.

 

gcc: -Werror (a good idea in any case as you don't want warnings scrolling by unnoticed, and warnings should be dealt with to make them disappear)

 

-Wall -Werror (-Wall will also include other goodies)

if( apples = 12 ) printf("1\n"); //-Wall warning (parentheses), -Werror turns it into an error (you will have to deal with it)

 

if you just made a mistake, then you correct it-

if( apples == 12 ) printf("1\n")

 

if you really intended the = plus a bool test, a replacement that doesn't require another set of parens-

if( apples = 12, apples ) printf("1\n");

if( apples = something(), apples ) printf("1\n"); //usage that makes more sense (we don't know what apples will be)

 

also applies to while-

while( c = *str++, c ) uart_write( c );

 

 

Last Edited: Fri. Jan 1, 2021 - 06:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

>No reson compilers could not have a flag that converts that warning into an error.

 

gcc: -Werror (a good idea in any case as you don't want warnings scrolling by unnoticed, and warnings should be dealt with to make them disappear)

Emphasis added.

-Werror is a somewhat bigger hammer.

Moderation in all things. -- ancient proverb

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

>-Werror is a somewhat bigger hammer.

 

Smaller hammer (I don't think there is a way to do this globally, so this line would have to be in a global header)-

#pragma GCC diagnostic error "-Wparentheses"

 

I would rather also catch all the others included in -Wall, and make them errors so there is no playing around with warnings. That may mean you push/ignore/pop a warning/error option in some specific instances if needed (like when using anonymous structs), or simply correct the code so the compiler is happy. In any case, you had to deal with it in a way that required some thought, and over time you will tend to avoid producing these warnings/errors in the same way you tend not to skip using semicolons (you already know the compiler will not like it).

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

Kevil wrote:

Thank you. I am changing the string before calling the function e.g. writing two bytes (uint16_t measured ADC voltage converted to ASCII e.g 11.22 Volts *100 = 1122 = 0x0244.  "AT$SF=1122\r" will be "AT$SF=4402\r".

 

Why not do this:

 

const char *mask = "AT$SF=%04X\r";
char buffer [16];
int volts;

volts = (adc * 100);
sprintf (buffer, mask, volts);

// whichever of these you use
Serial.print (buffer);
printf (buffer);
fwrite (buffer, sizeof (char), strlen (buf), stdout);

 

If you need to twist the value around for big-endian, just use "%02X%02X" in the mask instead and sprintf each half of the int with "value << 8" and "value & 0xFF".

 

(edit to add): The above is assuming you have an Arduino 16 bit int. If you have a real machine, you'll need to either explicitly use unit16_t or int16_t, or mask of the extra parts of the int with "value & 0xFFFF" or "value & 0xFF" or whatever as needed.

 

Hope this helps.

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

Last Edited: Sun. Jan 10, 2021 - 04:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kevil wrote:

Appreciate your help. I forgot to set a condition for n:

 

n < n_chars instead of n_chars

 

Why then did you choose this strange misleading title for your question? Why did you make it about "passing a string to a function" when in reality the problem had absolutely nothing to do with it?

Dessine-moi un mouton

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

AndreyT wrote:

Kevil wrote:

Appreciate your help. I forgot to set a condition for n:

 

n < n_chars instead of n_chars

 

Why then did you choose this strange misleading title for your question? Why did you make it about "passing a string to a function" when in reality the problem had absolutely nothing to do with it?

 

Ha ha ha ha - ouch, sorry Kevil, this court is brutal!  That should teach you to never ask a question you don't already know the answer to...  LMFAO

Field the chicken, ignore the ball.

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

fsykes wrote:

Ha ha ha ha - ouch, sorry Kevil, this court is brutal!  That should teach you to never ask a question you don't already know the answer to...  LMFAO

 

I guess this counts as an answer to my question above, even if it is coming from a different person. So, let's see what's out lesson here:

 

When asking a question about a problem, just make any random assumption about a possible root of the issue and put it in the title! Anything goes! You can just spew any kind of nonsense and no one can blame you for that, since you don't already know the answer, right? Right?

 

What? What? Can't hear you, a guy in the back... You say that a question title should not mislead and instead should contain a brief and focused description of the problem? Makes it easier to find for other people having the same issue, you say? Maybe... An interesting idea, but nah. Nah... That is overrated. Only problem people, negative people put their problems in the question title. We are solution people, we are proactive positive people! For proactive positive people a random assumption is always better than nothing.

 

Sigh...

Dessine-moi un mouton

Last Edited: Tue. Jan 12, 2021 - 11:11 PM