C -- Structs and Pointers Basic Questions - c

So I'm trying to learn C right now, and I have some basic struct questions I'd like to clear up:
Basically, everything centers around this snippet of code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NAME_LEN 127
typedef struct {
char name[MAX_NAME_LEN + 1];
unsigned long sid;
} Student;
/* return the name of student s */
const char* getName (const Student* s) { // the parameter 's' is a pointer to a Student struct
return s->name; // returns the 'name' member of a Student struct
}
/* set the name of student s
If name is too long, cut off characters after the maximum number of characters allowed.
*/
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct | 'name' is a pointer to the first element of a char array (repres. a string)
char temp;
int i;
for (i = 0, temp = &name; temp != '\0'; temp++, i++) {
*((s->name) + i) = temp;
}
/* return the SID of student s */
unsigned long getStudentID(const Student* s) { // 's' is a pointer to a Student struct
return s->sid;
}
/* set the SID of student s */
void setStudentID(Student* s, unsigned long sid) { // 's' is a pointer to a Student struct | 'sid' is a 'long' representing the desired SID
s->sid = sid;
}
I've commented up the code in an attempt to solidify my understanding of pointers; I hope they're all accurate.
Also, I have another method,
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
which I'm sure is wrong in some way... I also think my setName is implemented incorrectly.
Any pointers? (no pun intended)

This is very wrong. If you insist on not using strcpy do something like this (not tested)
int iStringLength = strlen(name);
for (i = 0; i < iStringLength; i++) {
s->name[i] = name[i];
}
but make sure that the length is not longer than your array size.
This is also wrong
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
because the s object is destroyed when the function exits - it is local to the function scope and yet you return a pointer to it. So if you try to access the struct using this pointer it will not be valid as the instance no longer exists. If you want to do this you should dynamically allocate it using malloc . Alternatively do not return a pointer at all and use the alternative option of #Andrew .

In your "another method" you are locally declaring Student s, which will dynamically allocate space (usually on the stack) and you are returning that address on completion.
However, that stack-space will be released on the return, so there is no guarantee that the data is uncorrupted - in fact the likelyhood is that it will be!
Declare Student s in the call to your method, and pass the pointer to makeAndrew:
void makeAndrew(Student *s) {
setName( s, "Andrew");
setStudentID( s, 12345678);
}
...
Student s;
makeAndrew( &s );
...

Your function makeAndrew returns pointer to a local variable. It is only valid before the scope ends, so as soon as the function finishes, it will change when the memory gets overwritten - i. e. almost instantly. You would have to allocate it dynamically (using Student *s = new Student;, or if you really want to stick to pure C, Student *s = malloc (sizeof Student );, and then free it outside the function after it is not needed to avoid memory leak.
Or do it as Andrew suggested, it's less error-prone.

I would change the makeAndrew() function to just return a struct, not a pointer to a struct to correct the error with respect to returning a pointer to a temporary variable:
Student makeAndrew(void)
{
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return s;
}
Student aStudent = makeAndrew();
Your setName does have an error with respect to temp, which should be a char *, since you are incrementing it in your loop to point to another character in the input c-string. I think it was missing the null termination as well. And as you mention in your comment, there should be a check for overflow of the name char array in Student:
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct |
// 'name' is a pointer to the first element of a char array (repres. a string)
const char *temp;
int i;
for (i = 0, temp = name; *temp != '\0' && i <= MAX_NAME_LEN; temp++, i++)
{
*((s->name) + i) = *temp;
}
s->name[i] = '\0';
}
You can use strncpy to simplify setName:
void setName2(Student *s,const char *name)
{
#include <string.h>
strncpy(s->name, name,MAX_NAME_LEN);
s->name[MAX_NAME_LEN] = '\0';
}

Related

using free() function causes runtime error

I have a struct called Person, that contains two attributes - first and last name.
After successfully dynamic allocation of memory for a variable of Person type, giving values to the attributes I would like to free the memory, but I keep getting a runtime error (the program window just crashes)
this it the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char firstName[15];
char lastName[15];
} Person;
void main(){
int len = 0;
char firstName[]="danny", lastName[]="johnes";
Person *temp = (Person*)malloc(sizeof(Person));
if (temp == NULL)
return;
len = strlen(firstName);
temp->firstName[len] = (char*)malloc(sizeof(char)*(len));
if (temp->firstName == NULL)
return;
strcpy(temp->firstName, firstName);
len = strlen(lastName);
temp->lastName[len] = (char*)malloc(sizeof(char)*(len));
if (temp->firstName == NULL)
return;
strcpy(temp->lastName, lastName);
freePerson(temp);
system("pause");
return;
}
This is the function I use to free the memory:
void freePerson(Person* ps) {
if (ps != NULL) {
free(ps->firstName);
free(ps->lastName);
free(ps);
}
}
All I want the code to do - is to store the name in a dynamically allocated structure, and free it.
Later on, I plan to replace the hard-coded names with values inputed from file.
Any ideas about the error? Thank you.
You have already space allocated for firstName, so you have to copy the name within the size constraits (15 bytes). You can do this best with snprintf like this:
snprintf(temp->firstName, sizeof(temp->firstName), "%s", firstName);
Same goes for lastName. Mind that both might be truncated if the length exceeds the size of the field.
The other option is to allocate the fields dynamically. Then your struct members should be pointers, not char arrays:
typedef struct {
char *firstName;
char *lastName;
} Person;
You can then allocate and assign the names like this:
temp->firstName = strdup(firstName); // (same for lastName)
But mind that you have to free these fields seperately if you want to free the whole item.
If you don't want to specify a maximum size for the names in the structure, you need to declare them as pointers, not arrays.
typedef struct {
char *firstName;
char *lastName;
} Person;
Then you should assign the result of malloc() to the member, without indexing it. You also need to add 1 to strlen(firstName), to make space for the null terminator.
temp->firstName = malloc(strlen(firstName)+1);
if (temp->firstName == NULL) {
return;
}
strcpy(temp->firstName, firstName);
This is how I would write this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define FIRSTNAME_MAXLEN 15
#define LASTNAME_MAXLEN 15
typedef struct
{
char firstName[FIRSTNAME_MAXLEN+1];
char lastName[LASTNAME_MAXLEN+1];
} person_t;
void freePerson(person_t *ps) {
if (ps) {
free(ps); ps=NULL;
}
}
int main(){
const char *firstName="danny";
const char *lastName="johnes";
person_t *temp = calloc(1, sizeof(person_t));
if (!temp) return 1;
strncpy(temp->firstName, firstName, FIRSTNAME_MAXLEN);
strncpy(temp->lastName, lastName, LASTNAME_MAXLEN);
printf("test: firstname: %s\n", temp->firstName);
printf("test: lastname: %s\n", temp->lastName);
freePerson(temp);
return 0;
}
You allocate enough room on the heap and cleanup things with calloc(), then you copy your string with strncpy() limiting to the bytes reserved and avoiding buffer overflow. At the end you need to free() the memory returned by calloc().
Since you allocated char firstName[] and char lastName[] inside your struct you don't need to reserve other memory with malloc() for those members, and you also don't need to free() them.
At least 5 issues:
To duplicate a string, insure allocation includes enough room for the characters including the null character.
Otherwise the strcpy() writes outside the allocation which is undefined behavior (UB).
len = strlen(firstName);
// temp->firstName[len] = (char*)malloc(sizeof(char)*(len ));
temp->firstName = (char*)malloc(sizeof(char)*(len + 1));
// + 1
...
strcpy(temp->firstName, firstName);
Same for lastName.
Also assign to the pointer, not the char. #Barmar
Person members are arrays. For dynamic allocation, they should be pointers. #NthDeveloper
typedef struct {
// char firstName[15];
// char lastName[15];
char *firstName;
char *lastName;
} Person;
2nd test is wrong
// if (temp->firstName == NULL)
if (temp->lastName == NULL)
int vs. size_t.
int len = 0; assumes the string length fits in a int. Although this is exceedingly common, the type returned from strlen() is size_t. That unsigned type is right-sized for array indexing and sizing - not too wide, not too narrow. Not a key issue in this learner code.
// int len = 0;
size_t len = 0;
Tip: cast not needed. Allocate to the referenced object, not the type. Easier to code right, review and maintain.
// Person *temp = (Person*)malloc(sizeof(Person));
Person *temp = malloc(sizeof *temp);
// temp->firstName[len] = (char*)malloc(sizeof(char)*(len + 1));
temp->firstName = malloc(sizeof *(temp->firstName) * (len + 1));
Tip: Although not C standard, many platforms provide strdup() to allocated and copy strings. Sample strdup() code.
temp->firstName = strdup(firstName);
Tip: Likely the most valuable one: A good compiler with warnings well enabled should have warned about temp->firstName[len] = (char*)malloc(sizeof(char)*(len)); as it is a questionable type mis-match in the assignment. These warnings save you and us all time. Insure your next compilation has all warning enabled.

Segfault when dereferencing pointer to pointer within struct

I have a structure which includes a pointer to a pointer as one of its members. I keep getting a segfault when trying to dereference this pointer.
Create a person in person_init and give it a name (John). Name is a pointer to a character string. I can printf() no problem in this function. Returning to the main() function, again I can printf() the name no problem. But then when
I enter a new function and try to printf() I get a segfault. I'm really confused because I'm pretty sure name is being allocated on the heap.
What am I missing here?
code:
#include <stdio.h>
#include <stdlib.h>
/* structure with a pointer to pointer member */
struct person {
char **name;
};
/* allocate space for the strucutre */
int person_init(struct person **p)
{
struct person *newp = malloc(sizeof(struct person));
/* give a name, allocated on the heap */
char *name = malloc(sizeof(char) * 5);
*name = 'J';
*(name + 1) = 'o';
*(name + 2) = 'h';
*(name + 3) = 'n';
*(name + 4) = '\0';
newp->name = &name;
*p = newp;
printf("Name in init: %s\n", *(*p)->name); /* this works */
return 0;
}
void print_name(struct person *p)
{
printf(*p->name);
}
int main()
{
struct person *person;
person_init(&person);
printf("Name in main: %s\n", *person->name); /* works */
print_name(person); /* segfault */
}
Here's the problem:
newp->name = &name;
newp->name now points to name, which is a local variable in person_init. As soon as person_init returns, name is gone and newp->name is an invalid pointer. Any attempt to use it afterwards results in undefined behavior.
Fix:
struct person {
char *name;
};
And initialize it as
newp->name = name;
Now newp->name is a copy of name, i.e. it points to the allocated string.

Dynamic memory allocation of a structure

I need to write a program in which is structure with two fields: integer and string. Next I need to write a function which dynamically allocates this structure and takes int and string as parameters to pass them down to allocated structure. This function will also return pointer to newly made structure. Second element of this program should be function which takes struct pointer as parameter, then prints all of the fileds on screen and then free memory of struct. This is the best I could come up with.
#include <stdio.h>
#include <stdlib.h>
struct str{
int num;
char text[20];
};
struct str* return_address(int *num, char *text){
struct str* new_struct=malloc(sizeof(struct str));
new_struct->num=num;
new_struct->text[20]=text;
return new_struct;
};
void release(struct str* s_pointer){
printf("%d %s", s_pointer->num, s_pointer->text);
free(s_pointer);
};
int main()
{
struct str* variable=return_address(1234, "sample text");
release(variable);
return 0;
}
Your array is very small, also it's not dynamic at all. If you are allocating using malloc() anyway, why not allocate everything dynamically?
You cannot assign to an array.
The num member, which I suppose is meant to store the length of the "string", is being assigned a pointer, which is not what you apparently want. And also, the behavior is only defined in very special circumstances when you assign a pointer to an integer, the compiler should be warning you unless you turned off warnings.
Perhaps you want this,
struct string {
char *data;
int length;
};
struct string *
allocate_string(int length, const char *const source)
{
struct string *string;
string = malloc(sizeof *string);
if (string == NULL)
return NULL;
string->length = strlen(source);
// Make an internal copy of the original
// input string
string->data = malloc(string->length + 1);
if (string->data == NULL) {
free(string);
return NULL;
}
// Finally copy the data
memcpy(string->data, source, string->length + 1);
return string;
}
void
free_string(struct string *string)
{
if (string == NULL)
return;
free(string->data);
free(string);
}

How to walk through array of Struct in c

I have a program which creates an array or struct and go through it for processing. Initially it initialize the array with the defined nyumber of elements. Then for some number of element in array, the name is assigned.
I pretend the code that is equal to my scenario which is tested in codebloc and get the similar error. The problem is described in comments.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _car {
char *name;
int year;
} Car;
char *getCarName(char *name, int var);
void processCar();
void printCars(Car car[]);
int INCREMENT = 10;
int main(void)
{
processCar();
return 0;
}
void processCar()
{
// create car array with amount of INCREMENT
Car CAR_ARRAY[INCREMENT];
int a=0;
// This function assign name for Car amount 10 less than INCREMENT
while (a<INCREMENT - 2) {
char *carName;
carName = getCarName(&carName, a);
CAR_ARRAY[a].name = malloc(strlen(carName) + 1);
strcpy(CAR_ARRAY[a].name, carName);
a++;
}
printCars(CAR_ARRAY);
}
void printCars(Car car[])
{
printf("IN Car \n");
int a = 0;
// when try to call name for car amount equals to INCREMENT program terminates.
while(a<INCREMENT) {
if (car[a].name != NULL) // checking if NULL
printf("Car Name : %d -> %s\n", a, car[a].name);
a++;
}
}
char *getCarName(char *name, int var)
{
name = "Toyota";
return name;
}
What is the right way to check the struct value on struct array whether it can be called?
EDIT
I created a hack to do this as follows.
// added these right after creating array
for (a = 0; a < INCREMENT; a++)
CAR_ARRAY[a].name = NULL;
I dont know if it is a standard way or not. pls advice.
You are checking for NULL before printing, which is a good idea, but it doesn't help you here, because your last two cars are uninitialised and likely contain garbage: The name pointer is not NULL, but doesn't point to a valid address either. Segmentation violation ensues.
You should initialise all cars, not only INCREMENT - 2. Alternatively, you could initialise your cars to zero by calling memset before your initialisation:
memset(CAR_ARRAY, 0, sizeof(Car) * INCREMENT);
As an aside, the way you deal with getCarName is rather shaky as well. At the moment, your name is a pointer to a string literal. Your local variable carName does a half-hearted double duty: You try to pass it by reference (but essentially you don't) and you also return it.
Basically, you could do this in one of two ways. The easier one here is to return a pointer. in that case, you don't have to pass any string:
char *getCarName(int var)
{
static char *names[3] = {"Toyota", "Dodge", "Peugeot"};
return names[var % 3];
}
and call it like so:
char *carName = getCarName(&carName, a);
Alternatively, you could pass a char pointer by reference, i.e. as pointer to pointer to char. In that case, you don't have to return anything:
void getCarName(char **name, int var)
{
static char* names[3] = {"Toyota", "Dodge", "Peugeot"};
*name = names[var % 3];
}
Call it like so:
char *carName;
getCarName(&carName, a);
There are other scenarios here, for example if you just pass a char pointer and have getCarName fill it, but I'll leave that for now - it would make everything even more complicated.

double pointer typedef struct array

I am new to c programming and I am stuck with this one its a typedef struct and what I would like to do is that I want to create an array from the double pointer from this structure
typedef struct
{
char* firstname;
float price;
}Name,*pName,**ppName;
typedef struct
{
ppName Names;
unsigned int numPerson;
}Book;
And my main which always give me segmentation fault dont mind the loop it is looping until the use says to quit.
int main(void)
{
Book D;
setUpCollection(&D);
while(..)
{
scanf(...);
switch(...)
{
case 1:
if(!AddNewPerson(&D))
return 1;
break;
case 2:
....
case 3:
....
default:
printf("Please enter a valid choice");
}
}
return 0;
}
void setUpCollection(Book* data){
Name name;
pName pname;
pname= malloc(MAX_PERSON* sizeof(pName));
pname= &name;
data->Names= &pname;
data->numPerson= 0;
}
BOOL AddNewPerson(Book* data){
char *title = malloc(sizeof(char));
int len;
Name name;
pName pname;
scanf(...);
len = strlen(firstname);
name.firstname = malloc(len * sizeof(char*));
name.firstname = firstname;
pname= malloc(1);
pname= &name;
data->DVDs[data->numPerson++] = pname;
printf("%0.2f", data->Names[(data->numPerson)-1]->price);
return TRUE;
}
My main problem is that I cant print all the added names and also getting segmentation fault.
There are quite a few errors in your program but let me mention a few:
Doesn't this seem odd to you:
pname= malloc(MAX_PERSON* sizeof(pName));
pname= &name;
you are creating a memory leak by first letting pname point to the array of pName then assigning to &name.
What is this:
char *title = malloc(sizeof(char)); // ?
here you allocate too less space
name.firstname = malloc(len * sizeof(char*));
it should be
name.firstname = malloc(len * sizeof(char) + 1);
or more readable:
name.firstname = malloc(len+1);
this makes no sense again:
pname= malloc(1);
pname= &name;
again you created a memory leak by first letting pname point to a heap block of 1 byte then assigning it to a local variable which you include in data - the local variable is freed up once you leave AddNewPerson() so data will point to garbage.
Instead do something like this (I am no fan of having
typedefs for pointers), also try avoiding naming types
the same way you name variables for clarity:
typedef struct
{
char *firstname;
float price;
} Name;
typedef struct
{
Name** names;
unsigned int numPerson;
} Book;
Now allocate the initial size of your array, the whole point
of having it on the heap is that the array can grow if more
records are added than MAX_PERSONS so you need to keep track
of the number of used records in the array as well as the number
of records allocated
int allocated = MAX_PERSONS;
Book D;
D.names = malloc( allocated * sizeof(Name*) );
D.numPerson = 0;
then loop over user input and add records keeping
track of how many records have been read. Since names
is an array of pointers, you need to allocate a Name
struct each time you add an entry
e.g.
D.names[i] = malloc( sizeof(Name) );
D.names[i]->firstname = strdup(userInputName);
D.names[i]->price = userInputPrice;
then at each iteration check if there is allocated memory left
++i;
if ( i == allocated )
{
// if yes you need to get more memory, use realloc for that
// get e.g. 10 more records
Name* tmp = realloc( D.names, (allocated + 10)*sizeof(Name) );
if ( tmp != NULL )
{
D.names = tmp;
allocated += 10;
}
else
{ .. some error msg .. }
}

Resources