Author Topic: Passing structure into function in c  (Read 6714 times)

0 Members and 1 Guest are viewing this topic.

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
Passing structure into function in c
« on: February 08, 2022, 12:05:34 pm »
We can pass the value of variable or memory location of variable into function.

What does it means pass structure into function in c language?

Is it correct if I say that I am passing value of structure variable or memory location of structure variable into function instead of saying passing structure
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5914
  • Country: es
Re: Passing structure into function in c
« Reply #1 on: February 08, 2022, 01:07:33 pm »
Usually you pass a pointer to a structure.

Ex.
Code: [Select]
typedef struct myStruct_t {
  uint32_t value0;
  uint16_t value1;
  uint8_t value2;
  char * string;
};

myStruct_t my_data;

void do_something(myStruct_t * data){
  // Read value 0
  if(data->value0 == 10){
    // Whatever
  }
}

void main (void){
  do_something(&my_data);
}
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Dadu@

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: fi
    • My home page and email address
Re: Passing structure into function in c
« Reply #2 on: February 08, 2022, 01:49:14 pm »
Is it correct if I say that I am passing value of structure variable or memory location of structure variable into function instead of saying passing structure
Because C passes variables by value, it is correct to say that you are passing the value of the structure variable.

What does it means pass structure into function in c language?
Consider the following program:
Code: [Select]
#include <stdio.h>

struct foo {
    int  x;  /* Example member */
};

void bar(struct foo arg)
{
    arg.x = 2;
}

struct foo baz(void)
{
    struct foo  arg;
    arg.x = 1;
    return arg;
}

int main(void)
{
    struct foo  example, another;

    example = baz();
    printf("First, example.x is %d.\n", example.x);

    another = example;
   
    bar(example);
    printf("Then, example.x is %d and another.x is %d.\n", example.x, another.x);

    return 0;
}
Compile and run it, and consider the output:

    First, example.x is 1.
    Then, example.x is 1 and another.x is 1.

Remember, in C we pass variables by value.  When you pass a value to a function, and that function modifies the value (like arg.x = 2; in bar()), those modifications are only visible within the function itself; they are not visible to the caller.

Because of this, it is not correct in general to say that passing a structure variable to a function is passing the memory location of structure variable to that function.  If passing a structure variable was like passing a pointer to the contents of the structure, changes made to the structure inside the function would be visible to the caller.  As shown above, they are not.

I included the baz() function to show that you can return a structure variable from a function just like any other variable.  That, and the another variable shows that structure variables can be assigned just like normal variables, too.

Exactly how the C compiler implements the passing, is up to the compiler, and is specified in the Application Binary Interface standard for the operating system and hardware architecture in question.  In fact, in Linux (SysV ABI) on AMD/Intel 64-bit (x86-64), using gcc, Intel CC, or Clang, it actually depends on the structure!  If the structure fits neatly in one or two 64-bit integer register and only contains suitable fields, it will be passed in one or two registers; otherwise, the compiler will create a temporary local variable in the caller, and pass a pointer to the structure.  (Because a temporary local variable is created in the caller, the original contents of the variable you pass will not be affected by the function modifying the fields of the structure.)

The related question is what happens if you pass an array to a function.  (Returning an array is not possible.  Even if you manage to do it, you'll be essentially returning a pointer to a local variable in the function scope, and that is a programming error.)
In C, the name of the array decays to a pointer of its first element.  Thus, when you pass an array to a function, you really are passing a pointer to its first element.  That is why any modifications done to the values in the array in the function, will be visible to the caller too.
 
The following users thanked this post: RoGeorge, Dadu@

Offline pardo-bsso

  • Regular Contributor
  • *
  • Posts: 201
  • Country: ar
Re: Passing structure into function in c
« Reply #3 on: February 08, 2022, 03:51:48 pm »

Consider the following program:
Code: [Select]

struct foo baz(void)
{
    struct foo  arg;
    arg.x = 1;
    return arg;
}


Here you are returning a struct that was allocated in the stack, this will lead to undefined behaviour and a crash if you are lucky.
 

Offline alexanderbrevig

  • Frequent Contributor
  • **
  • Posts: 700
  • Country: no
  • Musician, developer and EE hobbyist
    • alexanderbrevig.com
Re: Passing structure into function in c
« Reply #4 on: February 08, 2022, 04:02:38 pm »

Consider the following program:
Code: [Select]

struct foo baz(void)
{
    struct foo  arg;
    arg.x = 1;
    return arg;
}


Here you are returning a struct that was allocated in the stack, this will lead to undefined behaviour and a crash if you are lucky.

This is such a common misconception. Why do you think it works like this? This is safe, and has always been safe.
Returning a pointer to it however, is not.
 
The following users thanked this post: pardo-bsso

Offline Ian.M

  • Super Contributor
  • ***
  • Posts: 12865
Re: Passing structure into function in c
« Reply #5 on: February 08, 2022, 04:13:13 pm »
Exactly. The struct is returned by value (i.e. its contents), not by reference. See the ISO C99 standard
Quote from: 6.8.6.4 The return statement
3 If a return statement with an expression is executed, the value of the expression is
returned to the caller as the value of the function call expression. If the expression has a
type different from the return type of the function in which it appears, the value is
converted as if by assignment to an object having the return type of the function.
Other C/C++ standard versions have a similar clause.

The pointer may be unsafe because if the struct was allocated automatically in the function, the struct's storage would be freed on function exit so the pointer would be invalid.  If it was *NOT* allocated automatically, the struct's storage would not be freed so the pointer would remain valid.
« Last Edit: February 08, 2022, 04:16:35 pm by Ian.M »
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14490
  • Country: fr
Re: Passing structure into function in c
« Reply #6 on: February 08, 2022, 06:53:41 pm »

Consider the following program:
Code: [Select]

struct foo baz(void)
{
    struct foo  arg;
    arg.x = 1;
    return arg;
}


Here you are returning a struct that was allocated in the stack, this will lead to undefined behaviour and a crash if you are lucky.

Absolutely not. =)
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8180
  • Country: fi
