-2

This was a question in a C Programming assignment given to me as a part of last week's assessment. Requesting everyone to kindly explain what needs to be done and how it needs to be done. Kindly note that even though this particular question was graded, the deadline for it has already crossed and I can't make any edits. I am just asking on this forum to know where I went wrong and didn't understand

I'd also appreciate if someone explains the program flow as I am really clueless.

Question statement

This was the template code given to for further updation

#include<stdio.h>
#include<stdlib.h>

struct Student
{
    char name[50];
    struct Student *next;
};

struct Student* studentList()
{
    //Hint:
    // Allocate memory for new struct student
    // Read name and store inside name field of new struct
    // Store address of new struct into the next field of the previous student
    // Write solution code below
}

int main()
{
    struct Student *head;
    head = studentList();
    while (head != NULL)
    {
        printf("%s\n",head->name);
        head = head->next;
    }
}

This is what I tried (I am entering the code that was ought to be entered after the comments):

struct Student* student;
student = (struct Student*)malloc(sizeof(struct Student));
scanf("%s", student->name);
student->next = student + sizeof(struct Student);

Now, I am not running into compile time errors but the output is not coming as desired.

4
  • @Computable this is why I asked to do in the staging group, but now it is too late I think. The OP is very new in programming Commented Aug 10 at 13:45
  • 1
    It is not practical for us to read the text from a picture, next time please extract the text parts to give them as text in your question, and only give pictures parts as attached picture. Commented Aug 10 at 14:16
  • char name[50]; --> What is a reasonable length limit on person "Name" fields?. I'd go with char * and allocate the name space. Commented Aug 10 at 19:43
  • scanf("%s", student->name); fails to read a name that contains a space. Many surnames include prefixes that may or may not be separated by a space Commented Aug 10 at 19:49

2 Answers 2

4

For me someone accepted your question too early in the staging group, I preferred to let you thinking more, and then learn more by yourself.

There are some problems in your proposal, in order from top to bottom :

  • In main you do not free allocated memory, even the program stops just after I encourage you to not make memory leaks. Anyway you can replace my main by yours.

  • You do not need to cast the result of malloc, student = malloc(sizeof(struct Student)); is enough, but warning you get 0 if there is not enough memory.

  • scanf("%s", student->name); is very dangerous because in case the read name is longer than 49 characters you will write out of the array, never trust on inputs, limit the read size.

  • student + sizeof(struct Student) does not value the address of the next Student which is student + 1, but in both case you get an address you cannot use. You need to allocate a new Student each time, as you did for the first.

I suppose the definition of Student is imposed and cannot be changed. Here a proposal :

#include <stdio.h>
#include <stdlib.h>

struct Student
{
    char name[50];
    struct Student * next;
};

struct Student* studentList()
{
  struct Student * head = 0; /* the end of the list */
  struct Student ** tail = 0; /* &next of the last student to add at the end of the list */
  
  for (;;) {
    struct Student * student = malloc(sizeof(struct Student));
    
    if (student == 0) {
      fputs("not enough memory, stop to read\n", stderr);
      return head;
    }
    
    /* read up to 49 char to have place for final 0 */
    if (scanf("%49s", student->name) != 1) {
      fputs("cannot read student name\n", stderr);
      free(student);
      return head;
    }
    
    if (head == 0) {
      /* first read student */
      head = student;
    }
    else {
      /* add the new student at the end of the list, tail cannot be 0 */
      *tail = student;
    }
    
    /* update the tail pointer */
    tail = &student->next;
    
    /* currently the last student */
    student->next = 0;
    
    for (;;) {
      char yesno;
      
      if (scanf(" %c", &yesno) != 1) { /* <space>%c to bypass possible spaces */
        fputs("cannot read 0 or 1, force 0\n", stderr);
        return head;
      }
      if (yesno == '0')
        return head;
      if (yesno == '1')
        break;
      fputs("enter 1 to continue to add new student, else 0\n", stderr);
    }
  }
}

int main()
{
  struct Student * student = studentList();
  
  while (student != 0) {
    struct Student * next = student->next;
    
    puts(student->name);
    free(student);
    student = next;
  }
  
  return 0;
}

Notice that supposes the name of students are not composed, so DonaldDuck rather than Donald Duck for instance.

The program indicates when something unexpected is read, but :

  • in case the read name is longer than 49 characters, the name is cut and the remainder will be available when 0 or 1 is supposed to be enter

  • when 0 or 1 is expected, to enter for instance 1azeEnter rather than 1Enter let aze to name the next student.

To avoid that the best is to flush the characters up to the newline after reading the name and 0 or 1. Notice fflush(stdin) is not guaranty and must not be used. So define an additional function making a loop using getchar() and isspace(<read char>), and also managing the EOF case, you will be able to use it in your other programs.

As you see I do not read 0 or 1 using scanf("%d", &anInt) even that seems to be the easier solution, this is because there are some problems reading a number with scanf, and the case with only 0 or 1 is trivial. I do scanf(" %c", &yesno), thanks for the space before %c that allows to bypass the possible initial spaces (space, newline, tab ...) like this is the case for %s, and then an other solution was for instance char yesno[2]; ... scanf("%s", yesno) ... and use *yesno rather than yesno


Compilation under Linux and 'standard' execution:

bruno@raspberrypi:/tmp $ gcc -g -Wall student.c 
bruno@raspberrypi:/tmp $ 
bruno@raspberrypi:/tmp $ ./a.out
Gargi
1
  bruno
 1
foo
0
Gargi
bruno
foo
bruno@raspberrypi:/tmp $ 

to show why bypassing the rest of the line after reading names 0/1 would be better, my inputs are in bold :

bruno@raspberrypi:/tmp $ ./a.out
Gargi Chaturvedi
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
enter 1 to continue to add new student, else 0
1
azertyuiopazertyuiopazertyuiopazertyuiopazertyuiop
enter 1 to continue to add new student, else 0
1
bruno
1oupps
0
Gargi
azertyuiopazertyuiopazertyuiopazertyuiopazertyuio
bruno
oupps
bruno@raspberrypi:/tmp $

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you so much for your response. Unfortunately, no edits are allowed in the main function. We're only allowed to add on to the studentList() function i.e., write our code below the comments. I'd appreciate if you could curtail your response to that
you can use your main unchanged and not use mine, the program will works in the same way, except the free will not be done, that it all
in that case may be remove free(student); from studentList() even this is quite sad and a bad habit to let memory leaks ;-)
It seems to me that if (tail != 0) *tail = 0; is an unnecessary operation. The pointer *tail at this location is always NULL.
ah yes, this was required in an early version before I publish but not now. I edit to correct. You have lynx eyes;-)
-1

If you do not want to pass head as parameter, then you can only have one list and head will have to be a global variable

struct Student
{
    char name[50];
    struct Student *next;
};

struct Student *head;

struct Student* studentList(const char *name)
{
    struct Student *st = malloc(sizeof(*st));

    if(st)
    {
        st -> next = NULL;
        strncpy(st -> name, name, sizeof(st -> name) - 1);
        if(!head) head = st;
        else
        {
            struct Student *el = head;
            while(el -> next) el = el -> next;
            el -> next = st;
        }
    }
    return st;
}

void printList(void)
{
    struct Student *lp = head;
    while(lp) 
    {
        printf("Name: '%s'\n", lp -> name);
        lp = lp -> next;
    }
}

void freeList(void)
{
    struct Student *lp = head;
    while(lp) 
    {
        struct Student *cp = lp;
        lp = lp -> next;
        free(cp);
    }
}

int main()
{
    studentList("john");
    studentList("jim");
    studentList("Ann");
    studentList("Spok");

    printList();
    freeList();
}

I'd also appreciate if someone explains the program flow as I am really clueless.

Your main function flow makes no sense so there is nothing to explain.

4 Comments

Thank you so much for your response. Unfortunately, no edits are allowed in the main function. We're only allowed to add on to the studentList() function i.e., write our code below the comments. I'd appreciate if you could curtail your response to that
I don't like to make negative comment to an answer when I answered myself to the OP question, but your proposal do not read the name nor 0/1 as required by the statement (picture)
I do not view external pictures. OP could scan it and as AI to OCR it.
ah ah this is a little easy no ;-) for the decharge of the OP there is a picture in the picture, and it was better to (partially) replace it in the stage group, but ....

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.