3

As another Javascript developer learning C, I'm trying to implement a basic object.

UserStruct.h

#ifndef USERSTRUCT_H
#define USERSTRUCT_H

struct User {
    int age;
    int (* getAge)();
};
typedef struct User User;

int getAge(User);
User initUser(int);
#endif /* USERSTRUCT_H */

UserStruct.c

#include "./UserStruct.h"
int getAge(User this) {
  return this.age;
} 

User initUser(int age){  
  User user;
  user.age = age;
  user.getAge = getAge;
  return user;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include "./UserStruct.h"
int main(int argc, char** argv) {
   User user = initUser(15); // sets age to 15..
   user.age = 2222;
   printf("%d\n",user.getAge(1234)); // note the int passing here..
   printf("%d\n",user.age);
  return (EXIT_SUCCESS);
}

Questions!:
1. Can someone explain the syntax int (* getAge)(); inside the UserStruct definition?
2. getAge expects a User to be passed to it, how come it works with an int being passed in? This is especially strange as the implementation of getAge uses return this.age.

While I did figure out how to fix it, I'm still not sure why this behaves the way it does. (The solution is to pass a pointer of the User into getAge)

underscore_d
  • 6,309
  • 3
  • 38
  • 64
Patrick
  • 3,289
  • 2
  • 18
  • 31
  • 5
    Your program *doesn't* work. It exhibits [*undefined behavior*](https://en.wikipedia.org/wiki/Undefined_behavior) which makes your whole program *ill-formed* and invalid. The UB stems from your call passing an invalid argument to the `getAge` function. – Some programmer dude Jul 28 '17 at 08:57
  • 4
    *I'm still not sure why this behaves the way it does.* Undefined behavior is undefined. *The solution is to pass a pointer of the User into getAge* Not per the posted code it's not. The solution is to pass a `struct User` to `getAge()`, not a pointer of any kind, and also to declare `int (* getAge)();` as `int (* getAge)( struct User);` instead. – Andrew Henle Jul 28 '17 at 08:58
  • 3
    not again. Please stop trying to force `C` to work as another language/paradigm you are accustomed to. You can't do OOP in `C`. You can't do overloading in `C`. You can't do polymorphism in `C`. **you can't have member functions in `C`**. Please stop trying to do so. Use `C` as `C` or use another language where you can use the paradigm you want. – bolov Jul 28 '17 at 08:58
  • 3
    As for the declaration `int (* getAge)()`, that declares `getAge` to be a pointer to a function taking an indeterminate number of arguments of indeterminate type, returning an `int`. – Some programmer dude Jul 28 '17 at 08:58
  • 1
    This'll be a duplicate of [Can you write object-oriented code in C?](https://stackoverflow.com/questions/351733/can-you-write-object-oriented-code-in-c) or one of the many other threads about OOP in C, e.g. https://stackoverflow.com/questions/524033/how-can-i-simulate-oo-style-polymorphism-in-c – underscore_d Jul 28 '17 at 09:06
  • getAge is a pointer to a function, but this code is wrong. – Sir Jo Black Jul 28 '17 at 09:06
  • so this is really a chimera of two questions, both of which are duplicates. – underscore_d Jul 28 '17 at 09:10
  • As I said, I know this code is wrong :) I was just wondering why I was getting this strange behavior. The concept of "undefined behavior" is new to me(coming from javascript), so I didn't realize unexpected things can happen, even if they somehow fit what you want to do. *Sorry for offending pure C developers with this OOP nonsense!* – Patrick Jul 28 '17 at 09:10
  • I send you a reply the contains a code that run a little bit better! :) – Sir Jo Black Jul 28 '17 at 09:15
  • "Can someone explain the syntax `int (* getAge)();`"-- I don't understand why you are using this if you don't know what it means. This declaration is the root of your problems. Note that [this is deemed an obsolescent feature](http://port70.net/~nsz/c/c11/n1570.html#6.11.6) in C11. Also, I didn't see anyone call OOP "nonsense", just that it is a bad idea to attempt OOC before understanding vanilla C. – ad absurdum Jul 28 '17 at 14:19

2 Answers2

3

C is not an object oriented language as it existed before object, but your example is demonstrating that it can be used with objects.

  • int (* getAge)() is a function pointer, the function takes any parameters () and returns int, to specify that the function takes no parameter should be defined (void)

  • getAge is a "method" of class user it needs an object instance, in object languages the syntax would be user.getAge() which passes implicitly this as first argument, in C it's explicit: getAge(user).

The problem is that an int can be used as pointer which means user.getAge(1234) will use 1234 as an address to a user so will take the second int field as an address to fuction getAge and jump to this address.

Nahuel Fouilleul
  • 18,726
  • 2
  • 31
  • 36
  • Thanks, so apart from code being wrong(which I already wrote that I knew it was), I'm getting these behaviors because they're `undefined`, which means anything can happen. apart from that, assuming I pass correct "instances" to the "member functions" it should operate as expected. – Patrick Jul 28 '17 at 09:09
  • @M.M, it will be used as pointer because user.getAge points to getAge function defined in UserStruct.c. the declaration `int(*getAge)()` allows to use any arguments, `int(*getAge)(void)` will prevent `this.getAge=getAge;` – Nahuel Fouilleul Jul 28 '17 at 09:47
  • 1
    `getAge` is a function pointer, `1234` is a function argument. The function being pointed to doesn't even take any pointer arguments. – M.M Jul 28 '17 at 09:53
  • `getAge` is a function pointer that can take any argument because of `()`. `1234` is an argument and the real function `getAge` expects a User argument so dereferencing 1234 may raise a Segmentation fault – Nahuel Fouilleul Jul 28 '17 at 10:24
  • This makes no sense. When is the argument `this` dereferenced? `this` is not even a pointer. This is undefined behavior that can be resolved by changing the declaration of the function pointer to: `int (* getAge)(struct User);` so that an incompatible-type error is raised at compilation with: `user.getAge(1234)`. Even better to remove the superfluous `typedef`. – ad absurdum Jul 28 '17 at 13:45
  • @DavidBowling - I know this makes no sense, and I mentioned it. I was wondering why it was printing what it did. It makes no sense, yet it prints the `int` passed to `getAge` - I was just wondering why that happens, and it seems that the overall agreement is that it's "undefined behavior" – Patrick Jul 28 '17 at 14:57
  • @Patrick-- I meant that this _answer_ makes no sense, for the reasons outlined in my previous comment. The witnessed behavior may be explicable in an implementation-defined way, and may or may not be repeatable. `getAge()` expects a `User` structure as an argument, and you can't assign an `int` to a `struct`. Fixing the declaration will keep this from compiling. Also note that `struct`s are passed by value in C, unlike arrays which decay to pointers in function calls. – ad absurdum Jul 28 '17 at 15:09
2

I've modified your code in a way that it runs better.

The getAge field of the structure User is a pointer to a function. I've modified its prototype to obtain it points a function that receives a pointer to the structure from which you want get the age field contents. (I used a pointer to the structure to avoid to transfer the entire structure by means the stack)

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>

struct User {
    int age;
    int (* getAge)(struct User *);
};

typedef struct User User;

int getAge(User *);
User initUser(int);

int getAge(User * this_) {
  return this_->age;
}

User initUser(int age){
  User user;
  user.age = age;
  user.getAge = getAge;
  return user;
}

int main(int argc, char** argv) {
   User user = initUser(15); // sets age to 15..

   // user.age = 2222;

   printf("%d\n",user.getAge(&user)); // note the pointer used as parameter

   printf("%d\n",user.age);
  return (EXIT_SUCCESS);
}
Sir Jo Black
  • 2,024
  • 2
  • 15
  • 22
  • Thanks, is there a way to abstract the passage of instance(or pointer) to the function? I already heard that overloading is not a possibility – Patrick Jul 28 '17 at 09:16
  • This is not C++ is C. If you look at the code I've sent you, you note that I've renamed the variable `this` as `this_`, thus avoid confusion between C and C++. – Sir Jo Black Jul 28 '17 at 09:18
  • Overloading is not a capability of the C language. – Sir Jo Black Jul 28 '17 at 09:21
  • The more I learn about C the more I admire people who are working on it day to day! It's like programming with handcuffs :) Thanks for all your help! – Patrick Jul 28 '17 at 09:21
  • If you want things that were pretty much the first things C++ added to C, use C++, not C. – underscore_d Jul 28 '17 at 10:55