Re: Passing structure into function in c
« Reply #7 on: February 08, 2022, 07:13:54 pm »
This is one of the things which is extremely simple, and works like it works in most high level languages, and like even a beginner can expect.

The only confusion is by people who think they know C, but they don't, and instead assume that C must be always weird and difficult, and assume structs follow some kind of special rule (like array name decaying to a pointer of its first element). There is no such rule. It works like passing/returning an int.

So if you want to pass anything to a function by value (i.e., a copy; the modifications are not reflected back to the caller code) and not care how it's physically implemented, use struct.

And, if you want to return anything from a function by value (again, a copy), and not care how compiler actually does that, again use struct.

You can even put an array inside a struct and return that. Tadah, can return an array by value in C without having to think about memory allocations! People who complain about not being able to return multiple values or arrays in C, just don't know it's possible, maybe due to the small syntactic barrier (the struct).

Just be aware of the possible performance penalty! Compiler may have no other option than to copy large areas of memory (up to the size of the struct) to satisfy this simple and logical high-level behavior. But C programmers usually want low-level control themselves, which is why you most often see pointers to struct being passed or returned.
« Last Edit: February 08, 2022, 07:18:59 pm by Siwastaja »
 
The following users thanked this post: Dadu@

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3727
  • Country: us
Re: Passing structure into function in c
« Reply #8 on: February 08, 2022, 07:29:12 pm »

Consider the following program:
Code: [Select]

struct foo baz(void)
{
    struct foo  arg;
    arg.x = 1;
    return arg;
}


Here you are returning a struct that was allocated in the stack, this will lead to undefined behaviour and a crash if you are lucky.

Nope.  This is defined and correct behavior as others have explained.

Of course the C language only specifies that this works, it doesn't say how it is implemented.  Different ABIs do it differently but in general it works similar to how passing structures into functions work.  For simple and small structs the value may be stored in a register.  For larger structures the caller typically allocates the variable on the stack and passes a hidden pointer to the callee.  When the callee returns it copies the data into the caller frame using the provided pointer.  Because of this behavior both passing and returning structures from functions sometimes create unnecessary memcpy calls.  In many cases these copies can be optimized by a sufficiently advanced compiler with the right options, but the perceived (and sometimes real) inefficiency still leads many C programmers to assume it is inefficient and to always pass structures by pointer even when they don't need to support modification.  It's always worth remembering that pass by value in some situations might actually be more efficient and less error prone.

 
The following users thanked this post: Dadu@

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: fi
    • My home page and email address
Re: Passing structure into function in c
« Reply #9 on: February 08, 2022, 07:47:58 pm »

Consider the following program:
Code: [Select]

struct foo baz(void)
{
    struct foo  arg;
    arg.x = 1;
    return arg;
}

Here you are returning a struct that was allocated in the stack, this will lead to undefined behaviour and a crash if you are lucky.
As explained above by alexanderbrevig and Ian.M and others, you are mistaken.  It is perfectly normal, defined, acceptable ISO C code.  However, I understand why one would think it leads to undefined behaviour.

I created a small example at Compiler Explorer, for anyone interested in what the compiler actually generates (defaults to SysV ABI on x86-64, i.e. Linux on Intel/AMD 64-bit).  You'll see that on this ABI, when optimizations are enabled, functions that work on structures with two int members do not access stack at all.

If we expand the structure to include four int members, the ABI rules change: see here.  Comparing the ivec2d_add() and ivec2d_add_alt() functions, where the latter takes the temporary structure returned as an additional parameter, we find that the compiler generates exactly the same code.  In other words, it is the caller that allocates the temporary variable to store the structure at, not the callee (function).  This means that by returning a structure, we do not return an instance in the callee scope; the compiler makes sure it exists in the caller scope.

