61

I have a struct as follows, with a pointer to a function called "length" that will return the length of the chars member.

typedef struct pstring_t {
    char * chars;
    int (* length)();
} PString;

I have a function to return the length of the characters from a pointer to a PString:

int length(PString * self) {
    return strlen(self->chars);
}

I have a function initializeString() that returns a pointer to a PString:

PString * initializeString() {
    PString *str;
    str->length = &length;
    return str;
}

It is clear that I am doing something very wrong with my pointers here, because the str->length = &length line causes an EXC_BAD_ACCESS signal in my debugger, as does `return strlen(self->chars). Does anyone have any insights into this problem?

I specifically want to be able have the initializeString() function return a pointer to a PString, and the length function to use a pointer to a PString as input. This is just an experiment in implementing a rudimentary object-oriented system in C, but I don't have a lot of experience dealing with pointers head-on. Thanks for any help you can give me.

Jonathan Sterling
  • 18,320
  • 12
  • 67
  • 79
  • 4
    Although you say you specifically want to return a pointer. In this case, returning a struct itself is far better. It's a cheap to copy struct, and there is no need for a heavy weight heap allocation. String objects are value-based: They do not have identity (address), but their equality is based on their content. You could have this sense of distinction in your OOP system too, somehow. – Johannes Schaub - litb Aug 29 '09 at 16:41

5 Answers5

80

Allocate memory to hold chars.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct PString {
        char *chars;
        int (*length)(struct PString *self);
} PString;

int length(PString *self) {
    return strlen(self->chars);
}

PString *initializeString(int n) {
    PString *str = malloc(sizeof(PString));

    str->chars = malloc(sizeof(char) * n);
    str->length = length;

    str->chars[0] = '\0'; //add a null terminator in case the string is used before any other initialization.

    return str;
}

int main() {
    PString *p = initializeString(30);
    strcpy(p->chars, "Hello");
    printf("\n%d", p->length(p));
    return 0;
}
Dioswison
  • 81
  • 9
KV Prajapati
  • 93,659
  • 19
  • 148
  • 186
  • 2
    Great answer! Question: I need to call free(p), but do I also need to free the chars? – Jonathan Sterling Aug 29 '09 at 04:15
  • I suppose I need to write a destructor. – Jonathan Sterling Aug 29 '09 at 04:16
  • 17
    There's no need to cast the return value of `malloc` in C. Also, you should initialise `str->chars` after you allocate it (ie. `str->chars[0] = '\0';`). – caf Aug 29 '09 at 04:41
  • 4
    Does the function pointer `int (* length)()` need/not need to identify the `PString *` parameter? – Ephemera Oct 09 '13 at 23:26
  • 3
    @ephemera has a point. You have to use the full prototype of the function. I tried without specifying the function's arguments in the PString-structure and I get an error. If I write **int (* length)(PString *self);**, it works fine. – rbaleksandar Nov 08 '13 at 11:41
  • 1
    Why does initializeString use `str->length = length;` instead of `str->length = &length;`? Does it automatically get reduced to a pointer with that usage? – Jacob Phillips Nov 22 '13 at 22:11
  • Why doesn't this compile (at least using MinGW)? GCC complains about `invalid conversion from 'void*' to 'PString*' [-fpermissive]` and `invalid conversion from 'void*' to 'char*' [-fpermissive]` in the `malloc` calls. – Gérard Binkhorst Nov 20 '20 at 17:06
  • For me this only compiles using g++ instead of gcc and after adding typecasts before the `malloc` calls (`(PString *)` and `(char *)` respectively). – Gérard Binkhorst Nov 20 '20 at 17:28
7

My guess is that part of your problem is the parameter lists not matching.

int (* length)();

and

int length(PString * self)

are not the same. It should be int (* length)(PString *);.

...woah, it's Jon!

Edit: and, as mentioned below, your struct pointer is never set to point to anything. The way you're doing it would only work if you were declaring a plain struct, not a pointer.

str = (PString *)malloc(sizeof(PString));
jtbandes
  • 115,675
  • 35
  • 233
  • 266
6

Maybe I am missing something here, but did you allocate any memory for that PString before you accessed it?

PString * initializeString() {
    PString *str;
    str = (PString *) malloc(sizeof(PString));
    str->length = &length;
    return str;
}
EdH
  • 3,194
  • 3
  • 21
  • 23
4

The pointer str is never allocated. It should be malloc'd before use.

jgottula
  • 1,346
  • 2
  • 11
  • 17
  • 1
    Replace `PString *str;` with `PString *str = (PString *)malloc(sizeof(PString));` And ideally, call `free` with the pointer when done using it to avoid memory leaks. – jgottula Aug 29 '09 at 03:54
0

You can use also "void*" (void pointer) to send an address to the function.

typedef struct pstring_t {
    char * chars;
    int(*length)(void*);
} PString;

int length(void* self) {
    return strlen(((PString*)self)->chars);
}

PString initializeString() {
    PString str;
    str.length = &length;
    return str;
}

int main()
{
    PString p = initializeString();

    p.chars = "Hello";

    printf("Length: %i\n", p.length(&p));

    return 0;
}

Output:

Length: 5
ontech7
  • 13
  • 5
  • Beware! This leads to undefined behavior, since the variable `PString str;` in `initializeString()` does not exist, when you access it through pointer in `main()`. – Kupto Mar 28 '18 at 11:29
  • As @Kupto mentioned since you aren't allocating memory to the pointer of the function there is really no telling what your program is really going to do. I recommend having initializestring method return a ptr to the type, and before returning it, assign the pointer to the function the function. – Larry Oct 18 '19 at 23:41