-2

So, I have 2 structs.

The first one:

typedef struct AST_NODE_STRUCT {
    token* tok;
} ast_node;

The other one:

typedef struct AST_BINOP_STRUCT {
    ast_node base;

    token* tok;

    ast_node* left;
    ast_node* right;
} ast_node_binop;

Now, I have a method that looks something like this:

void do_something(ast_node* node) {
    ...
}

And I want to pass in an instance of the ast_node_binop struct... No compilation errors are thrown if I cast ast_node_binop to a ast_node but valgrind says:

==6554== Memcheck, a memory error detector
==6554== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6554== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==6554== Command: ./cola.out examples/hello_world.cola
==6554== 
==6554== Stack overflow in thread #1: can't grow stack to 0xffe801000
==6554== 
==6554== Process terminating with default action of signal 11 (SIGSEGV)
==6554==  Access not within mapped region at address 0xFFE801FE8
==6554== Stack overflow in thread #1: can't grow stack to 0xffe801000
==6554==    at 0x109B20: parse_expr (parse.c:88)
==6554==  If you believe this happened as a result of a stack
==6554==  overflow in your program's main thread (unlikely but
==6554==  possible), you can try to increase the size of the
==6554==  main thread stack using the --main-stacksize= flag.
==6554==  The main thread stack size used in this run was 8388608.
==6554== Stack overflow in thread #1: can't grow stack to 0xffe801000
==6554== 
==6554== Process terminating with default action of signal 11 (SIGSEGV)
==6554==  Access not within mapped region at address 0xFFE801FD8
==6554== Stack overflow in thread #1: can't grow stack to 0xffe801000
==6554==    at 0x4A266B0: _vgnU_freeres (vg_preloaded.c:59)
==6554==  If you believe this happened as a result of a stack
==6554==  overflow in your program's main thread (unlikely but
==6554==  possible), you can try to increase the size of the
==6554==  main thread stack using the --main-stacksize= flag.
==6554==  The main thread stack size used in this run was 8388608.
==6554== 
==6554== HEAP SUMMARY:
==6554==     in use at exit: 4,587 bytes in 6 blocks
==6554==   total heap usage: 16 allocs, 10 frees, 9,281 bytes allocated
==6554== 
==6554== LEAK SUMMARY:
==6554==    definitely lost: 0 bytes in 0 blocks
==6554==    indirectly lost: 0 bytes in 0 blocks
==6554==      possibly lost: 0 bytes in 0 blocks
==6554==    still reachable: 4,587 bytes in 6 blocks
==6554==         suppressed: 0 bytes in 0 blocks

I looked at this stackoverflow post: Struct Inheritance in C to see how to implement "struct" inheritance in C.

I basically just want to be able to pass in struct instances that are derived from another struct into functions that expects the base/parent struct.

What is the correct way to do this? I will have multiple "derived" structs from ast_node , and I will not always know which derived struct it is, I just know that they will always be derived from ast_node Starting with: ast_node base; in the struct.

I know its confusing but hopefully you will understand what I am trying to achieve.

Here is parse.c since people in the comments said that valgrind complained about something else: https://pastebin.com/SvqeHKqs

