2 returns in a function ?

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

hello,

 

i want to know, if it is possible to have 2 return values out of a function.

and how this goes of course.

 

thanks in advance

 

* correction of some spellings to help others understand what you are asking. *

Last Edited: Fri. Jun 5, 2020 - 02:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NO, you cannot.

 

There are a number of things that you  CAN do:

 

1) Pass pointers to values as function parameters, and change those values in the function.

 

2) Return a pointer to an array or struct. Use the array or struct to hold several values.

 

By the way, the word is "function".

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Thu. Jun 4, 2020 - 05:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No.

 

But you could return a struct.

 

Or have output parameters.

 

This is standard 'C' - nothing to do with Atmel Studio or AVR

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

ka7ehk wrote:
2) Return a pointer to ... struct

doesn't have to be a pointer - you can return a struct by value.

 

Two things to beware of:

 

  1. If you do return a pointer, it will have to be to something that remains valid after the function returns
    (ie, not a pointer to an automatic)
     
  2. If you do return a struct by value, remember that it will have to do a copy of the entire struct;
    this is (probably) not a problem if it's just a couple of bytes - but may be unwise for a large struct ...

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: 1

It's stretching a point;  but if you write a function uint8_t foo (void) then you are actually returning one value comprising 8-bits. In effect you can use this to return 8 boolean values. A keyboard scanner may well do such a thing, where each bit represents the state of an individual key.

 

Should you wish to return 2 8-bit integers you could aggregate then into a uint16_t and return that.

 

NB: If you write such code be sure to comment well your intentions.

 

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

Yes, some important points, here.

 

One, in particular. If you return a pointer, it cannot be a pointer to a value (or array or struct) that is local to the function. If you do, what the pointer points to will be destroyed when the function exits. Thus, that pointer has to point to something like a global that will still be there when the function is gone.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

ka7ehk wrote:

If you return a pointer, it cannot be a pointer to a value (or array or struct) that is local to the function.

Just to nit pick, it can't be a pointer to an automatic variable. Returning a pointer to a static local variable is ok.

 

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

N.Winterbottom wrote:
Should you wish to return 2 8-bit integers you could aggregate then into a uint16_t and return that.

Indeed you could.

 

Not sure that would have any advantage over returning a struct with two uint8_t members, though - especially on an AVR, where there's no worries about padding/alignment, etc ...

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

I remember some code I was once looking that was returing a struct that was supposed to contain two image buffer pointers but the author made an error in the definition and instead of two pointers to 6MB for each image it had two 6MB images in it. That was a lot of memcpy()ing!

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

ka7ehk wrote:
will be destroyed when the function exits.

If we're picking nits, it isn't actually destroyed - it's just that the memory it occupied (usually on the stack) becomes entirely "up for grabs".

 

So you might get "lucky" (sic?) and it might happen to work for a while - until, "all of a sudden", your code starts behaving weirdly and/or crashing ...

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

Say if you could return 3 different values (non struct) from a function....how were you planning to assign those to something upon the return?  

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

Use Lisp!

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

awneil wrote:

No.

 

But ...

What if OP meant two return points?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Kartman wrote:
Use Lisp!

or Python

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

theusch wrote:
What if OP meant two return points?

Good point (sic).

 

Would need OP to clarify that

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

a lot of reactions, thanks for that.

 

N.Winterbottom wrote:

Should you wish to return 2 8-bit integers you could aggregate then into a uint16_t and return that.

 

i think this is a smart idea, i only have 2 variabele's of 9 bits. but i can put them in a: int

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

trixo wrote:
i think this is a smart idea, i only have 2 variabele's of 9 bits. but i can put them in a: int
On the whole everything in a computer tends to be held in some multiple of 8 bits. So when you have 9 bits you will actually need to use 16 bits to hold each value. If you then have two of them you would need two lots of 16 bits to combine them. So you could do:

uint32_t getValues() {
    uint16_t a = 0b101101110;
    uint16_t b = 0b110110001;

    return (a | (b << 16));
}

but why would you bother to do something like this? C has "structures" so you can combine what you like all as one object:

typedef struct {
    uint16_t a;
    uint16_t b;
} combined_t;

combined_t getValues() {
    combined_t both;
    both.a = 0b101101110;
    both.b = 0b110110001;

    return both;
}

Why would you want to make it any more complex than this?

 

What's more, at the point of use the access code is easier too. This is what you would have to do to split two u16 in a u32::

int main(void) {
    uint32_t value;
    uint16_t recovered_a;
    uint16_t recovered_b;

    value = getValues(); // version that combines into a uint32_t

    recovered_a = value & 0xFFff;
    recovered_b = value >> 16;
}

that's pretty cumbersome while the struct version is:

int main(void) {
    combined_t value;
    uint16_t recovered_a;
    uint16_t recovered_b;

    value = getValues(); // version that returns struct

    recovered_a = value.a;
    recovered_b = value.b;
}

So much simpler! This is why people choose to use structs. (and later classes in C++ which are like structs on steroids).

Last Edited: Fri. Jun 5, 2020 - 08:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using a struct makes more sense - then you don't have to hack about with manually combining & separating them.

 

More maintainable.

 

Probably no overhead,

 

trixo wrote:
i only have 2 variabele's of 9 bits. but i can put them in a: int