In the C99 standard (it just happens to be the easiest to refer to), section 6.7.5.3p1 limits function return values; it cannot be a function type (although a function pointer is perfectly acceptable) nor an array.  There is no mention of structures.  Similarly, in the chapters where structures or type promotions or type conversions are described (like in 6.9.1p10, which describes that when used as function parameters, array and function expressions are converted to pointers), there is no mention of structures being converted to pointers at all.  Indeed, even in sections that discuss lvalues (expressions that can be used on the left side of assignment operations), structures are treated just like scalar variable types (unless they contain const members or variably-modified members or a final flexible array member).

Hopefully, you can either accept my word and experimental results as proof that returning a structure from a function is not equivalent to returning something that only exists in the callee scope (like say a pointer to a local variable, allocated on stack), or explore the C99 or later standards for yourself to see that there is nothing in the standards that would allow a C compiler to behave that way.

The reason many make the same mistake as pardo-bsso did, is that when using arrays, the similar pattern would indeed yield undefined behaviour.  For example, the following is indeed undefined behaviour:
Code: [Select]
char *bad(void)
{
    char buffer[24] = "Hello";
    return buffer;
}
However, the following is completely acceptable:
Code: [Select]
struct buffer {
    char  data[24];
};

struct buffer foo(void)
{
    struct buffer  b = { .data = "Hello" };
    return b;
}

struct buffer bar(void)
{
    return (struct buffer){ .data = "Hi from me too" };
}
Although the struct variable is instantiated/created in the function, it is not converted to a pointer like an array would be.  Thus, we are not returning an address or even referring to any address; we are returning the structure by value.  It is up to the compiler (and as I mentioned before, the details are agreed in whatever ABI is being used) to handle it correctly, but it has to handle it correctly to comply with the C standard –– remember, the C standard does not allow the compiler to treat a structure that contains an array like an array; a structure is a structure and is passed by value, not by reference, even if that structure contains one or more arrays.
 
The following users thanked this post: alexanderbrevig, Dadu@

Offline alexanderbrevig

  • Frequent Contributor
  • **
  • Posts: 700
  • Country: no
  • Musician, developer and EE hobbyist
    • alexanderbrevig.com
Re: Passing structure into function in c
« Reply #10 on: February 08, 2022, 08:44:46 pm »
[...] However, I understand why one would think it leads to undefined behaviour. [...]

https://godbolt.org/z/Enosjdfcv

Here's another easy pitfall, this is obviously a poor example. But especially as a beginner, this is not that hard to do. Better exemplified by your classical buffer, but things like these will lead people to think that structs are special (when it is in fact just pointers and their lifetime not being enforced on us).

Do not do this:
Code: [Select]
struct ivec2d*  ivec2d_bad(struct ivec2d v1, struct ivec2d v2) {
    return &(struct ivec2d){ .x = v1.x - v2.x, .y = v1.y - v2.y };
}

Now, try to express something as stupid with Rust and only get a warning!  :box:
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14490
  • Country: fr
Re: Passing structure into function in c
« Reply #11 on: February 08, 2022, 09:01:34 pm »
Fundamentally, that'd be a misunderstanding of what pointers are and what values are.

To make things just a tiny bit more confusing for beginners , though, C 'arrays' are peculiar beasts, and are partly an exception to this.
(But as we talk about on a regular basis - just wrap arrays in structs, and now they act as any other value. =) )
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: fi
    • My home page and email address
Re: Passing structure into function in c
« Reply #12 on: February 08, 2022, 11:14:13 pm »
Do not do this:
Code: [Select]
struct ivec2d*  ivec2d_bad(struct ivec2d v1, struct ivec2d v2) {
    return &(struct ivec2d){ .x = v1.x - v2.x, .y = v1.y - v2.y };
}
Indeed.

The & (address-of operator), and the pointer * in the function return type, should be a dead giveaway to every C programmer, to examine exactly what it is that we're returning a reference (pointer) to.  With warnings enabled, which is something at least I myself always do, the compiler will fortunately complain about those if one happens to miss it.  In the above example at Compiler Explorer,
Code: [Select]
<source>: In function 'ivec2d_bad':
<source>:16:12: warning: function returns address of local variable [-Wreturn-local-addr]
   16 |     return &(struct ivec2d){ .x = v1.x - v2.x, .y = v1.y - v2.y };
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fundamentally, that'd be a misunderstanding of what pointers are and what values are.
Yes.  I try to 'clarify' the nature of pointers by describing them as references to something.

I have found that the address analogy does not work that well.  It often leads to bugs like
    ptr += sizeof (the type that ptr points to); /* Move to next one */