Sebastian Karlsson
  • 715
  • 1
  • 8
  • 19
  • Standard C is not an object-oriented language. – Qubit Nov 13 '18 at 08:08
  • @Qubit oo patterns can be very well used in C, though. – Swordfish Nov 13 '18 at 08:08
  • 3
    The Valgrind output clearly states that you have a stack overflow in your program. But you didn't show relevant code. – Swordfish Nov 13 '18 at 08:09
  • Is it impossible to achieve this? @Qubit – Sebastian Karlsson Nov 13 '18 at 08:10
  • There's no "magic" here. You can have nested structs (a struct whose definition contains another struct). In your case, ast_node_binop contains a *pointer* to the struct. Which is perfectly legal :) PS: Whatever's causing Valgrind to blow up - it's NOT* a coding error in what you've shown us. – paulsm4 Nov 13 '18 at 08:10
  • @Swordfish Sure, but if this is the type of approach you want to take it might be better to use C++, unless there is a very specific reason C is required. – Qubit Nov 13 '18 at 08:10
  • @paulsm4 So there is nothing wrong with the code posted above? I can pass the derived struct to a function that expects the base struct? – Sebastian Karlsson Nov 13 '18 at 08:11
  • 4
    @SebastianKarlsson `==6554== Stack overflow in thread #1: can't grow stack to 0xffe801000`: You have a stack-overflow somewhere in your code, the code you show can't be the cause of that. – Qubit Nov 13 '18 at 08:12
  • @Qubit "it might be better to use C++" tell Linus ;) – Swordfish Nov 13 '18 at 08:14
  • @Qubit The weird thing is, if I comment out where I pass a ast_node_binop to a ast_node pointer, no valgrind errors are shown anymore – Sebastian Karlsson Nov 13 '18 at 08:14
  • 1
    I'm with Swordfish (and Qubit). Though I'm not experienced with Valgrind, there is nothing that states any problem with `do_something()` but a clear statement concerning stack overflow. I suspect it hasn't anything to do with the cast (or, at least, in a subtle way that is not obvious according to the exposed code). Btw. I've seen much of such C OOP things in `glib`. I doubt that it's wrong. – Scheff's Cat Nov 13 '18 at 08:15
  • 2
    @SebastianKarlsson Do you call that function recursively? That could be the issue (and given the names I assume you do as it sounds like a tree). Recursively walking through a large tree is a common cause of stack overflows. – Qubit Nov 13 '18 at 08:15
  • Okay guys, I will post exactly what is at parse.c line 88 which valgrin complains about, 1 sec, just gonna update the post – Sebastian Karlsson Nov 13 '18 at 08:16
  • . o O ( One line will hardly be enough ) – Swordfish Nov 13 '18 at 08:16
  • @Swordfish Don't get me wrong, I like C because of the low-level approach, but there are times when life can be made much easier using something else. – Qubit Nov 13 '18 at 08:17
  • @Qubit I totally agree with you. Only having a hammer at hand makes everything sudenly look like a nail. – Swordfish Nov 13 '18 at 08:19
  • https://pastebin.com/SvqeHKqs Here is the whole thing. But yes, I basically just wanted to know if I was doing it correct and since you said valgrind is complaining about something else, this post is no longer about the same problem and should probably be closed. – Sebastian Karlsson Nov 13 '18 at 08:19
  • I dont know what can be wrong at line 88 though, since it is just the beginning of a function definition.... – Sebastian Karlsson Nov 13 '18 at 08:21
  • @SebastianKarlsson It's probably not there, that might just be the function it called and couldn't get enough stack to perform the call. – Qubit Nov 13 '18 at 08:26
  • 2
    Valgrind is not the right tool for the job. Use a debugger to step through your program and find out where functions are called in an order and depth you have not considered. – Swordfish Nov 13 '18 at 08:27
  • Thank you @Swordfish I actually solved the problem by using GDB instead.... The function was called recursively and ran out of memory D: – Sebastian Karlsson Nov 13 '18 at 08:38
  • @SebastianKarlsson You can always replace recursion with a simple stack. – Qubit Nov 13 '18 at 09:02

1 Answers1

0

This is the correct way. The errors posted don't have anything to do with inheritance or type conversions, but some manner of completely unrelated stack overflow. Perhaps you are using recursion or something - I don't know.

It is perfectly fine and well-defined to cast ast_node_binop* to ast_node* and then have that function access the data inside the ast_node_binop.

Formally, this is guaranteed by C17 6.5 §7, ast_node_binop is an aggregate that includes the lvalue type ast_node among its members. It is the first member of the struct, so issues regarding padding and alignment do not apply either.

Lundin
  • 195,001
  • 40
  • 254
  • 396