简体   繁体   中英

Creating, returning, and casting a struct with a char pointer in C

I'm pretty bad at remembering C rules with structs. Basically, I have a struct like this:

typedef struct {
    char* ptr;
    int size;
} Xalloc_struct;

Where the char* ptr will only be one character max.

In my program, I have to allocate and free memory to a fake disk (declared globally as char disk[100]; ) using my own functions:

char disk[100];

void disk_init() {
    for(int i = 0; i < 100; ++i) {
        disk[i] = memory[i] = 0;
    }
}

struct Xalloc_struct* Xalloc(int size) {
    // error checking
    // ...
    // run an algorithm to get a char* ptr back to a part of the global disk
    // array, where index i is the index where content at disk[i] starts
    char* ptr = &disk[i];
    struct Xalloc_struct *ret = malloc(sizeof(struct Xalloc_struct));
    ret->size = size;
    ret->ptr = malloc(sizeof(char));
    ret->ptr = ptr;
    return ret;
}

int Xfree(void* ptr) {
    struct Xalloc_struct* p = (struct Xalloc_struct*) ptr;
    int size = p->size;
    int index = *(p->ptr);
    // .. more stuff here that uses the index of where p->ptr points to
    free(p->ptr);
    free(p);
    return 0;
}

int main() {
    disk_init();
    struct Xalloc_struct* x = Xalloc(5);
    Xfree(x);
    return 0;
}

When this compiles I get quite a few errors:

 error: invalid application of ‘sizeof’ to incomplete type ‘struct Xalloc_struct’
     struct Xalloc_struct *ret = malloc(sizeof(struct Xalloc_struct));
                                               ^

error: dereferencing pointer to incomplete type
     ret->size = size;
        ^

error: dereferencing pointer to incomplete type
         free(x->ptr);
               ^
 error: dereferencing pointer to incomplete type
     int size = cast_ptr->size;
                        ^
error: dereferencing pointer to incomplete type
     int free_ptr = *(cast_ptr->ptr);
                              ^

So, how should I be allocating and deallocating these structs? And how can I modify / edit what they contain?

First problem is Xalloc_struct is a type, not the name of a struct. You declared that type with this:

typedef struct {
    char* ptr;
    int size;
} Xalloc_struct;

typedef is of the form typedef <type name or struct definition> <name of the type> . So you declared the type Xalloc_struct to be struct { char *ptr; int size; } struct { char *ptr; int size; } struct { char *ptr; int size; } .

That means you use it like any other type name: Xalloc_struct somevar = ...; .

Had you declared the struct with a name...

struct Xalloc_struct {
    char* ptr;
    int size;
};

Then it would be struct Xalloc_struct somevar = ...; as you have.


The rule of thumb when allocating memory for an array (and a char * is an array of characters) is you allocate sizeof(type) * number_of_items . Character arrays are terminated with a null byte, so for them you need one more character.

Xalloc_struct *ret = malloc(sizeof(Xalloc_struct));
ret->ptr = malloc(sizeof(char) * num_characters+1);

But if you're only storing one character, there's no need for an array of characters. Just store one character.

typedef struct {
    char letter;
    int size;
} Xalloc_struct;

Xalloc_struct *ret = malloc(sizeof(Xalloc_struct));
ret->letter = 'q';  /* or whatever */

But what I think you're really doing is storing a pointer to a spot in the disk array. In that case, you don't malloc at all. You just store the pointer like any other pointer.

typedef struct {
    char* ptr;
    int size;
} Xalloc_struct;

Xalloc_struct *ret = malloc(sizeof(Xalloc_struct));
ret->ptr = &disk[i];

Then you can read that character with ret->ptr[0] .

Since you didn't allocate ret->ptr do not free it! That will cause a crash because disk is in stack memory and cannot be free'd. If it were in heap memory (ie. malloc) it would probably also crash because it would try to free in the middle of an allocated block.

void Xalloc_destroy(Xalloc_struct *xa) {
    free(xa);
}

Here's how I'd do it.

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

char disk[100] = {0};

typedef struct {
    char *ptr;
    int idx;
} Disk_Handle_T;

static Disk_Handle_T* Disk_Handle_New(char *disk, int idx) {
    Disk_Handle_T *dh = malloc(sizeof(Disk_Handle_T));
    dh->idx = idx;
    dh->ptr = &disk[idx];

    return dh;
}

static void Disk_Handle_Destroy( Disk_Handle_T *dh ) {
    free(dh);
}

int main() {
    Disk_Handle_T *dh = Disk_Handle_New(disk, 1);

    printf("%c\n", dh->ptr[0]);  /* null */

    disk[1] = 'c';
    printf("%c\n", dh->ptr[0]);  /* c */

    Disk_Handle_Destroy(dh);
}

What you are attempting to accomplish is a bit bewildering, but from a syntax standpoint, your primary problems are treating a typedef as if it were a formal struct declaration, not providing index information to your Xalloc function, and allocating ret->ptr where you already have a pointer and storage in disk .

First, an aside, when you are specifying a pointer, the dereference operator '*' goes with the variable , not with the type . eg

Xalloc_struct *Xalloc (...)

not

Xalloc_struct* Xalloc (...)

Why? To avoid the improper appearance of declaring something with a pointer type , (where there is no pointer type just type ) eg:

int* a, b, c;

b and c above are most certainly NOT pointer types, but by attaching the '*' to the type it appears as if you are trying to declare variables of int* (which is incorrect).

int *a, b, c;

makes it much more clear you intend to declare a pointer to type int in a and two integers b and c .

Next, in Xfree , you can, but generally do not want to, assign a pointer type as an int (storage size issues, etc.) (eg int index = *(p->ptr); ) If you need a reference to a pointer, use a pointer. If you want the address of the pointer itself, make sure you are using a type large enough for the pointer size on your hardware.

Why are you allocating storage for ret->ptr = malloc(sizeof(char)); ? You already have storage in char disk[100]; You get no benefit from the allocation. Just assign the address of the element in disk to ptr (a pointer can hold a pointer without further allocation) You only need to allocate storage for ret->ptr if you intend to use the memory you allocate, such as copying a string or multiple character to the block of memory allocated to ret->ptr . ret->ptr can store the address of an element in data without further allocation. (it's unclear exactly what you intend here)

You are free to use a typedef , in fact it is good practice, but when you specify a typedef as you have, it is not equivalent to, and cannot be used, as a named struct. That is where your incomplete type issue arises.

All in all, it looks like you were trying to do something similar to the following:

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

typedef struct {
    char* ptr;
    int size;
} Xalloc_struct;

char disk[100] = "";

Xalloc_struct *Xalloc (int size, int i) {
    char *ptr = &disk[i];
    Xalloc_struct *ret = malloc (sizeof *ret);
    ret->size = size;
    // ret->ptr = malloc (sizeof *(ret->ptr)); /* you have a pointer */
    ret->ptr = ptr;
    return ret;
}

int Xfree (void *ptr) {
    Xalloc_struct *p = (Xalloc_struct *) ptr;
    // int size = p->size;    /* unused */
    // int index = *(p->ptr); /* what is this ?? */
    // .. more stuff here that uses the index of where p->ptr points to
    // free (p->ptr);
    free (p);
    return 0;
}

int main (void) {

    int i = 0;
    Xalloc_struct *x = Xalloc (5, i++);
    Xfree(x);

    return 0;
}

Look at the difference in how the typedef is used and let me know if you have any questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM