Passing structure into function  in c - Page 1
Forum section: electronics / general

author: Dadu@
author_rank: Contributor
timestamp: February 08, 2022, 12:05:34 pm
content: 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

author: DavidAlfa
author_rank: Super Contributor
timestamp: February 08, 2022, 01:07:33 pm
content: Usually you pass a pointer to a structure.Ex.Code: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 0if(data->value0 == 10){// Whatever}}void main (void){do_something(&my_data);}

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 08, 2022, 01:49:14 pm
content: Quote from: Dadu@ on February 08, 2022, 12:05:34 pmBecause C passes variables by value, it is correct to say that you are passing the value of the structure variable.Quote from: Dadu@ on February 08, 2022, 12:05:34 pmConsider the following program:Code:#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 (likearg.x = 2;inbar()), 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 thebaz()function to show that you can return a structure variable from a function just like any other variable. That, and theanothervariable shows that structure variables can be assigned just like normal variables, too.Exactlyhowthe 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 arraydecaysto a pointer of its first element. Thus, when you pass an array to a function, youreallyare 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.

author: pardo-bsso
author_rank: Frequent Contributor
timestamp: February 08, 2022, 03:51:48 pm
content: Quote from: Nominal Animal on February 08, 2022, 01:49:14 pmHere you are returning a struct that was allocated in the stack, this will lead to undefined behaviour and a crash if you are lucky.

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: February 08, 2022, 04:02:38 pm
content: Quote from: pardo-bsso on February 08, 2022, 03:51:48 pmThis 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.

author: Ian.M
author_rank: Super Contributor
timestamp: February 08, 2022, 04:13:13 pm
content: Exactly. The struct is returned by value (i.e. its contents), not by reference. See the ISO C99 standardQuote from: 6.8.6.4 The return statementOther 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.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 08, 2022, 06:53:41 pm
content: Quote from: pardo-bsso on February 08, 2022, 03:51:48 pmAbsolutely not. =)

author: Siwastaja
author_rank: Super Contributor
timestamp: February 08, 2022, 07:13:54 pm
content: 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., acopy; 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.

