C Change Pointer of Array to Larger Array - c

I have an array inside a struct like so:
typedef struct mystruct{
const char *myarr[30];
} mystruct;
I need to grow this array later in the program to 60 elements by creating a new array, duplicating the content, and then changing myarr to point to the new array.
I have tried the following:
const char newtable[n];
s->*myarr = newtable;
But gcc complains:
error: incompatible types in assignment
Any ideas as to the right way to accomplish this?

Assuming that you indeed want your array to contain char *s, not chars, you should define your structure like this:
typedef struct {
const char **myarr;
/* I assume you actually have more members here */
} mystruct;
and initialize it like this:
mystruct s;
s.myarr = (const char **) malloc(30 * sizeof(const char *));
if (!s.myarr) { /* handle out-of-memory condition somehow */ }
Then you can later extend it with realloc():
const char **tmp = (const char **) realloc(s.myarr, 60 * sizeof(const char *));
if (tmp) s.myarr = tmp;
else { /* handle out-of-memory condition somehow */ }
(Note that, if realloc() returns NULL, the original value of s.myarr will still be valid.)

Don't allocate it as an array inside the struct. Just leave it as a pointer:
const char *myarr;
Then this'll work:
const char newtable[n];
s->myarr = newtable;
You can still use it like an array, e.g.
char c = s->myarr[20];

You declare an array of pointer, not a pointer point to a array.
typedef struct { // no need mystruct here
const char *myarr;
} mystruct;
const char newtable[60];
s->myarr = newtable;
But const char *myarr is different from const char (*myarr)[30], and you didn't actually create a new array by const char newtable[60];, you maybe need malloc instead.

So growing memory region at runtime should really involve dynamic memory allocation. I'd suggest something like this:
typedef mystruct {
char *data;
} mystruct;
[...]
char *ptr = realloc(s->data, 60); // that will copy your previous data over
if (ptr != NULL) {
s->data = ptr;
}
I don't think it has to be much more complicated than that really... especially you should avoid declaring a 60 elements array statically in the .data/.rodata section...
Hope it helps,

Related

Can I iterate dynamic array without knowing the size of it

I allocated some data in a dynamic array of a struct. Can I iterate that struct * without knowing it's size somehow?
Would this work?
int main() {
struct *foo = NULL;
//struct filling
iterate_foo(foo);
}
void iterate_foo(struct *foo) {
int i=0;
while (foo[i] != NULL) { // Would this work?
//something
}
}
The only way that can work is if you have a sentinel value in the array that indicates the end. Strings in C work that way.
For instance, if you invoke strcpy(a, b), then characters from b will be copied to a until and including the value zero. If there's no zero terminator within the b array, or if a is not big enough to hold all the characters, the function call leads to undefined behavior.
If you don't want to use a sentinel value, you have the option of passing the size as a separate parameter. Another way is to wrap things in a container struct, like this:
struct container {
struct mystruct *data;
size_t size;
}
Furthermore, struct *foo = NULL; is wrong. It has to be something like struct mystruct *foo = NULL;
But if you have this code:
void foo(T *ptr) {
// Do something
}
int main(void) {
T *a = malloc(N * sizeof *a);
T b[N];
foo(a);
foo(b);
}
Then it's completely impossible for foo to figure out N in a portable way. In some cases, believe that the implementation of malloc stores the size right before the data. But don't try to exploit that. It will only cause severe head ache.

C struct with array size determined at compile time

How does one build a struct with an array that can be set differently for each struct, ideally by a parameter? The application being a single data type that supports arrays of different, but fixed lengths
My attempt looks somehting like this, which obviously didnt compile:
struct Data_struct(n)
{
int xData[n];
int test;
};
The only method available is to use a flexible array member.
struct Data_struct {
int test;
int xData[];
};
You would then allocate space for this using malloc():
int n = 4;
struct Data_struct *s = malloc(sizeof(struct Data_struct) + n * sizeof(int));
Note that we had to explicitly allocate additional space for the flexible array.
You can dynamically allocate the array
struct Data_struct
{
int * xData;
int test;
};
....
s.xData = calloc(size, sizeof(int))
and remember to free xData when finished
Normally you would define a variable length array at the end of the struct, and then fix up the size at run-time, e.g.
typedef struct
{
int test;
int xData[1];
} Data_struct;
To allocate a struct such as this with a size of n for xData you'd do soemthing like this:
Data_struct * s = malloc(sizeof(Data_struct) + (n - 1) * sizeof(int));
One might call this ugly but here goes. Use a #define
#define foo(n) struct Data_struct##n { int test; int xData[n]; }
foo(20);
struct Data_struct20 abc;
The foo(20) defines a structure with n = 20 characters.
You could use another #define for the allocation of space if you wish.

access array using pointer to array stored in a struct C

I have a struct defined as following:
typedef char *element_t;
typedef
struct {
element_t *array; /* start of the array */
int capacity; /* number of elements the array */
int length; /* used portion of array, 0..capacity */
} list;
I am trying to access the array that *array points to and assign it a char value and print it.
I'm a bit rusty with C but this is how i'm trying to do it:
list.array[0] = "a";
printf("%s\n", list.array[0]);
This makes my program crash. Any fixes?
EDIT: I should also mention that I have done the following initialisations too:
element_t* array[LMAX];
list.array= *differentArray;
Seems to work for me:
typedef char *element_t;
typedef
struct {
element_t *array; /* start of the array */
int capacity; /* number of elements the array */
int length; /* used portion of array, 0..capacity */
} list;
int main(int argc, char **argv)
{
element_t array[2] = {"foo", "bar"};
list list;
list.array = array;
list.capacity = sizeof(array)/sizeof(array[0]);
list.length = 1;
printf("%s\n", list.array[0]);
return 0;
}
You are most likely forgetting to assign list.array to point to a valid array.
typedef char *element_t; Don't hide pointers behind typedefs, this is bad practice as it makes the code unreadable. element_t *array will be the same as char** array. Is this actually what you want?
list.array[0] = "a"; suggests that you intended "array" to be an array of pointers? Where do you allocate the actual array? Where do you initialize the struct? list.array[0] is pointing into la-la-land rather than at allocated memory, as far as I can tell from the posted code.
Also, the array of pointers need to be of type const char*, since you are pointing at string literals.
Change your initialization to:
element_t differentArray[LMAX];
list.array = &differentArray[0];
The rest of your code should work as is after this change. You don't need any further allocations as long as you only keep putting literals like "a" into it.

C Strings in Structs

I want my struct to carry a string. I defined it like so:
typedef struct myStruct {
char* stringy
} myStruct
and a function
free(char *stringy){
//structObj is a struct object
structObj->stringy = stringy
}
Is this correct? I have a feeling that since it's a local variable, stringy will be lost, and the pointer will point to garbage.
Sorry, new with C.
It would be garbage if you were somehow using char** stringy, but structObj->stringy = stringy means "you know the thing that stringy points to? Now structObj->stringy points to that". Of course, it is still possible to unset the value which the pointer is pointing to, and at that point dereferencing will yield garbage.
Here's an example to make it clearer:
#include<stdio.h>
typedef struct mStruct {
char* stringy;
} myStruct;
myStruct * structObj;
void doSomething(char* stringy)
{
structObj->stringy = stringy;
}
int main(int argc, char* argv)
{
char* a = "abc\n";
structObj = malloc(sizeof(myStruct));
doSomething(a);
a = "qxr\n";
printf(structObj->stringy);
}// prints "abc\n"
If stringy is defined in callers of free function, as long as they keep the actual string in its place (where stringy points), no problem.
There is not any local variable declaration in your code.
You have to declare:
typedef struct myStruct {
char* stringy
} myStruct;
free(char *stringy){
myStruct *structObj;
structObj->stringy = stringy;
}
Pay attention to the semicolon that I've added to the end of the typedef declaration.
This was not not in your code.
The object structObj is a struct whose type is myStruct.
Now, your parameter stringy comes from another site, it is not lost.
But the struct structObj will have duration only inside your "free" function.
EDIT
I have fixed an error: the right declaration has to be "pointer to structObj", which is done in this way:
myStruct *structObj;
Observe that now myStruct is a non-initialized pointer, so the following assignment is legal:
structObj->stringy = stringy;
but will not work.
However I think this goes beyond the scope of the original question...
myStruct is type which you defined for your struct myStruct .that to you need to create an object before using.
you need to do like this:
typedef struct myStruct {
char *stringy;
} myStruct_t; //user defined data type
myStruct_t *obj;
// you need to allocate memory dynamically.
obj= (myStruct_t *) malloc(sizeof(myStruct_t));
usage:
scanf("%s",obj->stringy);
printf("%s",obj->stringy);
in function:
my_free(char *str) //str is local string
{
obj->stringy=str;
}
your can also try this code :
typedef struct myStruct {
char stringy[20]; //char *stringy
} myStruct_t; //user defined data type
myStruct_t obj; //object creation
usage:
scanf("%s",obj.stringy);
printf("%s",obj.stringy);
in function:
my_free(char *str) //str is local string
{
strcpy(obj.stringy,str);
}
You're correct that as soon as what it points to goes out of scope, it will point to garbage: this is a dangling pointer. You'll need to allocate some memory and perform a copy to fix this:
add_string(my_struct* s, const char* c)
{
size_t len = strlen(c);
s->file = malloc(len + 1);
strcpy(s->file, c);
}
Don't forget that you'll need to free it when you're done:
void destroy_struct(my_struct* s)
{
free(s->file);
free(s);
}

How to work with string fields in a C struct?

I'm having trouble making a database based on a singly-linked list in C,
not because of the linked list concept but rather the string fields in the struct themselves.
This is an assignment in C and as far as I know (I'm a newbie), C doesn't recognize 'string' as a data type.
This is what my struct code looks like:
typedef struct
{
int number;
string name;
string address;
string birthdate;
char gender;
} patient;
typedef struct llist
{
patient num;
struct llist *next;
} list;
I was thinking of making a struct for the strings themselves so that I can use them in the struct, like this:
typedef struct string
{
char *text;
} *string;
Then I will malloc() each one of them when it is required to make new data of the string type (array of char).
typedef struct string
{
char *text;
} *string;
int main()
{
int length = 50;
string s = (string) malloc(sizeof string);
s->text = (char *) malloc(len * sizeof char);
strcpy(s->text, patient.name->text);
}
Can someone help me figure this out?
Thank you.
On strings and memory allocation:
A string in C is just a sequence of chars, so you can use char * or a char array wherever you want to use a string data type:
typedef struct {
int number;
char *name;
char *address;
char *birthdate;
char gender;
} patient;
Then you need to allocate memory for the structure itself, and for each of the strings:
patient *createPatient(int number, char *name,
char *addr, char *bd, char sex) {
// Allocate memory for the pointers themselves and other elements
// in the struct.
patient *p = malloc(sizeof(struct patient));
p->number = number; // Scalars (int, char, etc) can simply be copied
// Must allocate memory for contents of pointers. Here, strdup()
// creates a new copy of name. Another option:
// p->name = malloc(strlen(name)+1);
// strcpy(p->name, name);
p->name = strdup(name);
p->address = strdup(addr);
p->birthdate = strdup(bd);
p->gender = sex;
return p;
}
If you'll only need a few patients, you can avoid the memory management at the expense of allocating more memory than you really need:
typedef struct {
int number;
char name[50]; // Declaring an array will allocate the specified
char address[200]; // amount of memory when the struct is created,
char birthdate[50]; // but pre-determines the max length and may
char gender; // allocate more than you need.
} patient;
On linked lists:
In general, the purpose of a linked list is to prove quick access to an ordered collection of elements. If your llist contains an element called num (which presumably contains the patient number), you need an additional data structure to hold the actual patients themselves, and you'll need to look up the patient number every time.
Instead, if you declare
typedef struct llist
{
patient *p;
struct llist *next;
} list;
then each element contains a direct pointer to a patient structure, and you can access the data like this:
patient *getPatient(list *patients, int num) {
list *l = patients;
while (l != NULL) {
if (l->p->num == num) {
return l->p;
}
l = l->next;
}
return NULL;
}
I think this solution uses less code and is easy to understand even for newbie.
For string field in struct, you can use pointer and reassigning the string to that pointer will be straightforward and simpler.
Define definition of struct:
typedef struct {
int number;
char *name;
char *address;
char *birthdate;
char gender;
} Patient;
Initialize variable with type of that struct:
Patient patient;
patient.number = 12345;
patient.address = "123/123 some road Rd.";
patient.birthdate = "2020/12/12";
patient.gender = 'M';
It is that simple. Hope this answer helps many developers.
While Richard's is what you want if you do want to go with a typedef, I'd suggest that it's probably not a particularly good idea in this instance, as you lose sight of it being a pointer, while not gaining anything.
If you were treating it a a counted string, or something with additional functionality, that might be different, but I'd really recommend that in this instance, you just get familiar with the 'standard' C string implementation being a 'char *'...
You could just use an even simpler typedef:
typedef char *string;
Then, your malloc would look like a usual malloc:
string s = malloc(maxStringLength);
This does not work:
string s = (string)malloc(sizeof string);
string refers to a pointer, you need the size of the structure itself:
string s = malloc(sizeof (*string));
Note the lack of cast as well (conversion from void* (malloc's return type) is implicitly performed).
Also, in your main, you have a globally delcared patient, but that is uninitialized. Try:
patient.number = 3;
patient.name = "John";
patient.address = "Baker street";
patient.birthdate = "4/15/2012";
patient.gender = 'M';
before you read-access any of its members
Also, strcpy is inherently unsafe as it does not have boundary checking (will copy until the first '\0' is encountered, writing past allocated memory if the source is too long). Use strncpy instead, where you can at least specify the maximum number of characters copied -- read the documentation to ensure you pass the correct value, it is easy to make an off-by-one error.

Resources