No:  2 x 9 bits = 18 bits; an int (on AVR) is only 16 bits.

 

Which illustrates why it's better to say uint16_t than "int"

 

wink

 

EDIT

 

clawson beat me to 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...
Last Edited: Fri. Jun 5, 2020 - 08:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Why would you want to make it any more complex than this?

because i don't understand "structs" smiley

but that is a bad excuse....i'm gonna take a look on the struct, that is the way to do it.

thanks.

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

Given that structs are basically the core concept at the heart of C++ I would spend some time exploring how they are used.

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

trixo wrote:
because i don't understand "structs" 

Then, as clawson said, you really should take the time to understand them!

 

Not only are they key to C++, they really are a pretty fundamental part of the 'C' programming language.

 

https://publications.gbdirect.co.uk//c_book/chapter6/

 

There is a famous book  by Niklaus Wirth covering  fundamental topics of computer programming, called

Algorithms + Data Structures = Programs

 

https://en.wikipedia.org/wiki/Algorithms_%2B_Data_Structures_%3D_Programs

 

You are severely limiting your capabilities in any programming language if you don't have a good grasp of all the facilities it offers for structuring data!

 

 

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: 1

awneil wrote:
structuring data!
And this is kind of key to the whole thing. All the data we process tends to be in grouped "objects". For example you might have something like ADC readings and timestamps for those. When you pass these around and process them you want to keep the grouped data together. A struct is a "container" for this.

 

A more advanced example is things like WAV audio files or BMP pictures. In a WAV file you need to keep a number of details with the data like how long is it, how many bits in each sample, what is the encoding, etc, etc So when you pass round a block of "raw data" you have something like:

typedef struct wav_header {
    // RIFF Header
    char riff_header[4]; // Contains "RIFF"
    int wav_size; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
    char wave_header[4]; // Contains "WAVE"

    // Format Header
    char fmt_header[4]; // Contains "fmt " (includes trailing space)
    int fmt_chunk_size; // Should be 16 for PCM
    short audio_format; // Should be 1 for PCM. 3 for IEEE Float
    short num_channels;
    int sample_rate;
    int byte_rate; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
    short sample_alignment; // num_channels * Bytes Per Sample
    short bit_depth; // Number of bits per sample

    // Data
    char data_header[4]; // Contains "data"
    int data_bytes; // Number of bytes in data. Number of samples * num_channels * sample byte size
    // uint8_t bytes[]; // Remainder of wave file is bytes
} wav_header;

at the very start of the data to hold key information that applies. The same goes for pictures - as well as the actual pixel data you need to carry with it key details that describe the data like the width, height, bit depth and so on. So for that you have something like:

typedef struct {
   unsigned short int type;                 /* Magic identifier            */
   unsigned int size;                       /* File size in bytes          */
   unsigned short int reserved1, reserved2;
   unsigned int offset;                     /* Offset to image data, bytes */
} HEADER;

typedef struct {
   unsigned int size;               /* Header size in bytes      */
   int width,height;                /* Width and height of image */
   unsigned short int planes;       /* Number of colour planes   */
   unsigned short int bits;         /* Bits per pixel            */
   unsigned int compression;        /* Compression type          */
   unsigned int imagesize;          /* Image size in bytes       */
   int xresolution,yresolution;     /* Pixels per meter          */
   unsigned int ncolours;           /* Number of colours         */
   unsigned int importantcolours;   /* Important colours         */
} INFOHEADER;

Each BMP file has HEADER then INFOHEADER on the front before all the pixels. If you now open a BMP file and read sizeof(HEADER) bytes you can then interpret them as HEADER format. Then read sizeof(INFOHEADER) bytes. With the info from those you will then know how many pixel bytes follow and in what format. So you could read the picture and put it up on a screen.

 

I just grepped one of our code trees for the exact text "typedef struct" and it occurs 21,353 times and that's just typedef'd structs. Most of our code is C++ so those are simply types then used in C classes which, like I said previously, are like struct{} but with a lot more features and functionality (in fact in C++ struct and class are similar things with just difference in member visibility).

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

The other way you would do it using a struct, particularly when the struct gets much larger, would be to pass a pointer to the struct into the function

typedef struct
{
    int a;
    int b;
} values_t;

static void get_values(values_t *v)
{
    v->a = 99;
    v->b = 777;
}

int main(void)
{
    values_t values;
    get_values(&values);
    printf("%d  %d\n", values.a, values.b);
}

You could also do this without a struct by passing pointers to the individual values

static void get_values(int *a, int *b)
{
    *a = 99;
    *b = 777;
}

int main(void)
{
    int a, b;
    get_values(&a, &b);
    printf("%d  %d\n", a, b);
}

 

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

thanks for the reaction, i'm gonna look or this is working for me.

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

MrKendo wrote:

You could also do this without a struct by passing pointers to the individual values

static void get_values(int *a, int *b)
{
    *a = 99;
    *b = 777;
}

int main(void)
{
    int a, b;
    get_values(&a, &b);
    printf("%d  %d\n", a, b);
}

 

... which is where in C you might want to use `restrict` to facilitate generation of better code

 

static void get_values(int *restrict a, int *restrict b)
{
    *a = 99;
    *b = 777;
}

 

Dessine-moi un mouton