c++ pointer to a Structure whose member is a pointer - c++

Hi all I am working on an assignment , when given a struct and its members I will have to intialise the members using dynamic memory allocation . This is the code I have used:
# include <iostream>
# include <string>
using namespace std;
Structure
struct Student
{
char * name;
float gpa;
} ;
PROTOTYPES
Student * createStudent(char name[], float gpa);
int main ()
{
int length ;
float gpa ;
char *p;
Student *myPointer ;
Student myStudent ;
myPointer = &myStudent;
cout << " Please enter number of characters you want to enter " << endl;
cin >> length ;
length = length + 1;
p= new char [length +1];
cout << " Please enter name " << endl;
cin >> p ;
cout << " please enter gpa "<< endl;
cin >> gpa ;
myPointer = createStudent (p,gpa);
cout << myPointer->gpa;
cout << (*myPointer).name << endl;
Her's the error when printing the name, but when i see the value of the name before printing it is showing the same as entered by the user:
delete[]p;
p = 0;
system("pause");
return 0;
}
This function creates a student object and assigns name and gpa as passed to the student object and returns a pointer of the student object:
Student * createStudent( char name[], float gpa )
{
Student *studentPtr ;
Student studentObject;
studentPtr = &studentObject;
studentPtr-> name = name;
studentPtr-> gpa = gpa ;
return studentPtr ;
}
Can anyone give me an idea of why the name is not printing. Thank you in advance .

studentObject is allocated on the stack, i.e. it has automatic storage duration, which means it will be destroyed when the function returns. The pointer doesn't keep it alive.
That means myPointer is a dangling pointer; dereferencing it (myPointer->) invokes undefined behavior.
To correct it, simply return the object by value:
Student createStudent(char name[], float gpa)
{
Student studentObject;
studentPtr.name = name;
studentPtr.gpa = gpa;
return studentObject;
}
and
Student myStudent = createStudent(p, gpa);
Edit: I just read that you need to initialize the members using dynamic memory allocation. The above solution doesn't do that. For dynamic memory allocation you have to use new:
Student* createStudent(char name[], float gpa)
{
Student* studentPtr = new Student;
studentPtr->name = name;
studentPtr->gpa = gpa;
return studentPtr;
}
and
myPointer = createStudent(p, gpa);
// use
delete myPointer;
It would probably be a good idea to also allocate a new buffer for studentPtr->name with new, instead of just assigning the pointer; you can see Mateusz's answer about that.
But of course this is just a bad example from your professor. As mentioned by gd1, this kind of code is not good modern C++ and shouldn't end up in real production code.

This is totally wrong:
Student* createStudent( char name[], float gpa )
{
Student *studentPtr ;
Student studentObject;
studentPtr = &studentObject;
studentPtr-> name = name;
studentPtr-> gpa = gpa ;
return studentPtr ;
}
studentObject is a local object, that is destroyed just after the function ends. Returning pointers and/or references to local variables is an error - you should get warning from your compiler.
Another issue is this line:
studentPtr->name = name;
What you do now: allocate a block of memory in main(), pass it to an object, that may use this memory and then free it in main(). You should not do that - objects should be responsible for their content. Are you sure, that this responsibility should be yours? I would suggest you to:
Student* createStudent( char name[], float gpa )
{
size_t name_len = strlen(name);
Student studentObject;
studentObject.name = new char[name_len+1];
strncpy(studentObject.name, name, name_len + 1);
studentObject.gpa = gpa ;
return studentObject;
}
And analogical function to destroy Student:
void destroyStudent(Student* student)
{
delete[] student->name;
student->name = nullptr;
}
Obvious solution would be to use std::string, but I assume, that you have a good reason to do all this manually... don't you?
Oh, and if you really need to return Student as pointer:
Student* createStudent( char name[], float gpa )
{
size_t name_len = strlen(name);
Student* studentPtr = new Student;
studentPtr->name = new char[name_len+1];
strncpy(studentPtr->name, name, name_len + 1);
studentPtr-> gpa = gpa ;
return studentPtr;
}
void destroyStudent(Student* student)
{
delete[] student->name;
student->name = nullptr;
delete[] student;
}

Related

Pointer as a structure members

struct student{
string name;
int sNumber,*examGrade;
};
int main(){
int i,k,l;
cout<< "number of student in program:";
cin >>l;
cout<< "number of exam in program:";
cin >>k;
student stName[100], examNum[100];
for(int i=1;i<=l;i++){
student stdnt;
cout<<i<<".student name: ";
cin>>stdnt.name;
cout<<i<<".student number: ";
cin>>stdnt.sNumber;
stName[i] = stdnt;
for(int j=1 ; j<=k; j++){
student * test = new student();
cout<<i<<". student"<<j<<".grade: ";
cin>>*test->examGrade;
examNum[j]= *test;
}
}
return 0;
}
I just started coding. What is the reason why the function could not be completed in the code I wrote?
How to assign value to a pointer struct?
I think I made a mistake accessing the pointer to the struct members.
You used test->examGrade without initializing that. Assign valid pointer to that before dereferencing that.
for(int j=1 ; j<=k; j++){
student * test = new student();
cout<<i<<". student"<<j<<".grade: ";
test->examGrade = new int; // add this, for example
cin>>*test->examGrade;
examNum[j]= *test;
}

What is the necessity of pointer in this piece of C++ code?

Note - I have searched a lot on pointers and references for C++.
I don't seem to understand them in THIS particular scenario. Hence posting it here!
The following is CORRECT code. It is working. I wrote this for an online C++ practice problem. A part of the code was given to me initially.
I don't understand why an array of Person objects is being created in the main function with a *per[n] as shown below:
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
class Person {
string name;
int age;
public:
Person(){
name = "";
}
virtual void putdata() = 0;
virtual void getdata() = 0;
};
class Professor: public Person {
int publications, cur_id;
string name;
int age;
public:
static int professorCount;
Professor(){
name = "";
age = 0;
publications = 0;
cur_id = professorCount + 1;
professorCount++;
}
void getdata(){
cin >> name >> age >> publications;
}
void putdata(){
cout << name << " " << age << " " << publications << " " << cur_id << endl;
}
};
class Student: public Person {
int marks[6];
int cur_id;
string name;
int age;
public:
static int studentCount;
Student(){
name = "";
age = 0;
cur_id = studentCount + 1;
studentCount++;
}
void getdata(){
cin >> name >> age >> marks[0] >> marks[1] >> marks[2] >> marks[3] >> marks[4] >> marks[5];
}
void putdata(){
cout << name << " " << age << " " << marks[0] + marks[1] + marks[2] + marks[3] + marks[4] + marks[5] << " " << cur_id << endl;
}
};
int Professor::professorCount = 0;
int Student::studentCount = 0;
In this main function below, an array of Person objects is being created, but it is given a * in the beginning. How is it working?
int main(){
int n, val;
cin>>n; //The number of objects that is going to be created.
Person *per[n]; // THIS ONE RIGHT HERE! THIS ONE!
for(int i = 0;i < n;i++){
cin>>val;
if(val == 1){
// If val is 1 current object is of type Professor
per[i] = new Professor;
}
else per[i] = new Student; // Else the current object is of type Student
per[i]->getdata(); // Get the data from the user.
}
for(int i=0;i<n;i++)
per[i]->putdata(); // Print the required output for each object.
return 0;
}
I don't understand why an array of Person objects is being created in the main function with a *per[n] as shown below
The purpose of storing a pointer is to support virtual polymorphism (abstract classes like Person cannot be instantiated). A smart pointer serves that as well, but takes care about the correct dynamic memory management.
There's no need to use raw pointers or raw arrays in c++ at all. That code doesn't give a good example of "best practices".
At the main() function
Person *per[n]; // Note that VLA's aren't standard c++ syntax
should be replaced with
std::vector<std::unique_ptr<Person>> per(n);
and the loop accordingly
for(int i = 0;i < n;i++){
cin>>val;
if(val == 1){
// If val is 1 current object is of type Professor
per[i] = std::make_unique<Professor>();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
// Else the current object is of type Student
else per[i] = std::make_unique<Student>();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
per[i]->getdata(); // Get the data from the user.
}
Also
int marks[6];
should be replaced with
std::array<int,6> marks;
std::array is way more convenient and less error prone when passed as function parameter, etc.
You're creating an array of pointers to Person objects. That's how assignments like per[i] = new Professor; can work - new Professor returns a pointer to a Professor object, so you need an array of pointers to store that.

looping with cin inside the loop, thus storing different char*s in an array (c++)

I'm writing a basic program in C++ that creates a student database (name, major and GPA for each student). The user enters how many students will be in the database, and then enters the data items for each student one by one.
I made a loop that executes "x student" number of times and adds the students one by one to an array of Students by calling cin.getline for each data item and then creating an instance of Student with these items which it adds to the array in the next available place.
Name and major are char* variables (they cannot be strings for this assignment).
I'm having (I assume) pointer-based problems, because after putting a watch on the array of students, I realized that as soon as a new student is added to the array, every previous student in the array is set to the same data as the new student. I think it's because the names of the variables are always the same , since theyre in a loop, and I think that when I create a new Student object it keeps being put in the same address, thus changing all previous items in the array.
I have tried deleting the address of newStudent, and I've tried setting it to NULL after it adds a new student, but my program either breaks or the name and major in newStudent become lines of gibberish instead of deleting their content.
Here's the pertinent bits from main.cpp: (I removed the parts where I was trying to delete things and set things to NULL because they were making it worse and I don't know if I was on the right track). this is just the first part of main. it calls other functions later that I wrote, but I tested those and they all work fine. This is the part that's making it all break.
#include<iostream>
#include "student.h"
using namespace std;
int main(){
int numMajors = 0; // keeps track of the total number of Majors
int currAdded = 0; // keeps track of how many students have been added THUS FAR
const int MAX_MAJORS = 100; // max number of majors allowed
const int MAX_LENGTH = 25; // max length of character array
const int TABLE_SIZE = 100; // max table size
int numStudents = 0; // total number of students being added
char* name = new char[MAX_LENGTH]; // cin>>name
char* major = new char[MAX_LENGTH]; // cin>>major
float GPA = 0; // cin>>gpa
//Get Data
cout << "--Student Database Data Entry--" << endl << endl;
cout << "Enter the number of students in registry: ";
cin >> numStudents;
cout << endl << "Enter data for each student: " << endl;
Student table[TABLE_SIZE];
char* majors[MAX_MAJORS];
for(int i = 0; i < numStudents; i++){
cout << " Student " << (i+1) << " : " << endl;
cout << " Last Name: ";
// need to use getline to cin multiple words, so using
// cin.ignore() to eat the trailing \n from cin>>numStudents
cin.ignore();
cin.getline(name, MAX_LENGTH);
cout << " Major: ";
cin.getline(major, MAX_LENGTH);
cout << " GPA: ";
cin >> GPA;
Student* newStudent = new Student(name, major, GPA);
table[i] = *newStudent;
currAdded++;
and here is my constructors and destructor for Student (in student.cpp)
Student::Student() :
name(NULL),
major(NULL),
GPA(0)
{
}
Student::Student(char* name, char* major, float GPA) :
name(NULL),
major(NULL),
GPA(0)
{
this->name = name;
this->major = major;
this->GPA = GPA;
}
Student::~Student()
{
if(name)
delete [] name;
if(major)
delete [] major;
}
Student& Student::operator=(Student& student)
{
this->name = student.name;
this->major = student.major;
this->GPA = student.GPA;
return *this;
}
note: when my destructor is called, it also fills the name and major variables it's supposed to be deleting with long strings of gibberish characters, but that's a whole other problem. I commented it out for a while to temporarily get around this problem but I realize that's not a long term solution (the two are probably related)
If there's any more code you'd like to see I would be happy to post it, I'm trying to keep this as short as I can and I'm not sure if more is necessary.
Thank you so much for your insight
You have to copy the strings passed instead of just assigning the pointers passed.
Try this:
#include <cstring> // for using strlen() and strcpy()
Student::Student(char* name, char* major, float GPA) :
name(NULL),
major(NULL),
GPA(0)
{
this->name = new char[strlen(name) + 1]; // +1 for terminating null-character
strcpy(this->name, name);
this->major = new char[strlen(major) + 1];
strcpy(this->major, major);
this->GPA = GPA;
}

c++ order of cout with variables in struct changes the content? [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 7 years ago.
I'm having an odd problem. I have a practice exercise with pointers and the results are coming out differently depending on the order of cout statements. Depending on of the two struct variables I cout first, they come out differently. More specifically, the struct Student has two variables, name and gpa. After setting up the variables, if I cout gpa then name, gpa is fine and name is not; if I cout name then gpa, name is fine and gpa is not. Any idea why?
This is the code, with name then gpa output:
#include <iostream>
#include <iomanip>
using namespace std;
struct Student
{
char * name;
float gpa;
};
Student *createStudent(char name[], float gpa);
int main()
{
int MAX = 100;
float stuGpa = 0;
char *stuName = new char[MAX];
cout << fixed << setprecision(2);
stuName = "fubar";
stuGpa = 4.0;
Student *student1;
student1 = new Student;
student1 = createStudent(stuName, stuGpa);
// cout name (first)
cout << "\nStudent name is " << student1->name;
// cout gpa (second)
cout << "\nStudent gpa is " << student1->gpa;
return 0;
}
Student *createStudent(char name[], float gpa)
{
int length = strlen(name);
Student newStudent;
newStudent.gpa = gpa;
newStudent.name = new char[length];
newStudent.name = name;
return &newStudent; //return the address of the newly created student
}
My output:
Student name is fubar
Student gpa is 0.00
If I reverse the cout statements, the output is
Student gpa is 4.00
Student name is
Any idea why the cout order is affecting the content of the struct variables?
Yes, you are returning the address of a local variable:
Student *createStudent(char name[], float gpa)
{
Student newStudent; // <-------- here
return &newStudent; //return the address of the newly created student
}
The newly-created student only lives so long as the createStudent function is executing . It gets destroyed when } is reached. You returned a pointer to an object that no longer exist.
You also have a memory leak in the main function:
student1 = new Student; // point "student1" to a newly allocated Student
student1 = createStudent(stuName, stuGpa); // point it elsewhere
The object created by "new" no longer has any pointers to it, because you made "student1" point elsewhere.
There is another such leak in:
stuName = new char[MAX]; // point stuName at some allocated memory
stuName = "fubar"; // point stuName at "fubar" instead
This is the sort of mess you get into when using pointers and new.
To fix this mess your best option is to make the following changes:
Return Student, not Student *
Use Student , not Student * in the main function
Change char * name to std::string name
Change char *stuName = new char[MAX]; to std::string stuName;

Returning a pointer to the youngest student in a dynamic array

I am currently trying to write some function which will iterate through a dynamic array which holds students records in this format:
Name ID Age
Once this function finds the youngest age, return this as a pointer to the main function and output it.
The problem is the function is of type Student, which is my structure; and is formatted like so:
struct Student{
string name;
string id;
int age;
};
I am struggling to return the pointer (ptr) and output it to the console, as currently it is outputting (what I think) is the memory location...
Here is my code so far,
Any advice will be very helpful.
The structure:
struct Student{
string name;
string id;
int age;
};
The function call:
cout << "Youngest: " << youngest(student_Dynamic, sizeOf) << endl;
And the function:
Student* youngest(Student* s, int size)
{
int tempAge = 0;
int youngestAge = 100;
Student *pt = new Student;
for (int i = 0;i < size;i++)
{
tempAge = (s+i)->age;
if (tempAge < youngestAge)
{
youngestAge = tempAge;
pt->age = youngestAge;
}
}
return pt; //Here I am trying to return the pointer so that it outputs the youngest age
//to the console window...
}
UPDATE:
This question has now been answered by 'Billy Pilgrim'
Thankyou everyone for the advice!
You are returning the pointer, which is what is being printed. It should be something like:
Student * s = youngest(student_Dynamic, sizeOf);
cout << "Youngest: " << s->name << endl;
(Not sure what sizeOf is:).