since the "pointers are addresses" idea leads to "I need to add the size of the data to advance the address to the beginning of the next element"; whereas the correct expression is
    ptr++;  /* Move to next one */
because pointers are a reference to an object of some type, and adding or subtracting from it changes the reference in units of the target type.
(This also naturally leads to void and how void pointers are untyped, and what that means in practice.  And also to arrays, and the quirks of array index notation and pointer arithmetic in C.)

Casting a pointer to another type is then a way to refer to the same storage, but reinterpreting that storage as some other type.  That leads nicely to unions and explaining type punning, with a future reference to strict aliasing.

Especially nowadays when the actual address space we use is full of holes and other strangeness, it is better to avoid the "pointers are memory addresses" analogy/view, simply because it leads to many incorrect intuitive assumptions; like assuming that a program should be able to read from random memory addresses.
 
The following users thanked this post: Dadu@

Offline hans

  • Super Contributor
  • ***
  • Posts: 1641
  • Country: nl
Re: Passing structure into function in c
« Reply #13 on: February 09, 2022, 09:56:48 am »
Another problem with pointer magic is that it can make assumptions on the underlying memory architecture.

For example: you have a linear serial buffer that receives some kind of data structure. There is a sync routine that matches the head of the packet, and then passes the payload data to a handler function. To avoid memory copies, it's easy to just type-cast the pointer of the serial buffer (at a variable position) to the desired data structure.

Assume this C code was written/debugged for an ARM Cortex-m3 MCU and it worked fine. Next what happens, this MCU is out of stock and since the project software requirements weren't that heavy a Cortex-m0 is substituted as a replacement. The BSP functions are ported, it receives serial data.. and then the code crashes when it tries to access the type-cast data structure because the m0 doesn't require unaligned memory access.
Or to draw an even worse picture if RISC-V takes off in the MCU industry: a rv32i processor does not require unaligned memory access support neither. But it leaves room to do it, so some may. It depends on the implementation if it is handled in invisibly in hardware (where it may cause slowdowns), handled with a software trap, or as a fatal exception. Although I support open-source designs going down through the ISA, I don't particularly like the "it may do this" (or it may not) aspect being part of the core spec. I sincerely hope that most COTS implementations settle on a sane common ground..