author: ejeffrey
author_rank: Super Contributor
timestamp: February 08, 2022, 07:29:12 pm
content: Quote from: pardo-bsso on February 08, 2022, 03:51:48 pmNope. 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.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 08, 2022, 07:47:58 pm
content: Quote from: pardo-bsso on February 08, 2022, 03:51:48 pmAs 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 atCompiler 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 twointmembers do not access stack at all.If we expand the structure to include fourintmembers, the ABI rules change: seehere. Comparing theivec2d_add()andivec2d_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 easiestto 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 discusslvalues(expressions that can be used on the left side of assignment operations), structures are treated just like scalar variable types (unless they containconstmembers 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:char *bad(void){char buffer = "Hello";return buffer;}However, the following is completely acceptable:Code:struct buffer {char data;};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 structureby 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.

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: February 08, 2022, 08:44:46 pm
content: Quote from: Nominal Animal on February 08, 2022, 07:47:58 pmhttps://godbolt.org/z/EnosjdfcvHere'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: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!

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 08, 2022, 09:01:34 pm
content: 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. =) )

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 08, 2022, 11:14:13 pm
content: Quote from: alexanderbrevig on February 08, 2022, 08:44:46 pmIndeed.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:<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 };| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Quote from: SiliconWizard on February 08, 2022, 09:01:34 pmYes. 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 likeptr += sizeof (the type that ptr points to); /* Move tonextone */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 isptr++; /* 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 tovoidand 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 samestorage, but reinterpreting that storage as some other type. That leads nicely to unions and explainingtype punning, with a future reference tostrict 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.

author: hans
author_rank: Super Contributor
timestamp: February 09, 2022, 09:56:48 am
content: 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 itmaycause 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.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 09, 2022, 11:26:48 am
content: Good point; this is exactly why I myself useunaligned accessor functionswhen using GCC:Code:#include <stdint.h>#define ACCESSOR_FUNCTION __attribute__ ((__always_inline__, __unused__)) static inlineACCESSOR_FUNCTION uint32_t get_32le(const void *src) {const unsigned char *const s = src;return ((uint32_t)s)| ((uint32_t)s << 8)| ((uint32_t)s << 16)| ((uint32_t)s << 24);}ACCESSOR_FUNCTION uint32_t get_32be(const void *src) {const unsigned char *const s = src;return ((uint32_t)s << 24)| ((uint32_t)s << 16)| ((uint32_t)s << 8)| ((uint32_t)s);}Note that theACCESSOR_FUNCTIONmacro 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 singlemovinstruction (with the byte order swapped using abswapinstruction in the latter); thus, there is no overhead at all; seethisat Compiler Explorer.On ARMv7-a using Thumb instruction set, these compile to a single unalignedldrinstruction (with the byte order swapped usingrevinstruction in the latter); again, no overhead at all. Seethis.On ARMv8-a, even Clang compiles these to a single instruction with byte order swapping using another instruction if necessary, with no overhead; seethis.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 unalignedmemmove()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 withextended asmones forthat 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.)

author: hans
author_rank: Super Contributor
timestamp: February 09, 2022, 12:17:34 pm
content: I like itAlso 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. :-)

author: pardo-bsso
author_rank: Frequent Contributor
timestamp: February 09, 2022, 12:42:48 pm
content: Quote from: alexanderbrevig on February 08, 2022, 04:02:38 pmMy bad, I stand corrected. (was bitten by things like that when I was more novice than now... )

author: Dadu@
author_rank: Contributor
timestamp: February 09, 2022, 03:44:00 pm
content: I have written following code on PC, complied and ran on PC It compile without errorhow to figure out what happen in code by printing value of both ponter's in code ?Code:#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;}

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 09, 2022, 04:11:40 pm
content: Quote from: Dadu@ on February 09, 2022, 03:44:00 pmYou can use e.g.printf("qtr = %p\n", (void *)qtr);to print the address where pointerqtrpoints to.The(void *)part is acast, and converts whatever pointer expression is on its right side (above,qtr) to an untyped pointer. The%pformat specifier can only print the address of untyped pointers, you see.Note that I do recommend aborting the program execution usingexit(EXIT_FAILURE);ifmalloc()returns NULL, or perhaps usingASSERT(qtr != NULL);. That is, yourfoo()would be better written asCode: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;}or (adding #include <assert.h> near the top of your program),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 sureqtr != NULLat this point; if not, abort the program."However, when compiled with-DNDEBUG(or if there has been a#define NDEBUG), allassert()checks are completely skipped.When you go on to build trees and lists, I like to output their definitions using theGraphvizDOT language (it's plain text), so that I can use Graphviz tools, usuallydot, to print the pointer structures as a nice, visual graph.

author: Dadu@
author_rank: Contributor
timestamp: February 10, 2022, 04:42:16 am
content: Quote from: Nominal Animal on February 09, 2022, 04:11:40 pmwhy does qtr hold different memory location after each callCode:#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 : 00641230qtr hold memory location : 00641260after calling, ptr hold memory location : 00641260qtr hold memory location : 00641270after calling, ptr hold memory location : 00641270qtr hold memory location : 00641280after calling, ptr hold memory location : 00641280

author: brucehoult
author_rank: Super Contributor
timestamp: February 10, 2022, 06:21:53 am
content: Quote from: hans on February 09, 2022, 09:56:48 amIt'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.

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 10, 2022, 10:02:21 am
content: Quote from: brucehoult on February 10, 2022, 06:21:53 amWell 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 theint_fastN_tanduint_fastN_ttypes are useful to ensure the members in the internal structure are fast to access, but sufficiently large (Nbits 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 thememmove()in-place.

author: hans
author_rank: Super Contributor
timestamp: February 10, 2022, 10:32:58 am
content: Quote from: Dadu@ on February 10, 2022, 04:42:16 amThe 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: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

author: Nominal Animal
author_rank: Super Contributor
timestamp: February 10, 2022, 10:56:55 am
content: Quote from: Dadu@ on February 10, 2022, 04:42:16 amPrepend 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 developerintended the code to do.For example:Code:/* 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:#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 = '\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'; /* 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 aboveexample.cusing e.g.gcc -Wall -Wextra -O2 example.c -o exampleSince it outputs the Graphviz DOT language description of the list hanging off the variable foo, we can run it and pipe its output directly todot, so it'll show us the nice graph:./example | dot -Tx11which essentially looks likepointer foo → four → three → two → oneexcept the first is in an rectangle, and the four others are inside ovals/ellipses.

author: Dadu@
author_rank: Contributor
timestamp: February 10, 2022, 02:03:38 pm
content: Quote from: Nominal Animal on February 10, 2022, 10:56:55 amHow to modify the function in my code that link node to next node in listCode:#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:newNode -> nextNode = NULL;

author: Renate
author_rank: Super Contributor
timestamp: February 16, 2022, 07:27:42 pm
content: I'll just say that it's all a can of worms.Are you compiling caller and callee on the same box? Congratulations, it will work and you don't need to know the details.Are you calling a closed source routine? Then you have to know the game rules.I just got through interfacing with some stuff where you didn't know what they were doing.Since it was "Pascal" convention (callee cleans up) then you just need to call with a fistful of args and see how the stack is when the callee returns.One of the functions apparently used one more arg than I was thinking of, ergo, it was a "hidden arg" for a pointer to the return value.The return value was an array of two floats.But it could have been delivered in two FPU regs or whatever.You don't know until you try.