Related to this question.
What is wrong with the following code?
typedef char (*p)[20] ptr;
ptr myFunction () {
char sub_str[10][20];
return sub_str;
}
int main () {
ptr str;
str = myFunction();
}
Syntactically:
Change
typedef char (*p)[20] ptr;
To
typedef char (*ptr)[20];
To understand the syntax of typedef-declarations like this. Imagine you want to rename type T to type U. Declare a variable of type T named U and prefix the declaration with 'typedef'. That's all.
Semantically:
See my and other answers to your linked question. This is still undefined behavior
You're returning a pointer to memory that will not exist anymore when myFunction() returns.
The main problem is that substr is local to myFunction, and once myFunction exits it no longer exists, so the pointer you return will no longer be valid.
Secondly, you are not using typedef correctly. The proper syntax would be
typedef char (*ptr)[20];
Syntactically,typedef basically acts like a storage class specifier similar to static or extern (although the semantics are different). Basically, you figure out the declaration for an object
char (*ptr)[20]; // ptr is a pointer to a 20-element array of char
and then add the typedef to it:
typedef char (*ptr)[20];
Somehow myFunction needs to allocate memory in such a way that it isn't destroyed as soon as the function exits. Here's one option:
typedef char (*ptr)[20];
ptr myFunction(size_t count)
{
/**
* Dynamically allocate a block of N 20-element arrays of char
*/
ptr p = malloc(sizeof *ptr * count);
return p;
}
int main(void)
{
ptr str = myFunction(10);
size_t i, j;
for (i = 0; i < 10; i++)
for (j = 0; j < 20; j++)
str[i][j] = ...;
...
free(str);
return 0;
}
Related
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; }.
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. e.g.
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) e.g.:
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.) (e.g. 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.
I have a structure with a member that I need to pass to a function by reference. In that function, I'd like to allocate memory & assign a value. I'm having issues somewhere along the line - it seems that after the code returns from allocateMemory, the memory that I had allocated & the values that I assigned go out of scope (this may not be exactly what is happening, but it appears to be the case).
#include <stdio.h>
#include <stdlib.h>
typedef struct myStruct_t
{
char *myString;
} myStruct;
void allocateMemory(void *str);
int main(void) {
myStruct tmp = {
.myString = NULL
};
myStruct *p = &tmp;
allocateMemory(p->myString);
//allocateMemory(&(p->myString)); //also tried this
printf("%s", p->myString);
return 0;
}
void allocateMemory(void *str)
{
str = malloc(8);
((char *)str)[0] = 'a';
((char *)str)[1] = 0;
}
If I print the value of str inside of allocateMemory, the 'a' is successfully printed, but if I attempt to print p->myString in main, my string is empty.
Can anyone tell me what I'm doing wrong?
You need to pass address of the structure member and then you can change (aka allocate memory) to it. In your version of the function, you are not taking a pointer not reference of a pointer, so you can change the content of memory referenced by the pointer but not the pointer itself.
So change your function to
void allocateMemory(char **ret_str)
{
char *str = malloc(8);
str[0] = 'a';
str[1] = 0;
*ret_str = str;
}
And then call it as
allocateMemory(&p->myString)
An alternative way of writing the same function Rohan did, eliminating the need to define any new variables:
void allocateMemory(char **str, size_t size) {
*str = malloc(size);
(*str)[0] = 'a';
(*str)[1] = '\0';
}
Note that I pass a size parameter to justify using malloc() in the first place.
I tried all the solutions on the site and didn't manage to fix this problem
i got a declared struct in my header
struct _fileNew;
typedef struct _fileNew fileNew;
in my source file I defined fileNew
struct _fileNew
{
char chars[];
};
now in my main i tried printing something inside the struct
fileNew* blu;
int i;
for ( i = 0; i < 10; i++)
{
blu->chars[i] = 'b';
}
printf("%s", blu->chars);
and i got
error: dereferencing pointer to incomplete type
I ran a debug and i saw that the cahrs was filled correctly but it won't print it. and I doin something wrong in the source file while defining the fileNew.?
thanks!
You need to allocate a memory block for the structure in heap and assign its address to Your pointer.
In C every string ends with a '\0' (string terminator), so You need to add it also.
#include <stdio.h>
#include <stdlib.h>
#define MAX_FILE_SIZE 128
struct _fileNew;
typedef struct _fileNew fileNew;
struct _fileNew
{
char chars[MAX_FILE_SIZE];
};
int main()
{
fileNew *blu = malloc(sizeof *blu);
int i;
for (i = 0; i < 10; i++)
{
blu->chars[i] = 'b';
}
blu->chars[i] = '\0';
printf("%s", blu->chars);
return 0;
}
You only have a pointer that is not actually pointing to anything and need to allocate size for the char[]. In main you need to assign the pointer to point at the declared struct OR use malloc as the other answers suggest:
static fileNew blu;
fileNew *p_blu = &blu;
...
p_blu->chars[i] = 'b';
I've just started out with C and I'm struggling to get to grips when mixing pointers and arrays.
I am getting the following error:
error C2106: '=' : left operand must be l-value
#include <stdio.h>
struct PersonDetails {
char *name;
int *phoneNumber;
};
int* getPhoneNumber(struct PersonDetails *phoneBook[], char* name);
int main() {
struct PersonDetails a;
struct PersonDetails b;
struct PersonDetails people[2];
struct PersonDetails *ptr[2];
char aName = 'T';
int aNum = 123;
char bName = 'O';
int bNum = 456;
a.name = &aName;
a.phoneNumber = &aNum;
b.name = &bName;
b.phoneNumber = &bNum;
people[0] = a;
people[1] = b;
ptr = &people;
printf("%d", *getPhoneNumber(ptr, aName));
return 0;
}
int* getPhoneNumber(struct PersonDetails *phoneBook[], char* name) {
int i;
for (i = 0; i < 2; i++) {
if (*phoneBook[i]->name == *name) return phoneBook[i]->phoneNumber;
}
return 0;
}
It's happening on the line:
ptr = &people;
Edited Code:
#include <stdio.h>
struct PersonDetails {
char *name;
int *phoneNumber;
};
int* getPhoneNumber(struct PersonDetails *phoneBook[], char* name);
int main() {
struct PersonDetails a;
struct PersonDetails b;
struct PersonDetails people[2];
struct PersonDetails *ptr;
char aName = 'T';
int aNum = 123;
char bName = 'O';
int bNum = 456;
a.name = &aName;
a.phoneNumber = &aNum;
b.name = &bName;
b.phoneNumber = &bNum;
people[0] = a;
people[1] = b;
ptr = people;
printf("%d", *getPhoneNumber(ptr, aName));
return 0;
}
int* getPhoneNumber(struct PersonDetails *phoneBook, char* name) {
int i;
for (i = 0; i < 2; i++) {
if (*phoneBook[i].name == *name) return phoneBook[i].phoneNumber;
}
return 0;
}
Transferring sundry comments of mine in dialogue with the OP into an answer
Because the method getPhoneNumber() requires the parameter struct PersonDetails *phoneBook[].
Why does getPhoneNumber() require that eccentric type? There must be a reason why you chose to use it (like "the teacher set that in the question I'm working on"). Otherwise, it seems more likely that the parameter should be either struct PersonDetail *who or struct PersonDetail who[] — which, in the context of a function's parameter list (and only in the context of a function's parameter list) amounts to the same thing.
Originally it was PersonDetails *phoneBook, but I didn't think that would work? Am I wrong in thinking that, and how would I go about using that to find a number using a name?
Assuming you mean struct PersonDetails *phoneBook (there isn't a type PersonDetails in your code, but there is the type struct PersonDetails), then that would work fine. It is a pointer parameter that can either point to a single person's details, or to the start of an array of people's details. Inside the function, as long as you know that the array is big enough, you can use phoneBook[i].name or phoneBook[i].number with care. (Or, indeed, phoneBook->name and phoneBook->number, which refer to the single element pointed at by phoneBook, or you can think of it as using an effective subscript of 0.)
Oh wow, thank you, that helps so much. So I would change ptr to just struct PersonDetails *ptr; as opposed to an array of pointers?
Yes — using struct PersonDetails *ptr; is all you need. You are (accidentally) delving into more complex structures which do have a place in more complex situations, which is why no-one could say "this is wrong", but they're currently beyond what you need, or what you currently understand. That's OK; what you've just learned probably covers 95% or more of real life use cases.
Okay, it all compiles now, but crashes when executed. I have a feeling it has something to do with how I'm assigning both ptr and people. Do I just delete the people array? I've added an edited section in the question if you could have a look for me please?
You are now passing a char value, aName, where the function expects a char *, which would be &aName. The function prototype at the top also doesn't match the function definition; the definition is correct. You need to remove either the * or the [] but not both from the prototype. With that, it 'works'.
Be aware that you don't have strings (the char * values do not point to null terminated arrays of characters) so you can't do string comparison (strcmp()), but fixing that is probably the next phase of development.
Your compiler should have been generating warnings; pay heed. Remember, it knows a lot more about C than you do at the moment!
Working code
#include <stdio.h>
struct PersonDetails
{
char *name;
int *phoneNumber;
};
int *getPhoneNumber(struct PersonDetails *phoneBook, char *name);
int main(void)
{
struct PersonDetails a;
struct PersonDetails b;
struct PersonDetails people[2];
struct PersonDetails *ptr;
char aName = 'T';
int aNum = 123;
char bName = 'O';
int bNum = 456;
a.name = &aName;
a.phoneNumber = &aNum;
b.name = &bName;
b.phoneNumber = &bNum;
people[0] = a;
people[1] = b;
ptr = people;
printf("%d\n", *getPhoneNumber(ptr, &aName));
return 0;
}
int *getPhoneNumber(struct PersonDetails *phoneBook, char *name)
{
int i;
for (i = 0; i < 2; i++)
{
if (*phoneBook[i].name == *name)
return phoneBook[i].phoneNumber;
}
return 0;
}
Note that printing the result directly as shown will fail horribly (usually) if the 'name' is not found. You'll be dereferencing a null pointer, which invokes undefined behaviour — A Bad Thing™! You really need to use:
int *p_number = getPhoneNumber(ptr, &name);
if (p_number == NULL)
printf("No entry for name %c\n", name);
else
printf("Number for %c is %d\n", name, *p_number);
You should also review why you have int *number; instead of just int number; or char *number;. The former is better if you simply store an unformatted integer; the latter is better if you might need to store +44 1395 276679 or something like that, though you should then consider the relative merits of char number[MAX_PHONE_NUMBER_STRING_LEN]; instead of a pointer.
Also, for more nearly general-purpose code, your function should probably be told how many entries there are in the phone-book, rather than using a hard-wired size of 2 (which is a pretty minimal phone-book by any standard):
int *getPhoneNumber(int n_entries, struct PersonDetails *phoneBook, char *name)
{
int i;
for (i = 0; i < n_entries; i++)
{
if (*phoneBook[i].name == *name)
return phoneBook[i].phoneNumber;
}
return 0;
}
Where the number of entries in the array is a parameter. Assuming you have C99 or C11, you could also sensibly write that as:
int *getPhoneNumber(int n_entries, struct PersonDetails phoneBook[n_entries], char *name)
{
for (int i = 0; i < n_entries; i++)
{
if (*phoneBook[i].name == *name)
return phoneBook[i].phoneNumber;
}
return 0;
}
In both these last samples, I've not changed the data types in the structure (even though I think they should be changed). I've also not added const qualifiers to the pointer/array or the name, even though both could legitimately be const-qualified.
struct PersonDetails *ptr[2];
ptr is an array of pointers and array itself is never a modifiable value. lvalue should be some location where you store values(like variable). So this is an error.
You define as follows -
struct PersonDetails people[2];
struct PersonDetails *ptr[2];
which means the people is a 2-element array of structs of type PersonDetails, while ptr is a 2-element array of pointers to such structs.
You can't override the address of an array, it's not just some pointer (although there are some mutual semantics), it's allocated on the stack.
If you meant each element in ptr to point to the respective people element, use a loop:
for (i = 0; i < 2; ++i)
ptr[i] = &people[i];
Also note that one of the effects of passing around all these pointers to simple variables defined in main, is that you can return a null pointer from getPhoneNumber, and pass it to printf - that would segfault
ptr is an array of pointers to struct PersonDetails, so ptr[0] and ptr[1]
are pointers to struct PersonDetails, then you can asign the address of a variable of the type struct PersonDetails to each element of ptr.
in C an array is something like a pointer (but it's not exactly the same) which points to a part of the
memory (with the size of the type of variables times the numbers of elements of the array) where you can store one or more variables of one type, so in
your example, you could see people like a pointer to struct PersonDetails, and ptr like a pointer to a pointer to struct PersonDetails.
So, with all that said, what you can do is ptr[0] = people or ptr[1] = people.
I am looking to do the following:
struct def:
struct mystruct {
char cArr[500];
}
global:
struct mystruct **ptr;
int count = 0;
in main:
ptr = malloc(20*sizeof(struct test *));
for (int i = 0; i != 20 ; i++) {
ptr[i] = malloc(sizeof(struct test));
}
in some function that is called 20 times:
char burp[500];
//put whatever I want in burp;
ptr[count]->cArr = burp //is this right? Or do I have to memcpy to it, and if so how?
count++;
So at the end I will sequentially fill in the array of mystruct with the chars that I want. I tried doing this with char** but had no luck; I am now wrapping it in a struct as it helps me visualize what is going on.
So I want a global array of char[500], where everytime a function is called it puts that char[500] into the index (that is either passed into the function or also global).
Any advice is appreciated; Ofc I will need to free at the end every index of the array as well.
Thanks!
edit:
so would something like:
memcpy(ptr[count]->cArr, burp, 500);
work then?
#include <stdio.h>
#include <stdlib.h>
struct mystruct
{
char *cArr;
// U were trying to assign array using = operator
// Remember its not like in STL where u can perform deep copy of a vector
};
struct mystruct **ptr;
int count = 0;
int main()
{ int i;
ptr = malloc(20*sizeof(struct mystruct *));
for (i = 0; i != 20 ; i++)
{
ptr[i] = malloc(sizeof(struct mystruct));
}
char burp[500]="No this is not correct boy.";
//put whatever I want in burp;
(*ptr+count)->cArr = burp ;
// Assigning pointer to a pointer , OK. Remember pointer != Array.
//is this right? Or do I have to memcpy to it, and if so how?
//count++; // Has no use in your code, enclose in a loop to then use it.
printf("%s\n",(*ptr + count)->cArr); // This works , I think.
}
For arrays i.e. char cArr[500],
If you want to use memcpy u can use it :
memcpy((*ptr+count)->cArr, burp, 500);
Strcpy also works :
strcpy((*ptr+count)->cArr, burp);
Two points are important :
Assignment of pointers to pointers is allowed, but deep copy of array is not.
**ptr is a double pointer.So, (*ptr + count ) or ptr[count] is a pointer to struct.
2nd point is not required for your answer.
You can use strcpy to copy the string.
strcpy(ptr[count]->cArr,burp);
But strcpy terminates on null character. So, make sure your character string(i.e burp) is properly initialized.
I guess all that you wanted to do is to store some text in your structure for later usage.
struct mystruct {
char carr[500];
}
struct mystruct *ptr = NULL;
int count = 0;
main{
...
ptr = malloc( 20 * sizeof(struct test) );
//Function call func()
...
//After performing work
free( ptr );
}
//Some function
func() {
char burp[500];
// burp has some data fed
memcpy( ptr[count]->carr, burp, 500 );
}