Anyways, 10 years ago I stepped into this "memory address magic" trap when I was still learning C on embedded processors (started on ARM7TDMI, which didn't support unaligned access neither). Now I'm a lot more careful.
« Last Edit: February 09, 2022, 11:51:10 am by hans »
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: fi
    • My home page and email address
Re: Passing structure into function in c
« Reply #14 on: February 09, 2022, 11:26:48 am »
Good point; this is exactly why I myself use unaligned accessor functions when using GCC:
Code: [Select]
#include <stdint.h>
#define  ACCESSOR_FUNCTION  __attribute__ ((__always_inline__, __unused__)) static inline

ACCESSOR_FUNCTION uint32_t get_32le(const void *src) {
    const unsigned char *const s = src;
    return ((uint32_t)s[0])
         | ((uint32_t)s[1] << 8)
         | ((uint32_t)s[2] << 16)
         | ((uint32_t)s[3] << 24);
}

ACCESSOR_FUNCTION uint32_t get_32be(const void *src) {
    const unsigned char *const s = src;
    return ((uint32_t)s[0] << 24)
         | ((uint32_t)s[1] << 16)
         | ((uint32_t)s[2] << 8)
         | ((uint32_t)s[3]);
}
Note that the ACCESSOR_FUNCTION macro is there to tell the compiler that these really should be inlined, and it is okay if they are not used (and therefore do not actually generate any machine code in the resulting binary); but for exploration, one can simply define it to empty.

On x86-64 using SysV ABI, these compile to a single mov instruction (with the byte order swapped using a bswap instruction in the latter); thus, there is no overhead at all; see this at Compiler Explorer.

On ARMv7-a using Thumb instruction set, these compile to a single unaligned ldr instruction (with the byte order swapped using rev instruction in the latter); again, no overhead at all.  See this.

On ARMv8-a, even Clang compiles these to a single instruction with byte order swapping using another instruction if necessary, with no overhead; see this.

Clang does have issues with this pattern on ARMv7-a, and Intel CC even on x86-64, so it is not a perfect solution.  But, if one starts chasing a perfect solution, one ends up comparing unaligned memmove() speed to byte-wise loads, which end up both being dependent on cache, instruction pipelining, and so on.  Me, I prefer the "robust and reliable results that are nowhere horrible" approach the above accessors and their like yield –– except when I don't: for example, when I have an array or structure I want random access to, and am likely to access each member more than once, so copying the data to aligned storage ends up being more efficient overall.

If I end up having a toolchain that on my current architecture does not compile acceptable code for the above accessors and I know how to do it better than the compiler, I can always (with GCC, Clang, and Intel CC at least) use preprocessor directives to replace the above C implementation with extended asm ones for that particular architecture and compiler.  (This differs from external assembly function implementation in that if properly written using machine constraints, the compiler can still inline the calls and pick which registers etc. it will use for the instructions, even though the function body is extended assembly instead of C.  But extended asm is a Mage level tool that is prone to shoot oneself in the foot, if one is not careful and know exactly what they're doing.)
 
The following users thanked this post: Dadu@

Offline hans

  • Super Contributor
  • ***
  • Posts: 1641
  • Country: nl
Re: Passing structure into function in c
« Reply #15 on: February 09, 2022, 12:17:34 pm »
I like it :)
Also interesting to see how ARM GCC 4, 5 and 6 versions handle this kind of code. The big endian seems to be a bit harder to optimize, but at some point the compiler figured out it has the rev instruction available.

My point on RISC-V still stands. I've been googling around on this topic, and only a few months ago an optimization in the Linux kernel has been added to optimize unaligned memory access. However, that patch is done on a compile definition switch basis for the kernel, e.g. routines like get_xxle/get_xxbe could be replaced by naive loads if you're certain the underlying platform supports unaligned operations.
I'm not sure what GCC is doing in terms of compiler switches for this. The aforementioned optimization seems a bit of a hack to fix the non portability...

Anyway, I digress. The topic started about passing structures by value/pointer in C, and it's interesting how something seemingly simple can invoke so much trouble. :-)
 
The following users thanked this post: Dadu@

Offline pardo-bsso

  • Regular Contributor
  • *
  • Posts: 201
  • Country: ar
Re: Passing structure into function in c
« Reply #16 on: February 09, 2022, 12:42:48 pm »

Here you are returning a struct that was allocated in the stack, this will lead to undefined behaviour and a crash if you are lucky.

This is such a common misconception. Why do you think it works like this? This is safe, and has always been safe.
Returning a pointer to it however, is not.

My bad, I stand corrected. (was bitten by things like that when I was more novice than now... )
 
The following users thanked this post: alexanderbrevig

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
Re: Passing structure into function in c
« Reply #17 on: February 09, 2022, 03:44:00 pm »
I have written following code on PC, complied and ran on PC It compile without error

how to figure out what happen in code  by printing value of both ponter's in code ?
Code: [Select]
#include<stdio.h>
#include<stdlib.h>

struct buffer
{
struct buffer *N;
};

struct buffer *foo( struct buffer  *Ptr )
{
   struct buffer *qtr = malloc (sizeof(*qtr));
   
    if (qtr == NULL)
    {
        printf("Memory not allocated.\n");
        return 0;
    }
   
   qtr-> N = Ptr;
   
   return qtr;
}
   
int main()
{
   
    struct buffer  *ptr = malloc (sizeof(*ptr));


    if ( ptr == NULL )
    {
        printf("Memory not allocated.\n");
        return 1;
    }     
   
    ptr = foo ( ptr );   
   
    return 0;   
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: fi
    • My home page and email address
Re: Passing structure into function in c
« Reply #18 on: February 09, 2022, 04:11:40 pm »
how to figure out what happen in code  by printing value of both ponter's in code ?
You can use e.g.
    printf("qtr = %p\n", (void *)qtr);
to print the address where pointer qtr points to.

The (void *) part is a cast, and converts whatever pointer expression is on its right side (above, qtr) to an untyped pointer.  The %p format specifier can only print the address of untyped pointers, you see.

Note that I do recommend aborting the program execution using exit(EXIT_FAILURE); if malloc() returns NULL, or perhaps using ASSERT(qtr != NULL); .  That is, your foo() would be better written as
Code: [Select]
struct buffer *foo(struct buffer *ptr)
{
    struct buffer *qtr = malloc(sizeof *ptr);
    if (!qtr) {
        fprintf(stderr, "Out of memory.\n");
        exit(EXIT_FAILURE);
    }
    printf("qtr = %p, ptr = %p\n", (void *)qtr, (void *)ptr);
    qtr->N = ptr;
    return qtr;
}
[code]
or (adding [tt]#include <assert.h>[/tt] near the top of your program),
[code]
struct buffer *foo(struct buffer *ptr)
{
    struct buffer *qtr = malloc(sizeof *ptr);
    printf("qtr = %p, ptr = %p\n", (void *)qtr, (void *)ptr);
    assert(qtr != NULL);
    qtr->N = ptr;
    return qtr;
}
assert() is a preprocessor macro, that causes the program to stop if the argument expression is false.
Basically, assert(qtr != NULL); says that "make sure qtr != NULL at this point; if not, abort the program."

However, when compiled with -DNDEBUG (or if there has been a #define NDEBUG), all assert() checks are completely skipped.

When you go on to build trees and lists, I like to output their definitions using the Graphviz DOT language (it's plain text), so that I can use Graphviz tools, usually dot, to print the pointer structures as a nice, visual graph.
 
The following users thanked this post: Dadu@

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
Re: Passing structure into function in c
« Reply #19 on: February 10, 2022, 04:42:16 am »
how to figure out what happen in code  by printing value of both ponter's in code ?
You can use e.g.
    printf("qtr = %p\n", (void *)qtr);
to print the address where pointer qtr points to.

why does qtr hold different memory location after each call

Code: [Select]
#include<stdio.h>
#include<stdlib.h>

struct buffer
{
struct buffer *N;
};

struct buffer *foo( struct buffer  *Ptr )
{
   struct buffer *qtr = malloc (sizeof(*qtr));
   
    if (qtr != NULL)
    {
        qtr-> N = Ptr;
   
        printf("qtr hold memory location : %p  \n",(void*) qtr);
    }
   
   return qtr;
}
   
int main()
{
   
    struct buffer  *ptr = malloc (sizeof(*ptr));
   
    printf("ptr hold memory location : %p  \n", (void*) ptr);

    if ( ptr != NULL )
    {
        ptr = foo ( ptr );

        printf("after calling,  ptr hold memory location : %p  \n", (void*) ptr);   

        ptr = foo ( ptr );

        printf("after calling,  ptr hold memory location : %p  \n", (void*) ptr);

ptr = foo ( ptr );

        printf("after calling,  ptr hold memory location : %p  \n", (void*) ptr);
    }     
   
 
   
    return 0;   
}

ptr hold memory location : 00641230
qtr hold memory location : 00641260
after calling,  ptr hold memory location : 00641260
qtr hold memory location : 00641270
after calling,  ptr hold memory location : 00641270
qtr hold memory location : 00641280
after calling,  ptr hold memory location : 00641280
 

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 4040
  • Country: nz
Re: Passing structure into function in c
« Reply #20 on: February 10, 2022, 06:21:53 am »
Or to draw an even worse picture if RISC-V takes off in the MCU industry: a rv32i processor does not require unaligned memory access support neither. But it leaves room to do it, so some may. It depends on the implementation if it is handled in invisibly in hardware (where it may cause slowdowns), handled with a software trap, or as a fatal exception. Although I support open-source designs going down through the ISA, I don't particularly like the "it may do this" (or it may not) aspect being part of the core spec. I sincerely hope that most COTS implementations settle on a sane common ground..

It's important to distinguish between the instruction set and the overall execution environment.

RISC-V systems that run packaged binary software (.e.g Linux) require that misaligned accesses work. They don't require them to work quickly.

If you have access to the source code then I would suggest that you regard misaligned accesses as a bug. There are so many systems out there that don't support misaligned accesses that portable software got fixed decades ago.

If you aren't sure of the alignment of something then the easiest thing is to memcpy() it to a known-aligned variable. It's going to perform the most efficient series of loads and shifts for you (which hardware misaligned access has to do for you anyway). For a single variable that's less efficient than hardware misaligned support, but for an array it's probably more efficient.
 
The following users thanked this post: hans

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: fi
    • My home page and email address
Re: Passing structure into function in c
« Reply #21 on: February 10, 2022, 10:02:21 am »
If you have access to the source code then I would suggest that you regard misaligned accesses as a bug. There are so many systems out there that don't support misaligned accesses that portable software got fixed decades ago.

If you aren't sure of the alignment of something then the easiest thing is to memcpy() it to a known-aligned variable. It's going to perform the most efficient series of loads and shifts for you (which hardware misaligned access has to do for you anyway). For a single variable that's less efficient than hardware misaligned support, but for an array it's probably more efficient.
Well put.

The accessor functions (like the 32-bit one I showed above) are most useful when you receive structured datagrams with fixed-size unaligned fields.  (It does not matter if it comes from the network, over UART, SPI, or if you load it from a file.)

I use them to copy the unaligned data from the datagram that I treat simply as sequences of bytes, to a local/internal structure that contains the same information, but in a much more accessible form.  For example, if a 32-bit field contains say three different members, I separate them into separate members/fields in the internal structure.  (If the size of the structure is not a concern, then the int_fastN_t and uint_fastN_t types are useful to ensure the members in the internal structure are fast to access, but sufficiently large (N bits or more).

This way the data is easy to access inside the program, the internal representation is not dependent on the interchange format quirks (like byte order, AKA endianness), but we don't make any assumptions about unaligned accesses being okay.  Since they so often are not.

On the other hand, if you receive an array of 32-bit fields, then it is better to just move the array in memory if it is not aligned.  After all, you only need few extra bytes – one less than the alignment needed – in the buffer, to do the memmove() in-place.
 

Offline hans

  • Super Contributor
  • ***
  • Posts: 1641
  • Country: nl
Re: Passing structure into function in c
« Reply #22 on: February 10, 2022, 10:32:58 am »
how to figure out what happen in code  by printing value of both ponter's in code ?
You can use e.g.
    printf("qtr = %p\n", (void *)qtr);
to print the address where pointer qtr points to.

why does qtr hold different memory location after each call

Code: [Select]
#include<stdio.h>
#include<stdlib.h>

struct buffer
{
struct buffer *N;
};

struct buffer *foo( struct buffer  *Ptr )
{
   struct buffer *qtr = malloc (sizeof(*qtr));
   
    if (qtr != NULL)
    {
        qtr-> N = Ptr;
   
        printf("qtr hold memory location : %p  \n",(void*) qtr);
    }
   
   return qtr;
}
   
int main()
{
   
    struct buffer  *ptr = malloc (sizeof(*ptr));
   
    printf("ptr hold memory location : %p  \n", (void*) ptr);

    if ( ptr != NULL )
    {
        ptr = foo ( ptr );

        printf("after calling,  ptr hold memory location : %p  \n", (void*) ptr);   

        ptr = foo ( ptr );

        printf("after calling,  ptr hold memory location : %p  \n", (void*) ptr);

ptr = foo ( ptr );

        printf("after calling,  ptr hold memory location : %p  \n", (void*) ptr);
    }     
   
 
   
    return 0;   
}

ptr hold memory location : 00641230
qtr hold memory location : 00641260
after calling,  ptr hold memory location : 00641260
qtr hold memory location : 00641270
after calling,  ptr hold memory location : 00641270
qtr hold memory location : 00641280
after calling,  ptr hold memory location : 00641280

The code is repeatedly allocating new memory blocks (for the struct buffer), but not freeing them, so that's why the memory location is increasing on each allocation.
Next, the function foo is basically like packing the incoming box into a larger box. The pointer to the struct that's passed is in (the incoming box), is put into a new struct buffer and then returned (the larger box). You can see that the "qtr hold" and "after calling, ptr hold" memory address, both match to the same address.
If you would print like:
Code: [Select]
printf("after calling,  ptr hold memory location : %p  \n", (void*) ptr->N);  You would find the "previous" box that's held by the larger box.

Although we can reason about this code on this level, I would advise against putting these kinds of structures into practice :-) Object lifetime is quite hard to deal with in C/C++, so recursive pointers is quickly becoming unmanageable where it's easy to throw away the top allocation, but not all the allocations that were inside it.

For example, if you would put at the end your main function free(ptr), then that wouldn't free all memory, but only the latest struct buffer that was allocated.
In C++ you do have destructors for objects, where you could recursively free objects that 'reside' in that object, or making use of smart ptrs so that object access/lifetime is managed, but that can still become confusing and lead to memory leaks.
But I understand that this code is written for your understanding/practice, so by all means experiment :-+
« Last Edit: February 10, 2022, 10:35:15 am by hans »
 
The following users thanked this post: Dadu@

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6266
  • Country: fi
    • My home page and email address
Re: Passing structure into function in c
« Reply #23 on: February 10, 2022, 10:56:55 am »
why does qtr hold different memory location after each call
Prepend each function with a comment describing what the purpose of the function is, and you'll find out.
Such comments are, in the long term, as useful as the code itself, because while the code tells us what it does, nothing except comments tell us what the developer intended the code to do.

For example:
Code: [Select]
/* This function creates a new buffer structure.
   The function takes one parameter, a pointer to a buffer structure.
   The parameter is set as the N member of the new buffer structure.
   The function returns a pointer to the new buffer structure.
*/
struct buffer *foo( struct buffer  *Ptr )
{
   struct buffer *qtr = malloc (sizeof(*qtr));
   
    if (qtr != NULL)
    {
        qtr-> N = Ptr;
   
        printf("qtr hold memory location : %p  \n",(void*) qtr);
    }
   
   return qtr;
}

Can you understand now?



To throw you directly into the deep end, consider the following singly-linked list example:
Code: [Select]
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

typedef  struct list  list;
struct list {
    struct list  *next;
    char          data[];  /* Flexible Array Member (C99 or later). Only one allowed per struct, and must be last. */
};

/* Prepend new data to the beginning of the list.
   The first parameter is a pointer to the variable that points to the first element in the list,
   so that we can modify the value of that variable.
   The second parameter is the string to store in the new list element.
*/
void list_prepend(list **rootptr, const char *data)
{
    /* rootptr must be a valid pointer to a variable of the list type. */
    if (rootptr == NULL) {
        fprintf(stderr, "list_prepend(): NULL pointer to list pointer variable!\n");
        exit(EXIT_FAILURE);
    }

    /* We also need a valid string; an empty string is okay, but a NULL is not. */
    if (data == NULL) {
        fprintf(stderr, "list_prepend(): NULL data pointer, not a string!\n");
        exit(EXIT_FAILURE);
    }

    const size_t datalen = strlen(data);  /* Number of chars in data */

    /* Allocate the list element, with sufficent room for the data (as a 'flexible array member'),
       plus the end-of-string character '\0'. */
    list *element = malloc (sizeof (list) + datalen + 1);

    if (!element) {
        fprintf(stderr, "list_prepend(): Out of memory.\n");
        exit(EXIT_FAILURE);
    }

    /* If the string is not empty, copy it, */
    if (datalen > 0)
        memcpy(element->data, data, datalen);
    /* but make sure the data is always properly terminated with an end-of-string character. */
    element->data[datalen] = '\0';

    /* Prepend to the given list. */
    element->next = *rootptr;
    *rootptr = element;
}

/* Free an entire list, starting at the specified list element.
*/
void list_free(list *element)
{
    while (element) {
        /* Before we destroy this element, we need to grab the pointer to the next element first. */

        list *current = element;  /* This we will destroy, */
        element = element->next;  /* and next loop iteration will worry about the next element. */

        /* This is called "poisoning".  Its purpose is to make it easier to detect use-after-free() bugs,
           by making the members "invalid" or uncommon, easily detectable values ("poison"). */
        current->next = NULL;
        current->data[0] = '\0';  /* Note: This assumes we always reserve room for data! */

        /* Now that we've made the list element useless or "poisoned", we free it. */
        free(current);
    }
}

/* Write a Graphviz DOT language description of the list to the specified stream.
*/
static void list_dot_recurse(FILE *out, list *ptr);

void list_dot(FILE *out, list *root, const char *initial)
{
    /* If out is not a valid stream, do nothing, just return immediately. */
    if (!out)
        return;

    /* If we have no list nor an initial node, do nothing. */
    if (!root && !initial)
        return;

    fprintf(out, "digraph {\n");
    fprintf(out, "    rankdir = LR;\n");  /* Prefer left-to-right. */

    /* If we have an initial node, we draw it as a rectangle. */
    if (initial)
        fprintf(out, "    initial [ shape=\"rect\", label=\"%s\" ];\n", initial);

    /* If we have a list, recursively output the description of the list itself. */
    if (root)
        list_dot_recurse(out, root);

    /* If we have an initial node and a list, add an edge (arrow) from initial to the list. */
    if (initial && root)
        fprintf(out, "    initial -> \"%p\";\n", (void *)root);

    fprintf(out, "}\n");
}

static void list_dot_recurse(FILE *out, list *ptr)
{
    /* Draw the data node with an oval, and the data value inside it. */
    fprintf(out, "    \"%p\" [ shape=\"oval\", label=\"%s\" ];\n", (void *)ptr, ptr->data);

    /* If this is not the final node, recursively describe the following nodes. */
    if (ptr->next) {
        list_dot_recurse(out, ptr->next);

        /* And draw the edge from the current node to the next node. */
        fprintf(out, "    \"%p\" -> \"%p\";\n", (void *)ptr, (void *)(ptr->next));
    }
}


int main(void)
{
    /* We use 'foo' to point to the first element in the list. */
    list  *foo = NULL;

    /* Prepend some numbers to the list.
       Note that we need to pass a pointer to the pointer to the first element in the list,
       in this case a pointer to the variable foo. */
    list_prepend(&foo, "one");
    list_prepend(&foo, "two");
    list_prepend(&foo, "three");
    list_prepend(&foo, "four");

    list_dot(stdout, foo, "pointer foo");

    list_free(foo);
    return EXIT_SUCCESS;
}

In Linux, one compiles the above example.c using e.g.
    gcc -Wall -Wextra -O2 example.c -o example
Since it outputs the Graphviz DOT language description of the list hanging off the variable foo, we can run it and pipe its output directly to dot, so it'll show us the nice graph:
    ./example | dot -Tx11
which essentially looks like
    pointer foo → four → three → two → one
except the first is in an rectangle, and the four others are inside ovals/ellipses.
 
The following users thanked this post: Dadu@

Offline Dadu@Topic starter

  • Contributor
  • Posts: 34
  • Country: in
Re: Passing structure into function in c
« Reply #24 on: February 10, 2022, 02:03:38 pm »
To throw you directly into the deep end, consider the following singly-linked list example:
How to modify the function in my code that  link node to next node in list

Code: [Select]
#include <stdio.h>
#include <stdlib.h>


struct node
{
    int data;
    struct node *nextNode;
};


void addNode(int value )
{
    struct node *newNode = NULL;
   
    newNode = malloc (sizeof(*newNode));
   
    if (newNode != NULL)
    {
        newNode -> data = value;
       
        newNode -> nextNode = NULL;
    }
}

int main()
{

    addNode( 2 );
   
    return 0;
}

I know This line doesn't point to address of next node.
Code: [Select]
  newNode -> nextNode = NULL;
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf