how to allocate memory to array of pointers? - c

i have been given a structure and a pointer to an array.
each index of the array is a letter of the alphabet. i need to receive a name, last name and phone number, and allocate memory to a struct (phonebook).
then, each struct needs to be accessed from the array using the last name's first letter.
if the function is called again i need to use linked list to add another contact.
i dont know how to allocate memory for a certain index of an array. when i try to do
phonebook[letter] = (Contact**)malloc(sizeof(Contact));
i keep having de reference warnings, and i cant seem to figure out how to point the address of phonebook[letter] to a structure properly.
this is what i have tried:
typedef struct Contact {
char* firstName;
char* lastName;
char* phoneNum;
struct Contact* next;
} Contact;
int main(){
Contact* phonebook[26];
addNewContact(phonebook)
}
int addNewContact(Contact** phonebook) {
char newFirstName[SIZE], newLastName[SIZE], newPhoneNum[SIZE];
int letter;
printf("Enter a contact details \
(<first name> <last name> <phone number>):\n");
scanf("%s%s%s", newFirstName, newLastName, newPhoneNum);
//get number of the letter in the alphabet
letter = newLastName[0] - 'A';
//allocate memory to pointer
Contact *current;
phonebook = (Contact**)malloc(sizeof(Contact));
if (phonebook == NULL) {
printf("The addition of the contact has failed!");
//free
exit(1);
}
current = phonebook[letter];
//check if details are being used
do {
//if the name already exists
if (phonebook[letter]->firstName == newFirstName \
&& phonebook[letter]->lastName == newLastName) {
printf("The addition of the contact has failed, \
since the contact %s %s already exists!\n", newFirstName, newLastName);
//free
return 0;
}
//if the phone number already exists
if (phonebook[letter]->phoneNum == newPhoneNum) {
printf("The addition of the contact has failed, \
since the phone number %s already exists!", newPhoneNum);
//free
return 0;
}
current = current->next;
} while (current != NULL);
//assigning
phonebook[letter]->firstName = newFirstName;
phonebook[letter]->lastName = newLastName;
phonebook[letter]->phoneNum = newPhoneNum;
return 0;
}
in addition, i havent figured out the linked list part at all, i managed before to enter the details to the structure (though im not sure if i even pointed the struct to the right place) but i dont know how to iterate after the first addition of the name. if i intialize current->next to be NULL for the first time, it will also happen the next time i call the function.
currently the code stops due do access violation but it seemed that after the first name i had error with reading the inputs that only occured after the second time.

You are actually pretty close to making it work. Probably the main problem with this code is that it has only pointers to the data, doesn't have space to store the data. You are pointing to phonebook[letter]->lastName = newLastName, a local variable that is destroyed when the function returns. This will make your strings a dangling pointer; when you try to access this data, undefined things happen. Probably the easiest way to fix that is to make a char array of maximum length instead of the pointers. You don't need to typedef in most cases, and sometimes it's confusing. I would recommend that you take the typedef out and just have,
#define SIZE 128
struct Contact {
char firstName[SIZE];
char lastName[SIZE];
char phoneNum[SIZE];
struct Contact* next;
};
Double-pointer Contact** phonebook is valid, but a recipe for confusion. Instead of naked pointers, use struct to encapsulate a phone book, using a list of struct Contact.
struct Phonebook {
struct Contact *letter[26];
};
printf is defined in stdio.h. strcmp is defined in string.h. malloc and exit are stdlib.h.
See What's the correct declaration of main()? C doesn't scan ahead; switch the order of the functions or have a prototype above.
Perhaps in the future you will read from a file? It's easier do if you read the input separately from the interface. Maybe split it up into a separate function. This should be static, since it doesn't need to be published to other translation units.
static struct Contact *inputContact(void)
To allocate memory, current = malloc(sizeof *current);. Did you have a typo using phonebook? If you allocate it first, you can read directly into the allocated memory and not do any copying. Make sure you limit the size. (scanf is not meant for this.)
#define STR(n) STR2(n)
#define STR2(n) #n
if(scanf("%" STR(SIZE) "s%" STR(SIZE) "s%" STR(SIZE) "s",
current->firstName, current->lastName, current->phoneNum) != 3)
exit(EXIT_FAILURE);
Then,
static int addNewContact(struct Phonebook* phonebook, struct Contact *contact)
do-while checks that the current is valid after you use it; you want while.
//get number of the letter in the alphabet
int letter = contact->lastName[0] - 'A'; // might want to check that
struct Contact *current = phonebook->letter[letter];
//check if details are being used
while (current != NULL) {
In C, equality means they are the same memory location, not in general the same text. You probably want a library function strcmp. (But are you sure your logic is correct in that it prevents the same number in appearing with the same first letter of the last name?)
if (!strcmp(current->phoneNum, contact->phoneNum))
Assigning will just overwrite the one letter that is there. You probably meant for it to be appended to the list.
contact->next = phonebook->letter[letter];
phonebook->letter[letter] = contact;
return 1;
And lastly, be sure to initialize any data that is of automatic duration.
struct Phonebook phonebook = {0}; /* Initialize null lists. */
addNewContact(&phonebook, inputContact());
--edit
This can be seen as creating a static-26-bucket separately-chained hash-table, with your hash function being hash(struct Contact *entry) { return entry->lastName[0] - 'A'; }.
I think you are doing this, which also makes sense. Make sure that you allocate Contact and 3 text fields, for a total of 4 mallocs per entry. If you read the data into a temporary buffer, you can use strcpy to transfer it to a longer-lived string that you've just allocated.

Related

applications in linked lists in c programming

The question is at follows
The structure below represents information about students and their list of grades in
all modules.
typedef struct {
char* module; //module, e.g. "COMP1028"
int grade; //numerical grade
} module_grade;
typedef struct {
unsigned int student_ID;
char* student_name;
module_grade* grades; //array of grades
unsigned int grades_len; //length of the array grades
} student;
You are required to implement THREE functions for managing students’ grades.
a) Write a C function called create_student_list that takes a student name
and a student ID and returns a pointer to a new_student. The grades of the
new_student will be initialized to NULL and grade_len is set to 0.
The definition of the function is:
student* create_student_list (char* stu_name, int stu_id) {
b) Write a C function search_grade that takes a pointer to a student and a module
and returns the grade of the student for this module. If the array of grades is
NULL or the module is not in the array of grades, the function shall return -1.
The definition of the function is:
int search_grade(student* s, char* module) {
c) Write a C function adding_grade that takes a pointer to a student and a module
grade and adds the grade to the student’s array of grades.
If student->grades already contain a similar module, the function does nothing.
If student->grades==NULL, you need to create an array of length 1 and add the
new grade into the array.
If student->grades!=NULL, you need to increase the size of the array by ONE,
and add new_grade into the new entry in the array of grades.
The function shall return 1 if a grade is successfully added. Otherwise, the
function shall return 0; this can be because the module is already there or malloc
fails.
The definition of the function is:
int adding_grade(student* s, module_grade new_grade)
in question a) I don't understand if he want it to be a linked list so we would need a loop but i can't manage to make a node and link it together so i only assigned the data to the pointer but it is not reading it correctly. outputting only one character and not the whole string.
typedef struct {
char* module; //module, e.g. "COMP1028"
int grade; //numerical grade
} module_grade;
typedef struct {
unsigned int student_ID;
char student_name[50];
module_grade* grades; //array of grades
unsigned int grades_len; //length of the array grades
} student;
student* create_student_list (const char *stu_name, int stu_id);
int main(){
student Student;
module_grade Module;
printf("enter the student name?\t");
scanf("%s",Student.student_name);
printf("%s",Student.student_name);
printf("\nenter the student ID?\t");
scanf("%d",&Student.student_ID);
printf("%d",Student.student_ID);
create_student_list( &Student.student_name, Student.student_ID);
//search grade
printf("enter the module wanted to know the grade?");
scanf("%s",Module.module);
search_grade(&Student,Module.module);
return 0;
}
student* create_student_list (const char *stu_name, int stu_id) {
student student1 = {.student_ID = stu_id,.student_name =
*stu_name};
student *newstudent = &student1;
printf("\n%s\t%d",&newstudent->student_name,newstudent->student_ID);
newstudent->grades = NULL;
newstudent->grades_len = 0;
}
for question b and c i can't manage to make it work because i don't know if i need a linked list or how to link it with the given structures in the question.
please i need help my future and progress in uni depends on it so i will really appreciate any answers or explanation.
Whether to use a linked list or not
You are right that the objects of type module_grade could be stored as a linked list. However, this would require the struct definition of module_grade to contain a pointer to the next node, i.e. the struct definition would have to be changed to the following:
typedef struct module_grade
{
char* module;
int grade;
//pointer to the next node in the linked list
struct module_grade *next;
} module_grade;
However, your teacher probably does not want you to change the struct definition, since it is part of your assignment to use the struct definitions provided by your teacher. Also, in the text of the assignment, the teacher uses the word "array". This probably means that the grades member of the struct definition of student should point to the start of an array, not the first node of a linked list.
The information above only applies to storing the module_grade objects. Whether your teacher wants you to store the individual student objects in a linked list is another question. Since the information you provided in the question does not provide any indication that you need to create more than one student object, there is no reason to create such a linked list. However, I suspect that your teacher may ask you to create such a linked list in a future assignment.
Errors in your code
Errors in the function main
The line
scanf("%s",Student.student_name);
will not work, because scanf requires you to pass a pointer to the memory address of a memory buffer which has sufficient space to store the string. However, you are instead passing a pointer which is not pointing to anything, because it has an indeterminate value.
The simplest way to allocate sufficient memory for storing the string would be to declare a local array.
You are making the same error in the following line:
scanf("%s",Module.module);
Also, in the line
create_student_list( &Student.student_name, Student.student_ID);
it is wrong to pass &Student.student_name, as that will pass the address of the pointer Student.student_name, but you instead want to pass its value. Also, you are discarding the return value of that function call, which you should also not do. Instead, you should use it.
You should also not declare a variable
student Student;
in the function main, because it is the responsibility of the function crate_student_list to create that object.
In order to fix these bugs, you could use the following code:
int main()
{
char student_name[100];
int student_ID;
char module_name[100];
student* p_student;
printf( "Enter the student name: " );
scanf( "99%s", student_name );
printf( "%s\n", student_name );
printf( "Enter the student ID: ");
scanf( "%d", &student_ID );
printf( "%d\n", student_ID );
p_student = create_student_list( student_name, student_ID );
if ( p_student == NULL )
{
fprintf( stderr, "Error creating student!\n" );
exit( EXIT_FAILURE );
}
printf("enter the module wanted to know the grade?");
scanf( "%99s", module_name );
search_grade( p_student, module_name );
return 0;
}
Note that I am limiting the number of matched characters by scanf to 99 characters, so that scanf will not try to write more than 100 characters (99 matched characters plus the terminating null character) to any of the buffers. Otherwise a buffer overflow will occur if the user enters too many characters, which will invoke undefined behavior (i.e. your program may crash).
It is also worth noting that it does not make much sense to call search_grade before calling adding_grade, because if there are no grades stored in Student, then the search will always fail.
Errors in the function create_student_list
The function create_student_list is supposed to return a value. You probably forgot to write
return newstudent;
at the end of this function.
However, this would be wrong, because the lifetime of the object student1, to which the pointer newstudent is pointing, will end as soon as the function create_student_list returns, because you declared it as a variable with automatic storage duration. Therefore, if you return a pointer to that object at the end of the function, then you will be returning a pointer to an object that no longer exists, i.e. a dangling pointer. Any attempt to dereference this pointer in the function main will invoke undefined behavior.
In order to prevent the lifetime of the object to automatically end when the function returns, you should not declare the object as a local variable. Instead, you should use malloc and free to have full control of the lifetime of the object.
Also, you will probably want to create a copy of the string that stu_name is pointing to. Otherwise, if the memory containing this string is later modified (for example used for something else), then you will lose the string. However, if the student object has its own copy of the string, then there is no danger of this happening.
Therefore, it would be better to write the function create_student_list like this:
student* create_student_list ( const char* stu_name, int stu_id )
{
//allocate memory for new_student
student* new_student = malloc( sizeof *new_student );
if ( new_student == NULL )
{
fprintf( stderr, "Warning: malloc failed!\n" );
return NULL;
}
//set struct members except for student_name
new_student->student_ID = stu_id;
new_student->grades = NULL;
new_student->grades_len = 0;
//allocate memory for student_name and copy it
new_student->student_name = malloc( strlen(stu_name + 1 ) );
if ( new_student->student_name == NULL )
{
fprintf( stderr, "Warning: malloc failed!\n" );
free( new_student );
return NULL;
}
strcpy( new_student->student_name, stu_name );
return new_student;
}

struct and free() in C

My data gets overwritten whenever I insert a new value ,If I omit my free() in the main program my program works fine.Why ?How to recify this issue.Is memory allocation of structure is correct?
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct List
{
char val[20] ;
};
struct Hashtable
{
struct List *start;
};
struct Hashtable ht[26];
void init();
void insert(struct List*);
void init()
{
register int j;
for (j=0;j<26;j++)
{
ht[j].start=NULL;
}
}
int main(void)
{
init();
int i=0;
for (int i=0;i<5;i++)
{
struct List *newnode=(struct List*)malloc(sizeof(struct List));
scanf("%s",newnode->val);
insert(newnode);
free(newnode);
newnode=NULL;
}
return 0;
}
void insert(struct List *node)
{
if ( ht[node->val[0]-97].start==NULL)
{
ht[node->val[0]-97].start=node;
return;
}
else
{
printf("The value is %s\n", ht[node->val[0]-97].start->val);
}
}
-------------------------------------------------------------------------------------------------------------------------------------------------___________________________________________________________________________
When you assign pointers, you only copy the pointers themselves, not the memory they might point to.
When you call free using a pointer, all copies of that pointer become invalid and can no longer be used.
You either need to make a completely new copy in the insert function (including allocating a new List structure), or you should not call free.
My personal recommendation is that you don't allocate at all in the main function, and that the insert function takes the string to "insert" as an argument (instead of the List pointer it currently takes).
Perhaps something like this:
void insert(char *val)
{
// Get the hash-table index (note that it only works with ASCII encoding)
char hash = tolower(val[0]) - 'a';
// First check if it exists
if (ht[hash].start == NULL)
{
// No, then add it
// First allocate memory for the node
struct List *node = malloc(sizeof *node);
// Then copy the string
strcpy(node->val, val);
// And finally add it
ht[hash].start = node;
}
}
Then your loop in the main function could be like
for (unsigned i = 0; i < 5; ++i)
{
char val[20];
scanf("%19s", val);
insert(val);
}
Imagine memory is a row of lockers, each containig an 8 bit value, and numbered as 1, 2, 3... (Well, higher values usually, but you get it). A pointer is just the number of that locker, nothing more. What malloc does, is:
Find a spot with free memory
Mark that memory as used
Return the number of the first locker
When you free, you mark again that memory as free. There are many algorithms to do this, but it would be wise to search for a small free memory space whith its number as low as possible, since leaving empty spaces is a waste of memory.
When you assign a pointer, you do not assign the values it points to, but the locker number.
When you free, you give the algorithm the possibility to use that locker, and when you use malloc again, it probably finds the same locker as before, and then you modify its contents.
Remember that your hashtable still uses that locker number, and when it opens the locker, it will find out its contents are modified.
Solution below:
TL;DR:
Your hash table uses still the same memory, so don't free it, since it might be modified when found by malloc.
Free it from the hashtable once you are finished using that pointer, to avoid wasting memory
Note: If you just read the solution, understanding heap (above), will help you with a lot of headaches, plus it is nice to know what the computer is doing with your code.

C Linked List infinite loop

I am trying to edit part of a backnet stack to not use malloc as it always fails. The code uses malloc to create an object and insert into a linked list. In the following code snippet I have commented out the malloc. My plan is to create a local instance of the struct and insert that into my list. I am able to insert 2 items into my list, when trying to add the third, the list is not properly terminated and I enter an infinite while loop. Can anyone see why my list is not properly terminated?
CHobjects is a stuct and I want a linked list of them. I can not use malloc to create new CHobject instances. To get around this I am attempting to create a local instance of CHobject and add that to my list.
CHobjects* newNode(instance, channel, name, description)
{
CHobjects *node;
CHobjects newNode;
node=CHobjects;
while(node!=NULL)
{
if(node->instance==instance)
return
node=node->next;
}
if(strlen((char *)objectName)>objectNameMax || strlen((char *)description)>descriptionMax)
goto cc8; //fail name or description is too long
// if((node=(CHobject *)malloc(sizeof(CHobject)))==NULL) //get a block of space for this object's info
// goto cc8; //fail if we can't get space for it
test.next=CHobjects; //link on to list
CHobjects=&test;
CHcount++;
}
This code simply adds the elements to the list, the whole code would afterwards set some variables to default values.
After our extensive discussion in the comments, I think it's obvious that you problem is the use of local struct instances in a global list. The structs you create on the stack become invalid on exiting the newNode() function, and the same stack space is recycled on the next call. So you link the same instance to itself, and after two calls, you've got a circular list, and enter an infinite loop.
Since you're obviously on plain C without a heap, your only chance is to roll your own struct allocator in global memory, preallocated at compile time. Declare a global array of CHobjects large enough to satisfy all of you allocations (i.e. the maximum length of the list). In your case, this seems to be 4. Here's a raw outline:
#define CHOBJECTS_MAX 4
static CHobjects gaCHobjects [CHOBJECTS_MAX];
static int giNextSlot = 0;
public: static CHobjects* Allocator ()
{
return gaCHObjects + giNextSlot++;
}
The function Allocator() returns a struct pointer from your global array and increments the giNextSlot index, so you get a new instance on each invocation. Use this pointer inside newNode() instead of a local CHobjects instance.
Your question is a little unclear, but I think that it's possible to give you a useful answer anyway.
The only way I can think that you can implement this is using an array as the storage for the linked list, and as a plus you will have both an array and a linked list at the same time
#include <stdio.h>
struct list {
int value;
struct list *next;
};
static void
print_list(const struct list *item)
{
while (item->next != NULL) {
fprintf(stdout, "%d\n", item->value);
item = item->next;
}
}
int
main(void)
{
struct list items[15];
size_t count;
count = sizeof items / sizeof *items - 1;
for (int index = 0; index < count; ++index) {
items[index].next = &items[index + 1];
items[index].value = index + 1;
}
items[count].next = NULL;
print_list(items);
}
As you see, you need access to the given array element to use as storage location, and the array must be valid through the life time of the linked list.

C - Problems copying string from one struct to a node

I am trying to copy strings from a field in one struct to another struct (a node), so that I can ultimately create a hashtable. However, I seem to be having some issues in the actual string copying. I've created a for loop to iterate over the strings in the source stuct, and I know the iteration is working fine, because if I printf the source strings (data[i].c_name), they print out fine. Unfortunately, when I try to printf the destination (class_id), it seems to be empty (and thus of course my hash function isn't doing much). Any insights into the potential problem here would be greatly appreciated. Please let me know if I haven't given enough context.
#define LENGTH 30
#define MAX_OBS 80000
typedef struct
{
char c_name[LENGTH];
char s_name[LENGTH];
double value[MAX_OBS];
}
sample;
typedef struct node
{
char class_id[LENGTH];
struct node *next;
}
node;
{
char class_id[LENGTH];
for (int i = 0; i < total_columns; i++)
{
// malloc a new node pointer for each new class label
node *new_node = malloc(sizeof(node));
// check that there was sufficient memory
if (new_node == NULL)
{
return 6;
}
// copy c_name into node -- failing - class_id is empty
strcpy(new_node->class_id, data[i].c_name);
printf("%s\n", class_id);
}
}
Drop the last char class_id[LENGTH]; that you print as it was never initialized. Then switch your printf() to use the actual target of the strcpy.
strncpy(new_node->class_id, data[i].c_name, LENGTH);
printf("%.*s\n", LENGTH, new_node->class_id);
I've also put a few LENGTH limits in my code to assure you don't do bad things on bad input without a terminal \0. Never blindly trust your C input unless you generated it in a fail-safe manner.
Disclaimer: desktop inspection changes. Actual debugging is left as an exercise to the student.

Refering to an Array. Incrementing and saving index

Learning C through "Learning C the hard way", and doing some of my own exercises. I stumbled upon the following problem.
Let's say I have the following structure:
struct Person {
char name[MAX_INPUT];
int age;
}
In main(), I have declared the following array:
int main(int argc, char *argv[]) {
struct Person personList[MAX_SIZE];
return 0;
}
Now let's say 2 functions away (main calls function1 which calls function2) I want to save a person inside the array I declared in the main function like so:
int function2(struct Person *list) {
struct Person *prsn = malloc(sizeof(struct Person));
assert(prsn != NULL); // Why is this line necessary?
// User input code goes here ...
// Now to save the Person created
strcpy(prsn->name, nameInput);
ctzn->age = ageInput;
list = prsn; // list was passed by reference by function1, does main need to pass the array by
// reference to function1 before?
// This is where I get lost:
// I want to increment the array's index, so next time this function is called and a
// new person needs to be saved, it is saved in the correct order in the array (next index)
}
So if I return to my main function and wanted to print the first three persons saved in it like so:
...
int i = 0;
for(i = 0; i < 3; i++) {
printf("%s is %d old", personList[i].name, personList[i].age);
}
...
Basically how to reference the array across the application while keeping it persistent. Keeping in mind that main does not necessarily call the function directly that makes use of the array. I'm suspecting someone might suggesting declaring it as a global variable, then what would be the alternative? Double pointers? How do double pointers work?
Thank you for your time.
Here are a few pointers (no pun intended!) to help you along:
As it stands, the line struct Person personList[MAX_SIZE]; allocates memory for MAX_SIZE number of Person structs. You don't actually need to allocate more memory using malloc if this is what you are doing.
However, you could save some memory by only allocating memory when you actually need a person. In this case, you want the personList array to contain pointers to Person structs, not the structs themselves (which you create using malloc).
That is: struct Person * personList[MAX_SIZE];
When you create the person:
struct Person * person = (struct Person *) malloc(sizeof(struct Person));
personList[index] = person;
And when you use the person list: printf("%s", personList[index]->name);
Arrays don't magically keep a record of any special index. You have to do this yourself. One way is to always pass the length of the array to each function that needs it.
void function1(struct Person * personList, int count);
If you wanted to modify the count variable when you returned back to the calling function, you could pass it by reference:
void function1(struct Person * personList, int * count);
A possibly more robust way would be to encapsulate the count and the array together into another structure.
struct PersonList { struct Person * list[MAX_SIZE]; int count; }
This way you can write a set of functions that always deal with the list data coherently -- whenever you add a new person, you always increment the count, and so on.
int addNewPerson(struct PersonList * personList, char * name, int age);
I think that much should be helpful to you. Just leave a comment if you would like something to be explained in more detail.
First of all, malloc does not guarantee to allocate new space from the memory and return it. If it cannot allocate the requested memory, it returns a NULL value. That's why it is necessary to check the pointer.
While you are calling function two, you can pass the address of the next element by using a variable that holds the current count of the array in function1;
function2(&personList[count++]);
then you return the current count from function1 to the main function;
int size=function1(personList);

Resources