Thread: help with the logic of this creating new user

  1. #1
    Registered User
    Join Date
    Jan 2008
    Posts
    79

    help with the logic of this creating new user

    i want to run a program and have it add a new user and store all the users added.

    so if i do
    Code:
    typedef struct{
    
    char *name;
    char *pword;
    
    }new;
    
    int main (int argc, char *argv[]){
    
          
    
    }

    I can create one person with new person

    then person name etc

    but what if i have people that are gonna add themselves all the time. person can only relate to one of them.

    or would i have to do new person[100] then use person[0] in which case that would be very wastefull ??

  2. #2
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,817
    Dynamic allocation (malloc/realloc) of any array of such objects, or maybe a linked list of them.
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  3. #3
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by mushy View Post
    or would i have to do new person[100] then use person[0] in which case that would be very wastefull ??
    If you have no idea how many persons there are going to be, then that will be a problematic method, because as you say there is a good chance the array will be either too big, or worse, too small.

    You want to implement a dynamic array using realloc(). The simplest way is to just do this:
    Code:
    person *array = malloc(sizeof(person)); // one element;
    // expand to 2 elements
    array = realloc(array, sizeof(person)*2));
    However, this means keeping track of the size of the array, and doing more manual allocation for the name and pword pointers. Also, resizing one element at a time is inefficient. So here's an example of a dynamic array "class" in C:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct {
    	char *name;
    	char *pword;
    } person;
    
    typedef struct {
    	person *e;	// "e" is for elements
    	int size;
    	int last;
    } dray;
    
    person *dray_add (dray*, char*, char*, int);
    void dray_grow (dray*, int);
    void dray_init (dray*);
    void dray_fpop (dray*);
    
    int main(int argc, const char *argv[]) {
    	char *names[] = { "MK27", "Bob", "unknown" },
    		*pwds[] = { "uang2Wei", "oCoo0Ju8", "ze4uap0E"};
    	dray eg;
    	int i;
    
    	dray_init(&eg);
    
    /* populate */
    	dray_grow(&eg, 3);
    	for (i=0;i<3;i++)
    		dray_add(&eg,names[i],pwds[i],0);
    
    /* add another one */
    	dray_add(&eg,"new","aetel2A8",1);
    
    /* verify */
    	for (i=0;i<eg.size;i++) 
    		printf("%10s %8s\n",eg.e[i].name,eg.e[i].pword);
    
    /* clean-up */
    	for (i=0;i<eg.size;i++) dray_fpop(&eg);
    	free(eg.e);
    
    	return 0;
    }
    
    person *dray_add (dray *dr, char *name, char *pword, int resize) {
    	person *cur;
    	if (resize) dray_grow(dr, 1);
    	dr->last += 1;
    	cur = dr->e + dr->last;
    	cur->name = malloc(strlen(name)+1);
    	cur->pword = malloc(strlen(pword)+1);
    	strcpy(cur->name,name);
    	strcpy(cur->pword,pword);
    	return cur;		/* unused in this example program */
    }
    
    void dray_grow (dray *dr, int growby) {
    	person *tmp = realloc(dr->e, (dr->size+growby)*sizeof(person));
    	if (!tmp) {
    		puts("OUT OF MEMORY!!!!!!");
    		return;
    	}
    	dr->e = tmp;
    	dr->size += growby;
    	tmp = dr->e + dr->size -1;
    	tmp->name = NULL;
    	tmp->pword = NULL;
    }
    
    void dray_init (dray *dr) {
    	dr->size = 0;
    	dr->last = -1;
    	dr->e = malloc(0);	// so e pointer is a valid address!
    }
    
    void dray_fpop (dray *dr) {
    	person *p = dr->e + dr->last;
    	if (p->name) free(p->name);
    	if (p->pword) free(p->pword);
    	(dr->last)--;
    }
    Some notes about this:

    Realloc() is an "expensive" operation, meaning it takes a lot of processor time, so you should avoid resizing by one element at a time if you can. That is the purpose of the dray_grow() -- since in the example program we know that there will be at least 3 people, we initially grow the array to that size and then fill it with dray_add(). Dray_add() has a final argument to indicate whether the array needs to grow, or if it has already been sized up. Notice that parameter is in red: it's zero when you don't need more space and non-zero when you do.

    Dray_fpop() is for convenience. Notice it does not decrease the size of the array, it just frees internal pointers for the last struct (blanking it out). The array itself must be free separately, once all the stucts are blanked.
    Last edited by MK27; 04-14-2010 at 09:08 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  4. #4
    Registered User
    Join Date
    Jan 2008
    Posts
    79
    thanks for the very good and thorough reply - its quite a bit about my head, well i mean i can kinda understand whats going on, but all these pointers confuse me and whats pointing to what etc etc, ill keep reading through it and see if i understand it.

  5. #5
    Banned
    Join Date
    May 2007
    Location
    Berkeley, CA
    Posts
    329
    Here is my attempt at the linked list option

    Code:
    /*Currently listenting to Washington is Next by Megadeth*/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX 30
    
    /*I haven't learned what a typedef is yet*/
    struct new {
      char *name;
      char *pword;
      struct new *next;
    };
    
    void add_person(struct new** );
    void printme(struct new* );
    void cleanup(struct new* );
    
    void add_person(struct new** head_ref)
    {
      char name[MAX];
      char pword[MAX];
      struct new *person = malloc(sizeof(struct new));
    
      (void)printf("Enter a name: ");
      fflush(stdout);
      /*I'm not that sure how to handle scanf() error handling
        in this case
       */
      scanf("%s", name);
      if ((person->name = strdup(name)) == NULL) {
        (void)fprintf(stderr, "Invalid name\n");
        exit(1);
      }
    
      /*Need OS specific functions to disable echoing*/
      /*Also, I don't think strdup() is  standard */
      (void)printf("Enter a pasword: ");
      fflush(stdout);
      scanf("%s", pword);
      if ((person->pword = strdup(pword)) == NULL ) {
        (void)fprintf(stderr, "Invalid password\n");
        exit(1);
      }
    
      person->next = *head_ref;
      *head_ref = person;
    }
    
    void printme(struct new* head)
    {
      struct new* current = head;
    
      printf("\n");
    
      while (current != NULL) {
        (void)printf("The name is: %s\n", current->name);
        (void)printf("The password is: %s\n", current->pword);
        current = current->next;
      }
    
    }
    
    void cleanup(struct new* head)
    {
      struct new* temp;
    
      while (head != NULL) {
        temp = head;
        head = head->next;
        free(temp);
      }
    }
    
    int main(void)
    {
      struct new* head = NULL;
      int add = 1;  
      int c; 
    
      (void)printf("Do you want to add a name? ");
      (void)printf("Enter either y|Y to enter one or n|N to exit: ");
      fflush(stdout);
    
      while( ((c = getchar()) != EOF) && add != 0) {
        switch(c)
          {
          case 'Y': case 'y':
            add_person(&head);
            break;
          case 'N': case 'n':
            add = 0;
            break;
          default:
            (void)printf("Do you want to add another person? ");
            fflush(stdout);
            break;
          }
      }
    
      printme(head);
      cleanup(head);
    
      exit(0); 
    }
    And the output...

    [cd@localhost oakland]$ gcc -Wall -Wextra bored.c -o bored
    [cd@localhost oakland]$ ./bored
    Do you want to add a name? Enter either y|Y to enter one or n|N to exit: y
    Enter a name: cd
    Enter a pasword: nope
    Do you want to add another person? y
    Enter a name: mary
    Enter a pasword: yes
    Do you want to add another person? n

    The name is: mary
    The password is: yes
    The name is: cd
    The password is: nope
    [cd@localhost oakland]$
    Last edited by Overworked_PhD; 04-14-2010 at 01:46 PM. Reason: Formatting issues. Emacs needs to die a slow and horrible death.

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by mushy View Post
    thanks for the very good and thorough reply - its quite a bit about my head, well i mean i can kinda understand whats going on, but all these pointers confuse me and whats pointing to what etc etc, ill keep reading through it and see if i understand it.
    Lemme see if I can explicate part of it further. Pointers, notation, and pointer arithmetic can be confusing at first:

    dray_add() is used to add a new struct to the array. It takes 4 arguments:
    1) the dray to add to.
    2) the name for the new person
    3) the pword for the new person
    4) an int indicating whether to allocate space. This argument is "boolean", 0 = false, anything else = true. Unless you call dray_grow() appropriately first, this should be set true.
    Code:
    person *dray_add (dray *dr, char *name, char *pword, int resize) {
    	person *cur;
    	if (resize) dray_grow(dr, 1);   /* a truth test */
    	dr->last += 1;
    	cur = dr->e + dr->last;
    On thing it's important to understand here is that the dray struct contains a count size, which is the number of structs that the array can hold currently. It also contains another sort of counter, last. That's the index of the last element in the array which actually holds data. So size should always be greater than last (if there are 10 elements and they all hold data, the index of the last one is 9, since we start counting at 0). After the first dray_grow() call in main(), size will be 3, but last will still be -1, since none of the elements (include element [0]), hold any data yet.

    cur is a pointer set to the new last element, that is, an empty struct right after the previous last stuct, which holds data. That's why dr->last is incremented (dr->last += 1) before cur is assigned:
    cur = dr->e + dr->last;
    could be written:
    cur = &(dr->e[dr->last]);
    if that makes more sense to you. The idea is that dr->e is the first element in the array (index [0]). The way pointer arithmetic works in C is that the compiler counts one unit as enough bytes to contain one element of whatever type. So if you have an array of persons, and a pointer to element [0] (for example) , and you add 3 to that pointer, it now points to element[3], since 0+3 is 3. Adding dr->last to dr->e gives us a pointer to the last element. Which since we just added one to last, that element should be empty, and we can now put data into it:

    Code:
    	cur->name = malloc(strlen(name)+1);
    	cur->pword = malloc(strlen(pword)+1);
    	strcpy(cur->name,name);
    	strcpy(cur->pword,pword);
    Finally, might as well return this pointer, which means you could do something with it if you want (altho in the demo I don't use the return value):
    Code:
    	return cur;		/* unused in this example program */
    }
    Really, this is a sort of minimized implementation of a stack or queue like data structure.

    As Overworked_PhD implies, a linked list would also be good here. But no matter what, you do need to be comfortable with pointers, notation (direct notation is used in main here, such as eg.e, indirect notation is for use with pointers, such as dr->e), and how pointer arithmetic works.

    If you're not yet, looks like you will be learning. Linked lists are great ways to do that, there are lots of tutorials around about them. Same for stacks and queues. You might also google "C dynamic array".
    Last edited by MK27; 04-14-2010 at 12:15 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Banned
    Join Date
    May 2007
    Location
    Berkeley, CA
    Posts
    329
    I'm surprised that none of the regulars didn't catch the memory leak in the original code. Anyways, I fixed the memory leak and added a bit more error checking..

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX 30
    
    struct new {
      char *name;
      char *pword;
      struct new *next;
    };
    
    void add_person(struct new** );
    void printme(struct new* );
    void cleanup(struct new* );
    char *strddup(char *);
    
    char* strddup(char *s)
    {
      char *p;
    
      p = malloc(strlen(s) + 1);
      if (p != NULL)
        (void)strcpy(p, s);
    
      return p;
    }
    
    void add_person(struct new** head_ref)
    {
      char name[MAX];
      char pword[MAX];
      struct new* person;
    
      if ((person = malloc(sizeof(struct new))) == NULL ) {
        (void)fprintf(stderr, "Invalid name\n");
        exit(1);
      }
    
      (void)printf("Enter a name: ");
      fflush(stdout);
     
      if ((scanf("%s", name)) == 1) {
        if ((person->name = strddup(name)) == NULL) {
          (void)fprintf(stderr, "Invalid name\n");
          exit(1);
        }
      } else {
        (void)fprintf(stderr, "Invalid input\n");
        exit(1);
      }
    
      /*Need OS specific functions to disable echoing*/
      (void)printf("Enter a pasword: ");
      fflush(stdout);
      if ((scanf("%s", pword)) == 1) {
        if ((person->pword = strddup(pword)) == NULL ) {
          (void)fprintf(stderr, "Invalid password\n");
          exit(1);
        }
      } else {
        (void)fprintf(stderr, "Invalid input\n");
        exit(1);
      }
    
      person->next = *head_ref;
      *head_ref = person;
    }
    
    void printme(struct new* head)
    {
      struct new* current = head;
    
      printf("\n");
    
      while (current != NULL) {
        printf("The name is: %s\n", current->name);
        printf("The password is: %s\n", current->pword);
        current = current->next;
      }
    }
    
    void cleanup(struct new* head)
    {
      struct new* temp;
    
      while (head != NULL) {
        temp = head;
        head = head->next;
        free(temp->name);
        free(temp->pword);
        free(temp);
      }
    }
    
    int main(void)
    {
      struct new* head = NULL;
      int add = 1;  
      int c; 
    
      (void)printf("Do you want to add a name? ");
      (void)printf("Enter either y|Y to enter one or n|N to exit: ");
      fflush(stdout);
    
      while (((c = getchar()) != EOF) && add != 0) {
        switch(c)
          {
          case 'Y': case 'y':
            add_person(&head);
            break;
          case 'N': case 'n':
            add = 0;
            break;
          default:
            (void)printf("Do you want to add another person? ");
            fflush(stdout);
            break;
          }
      }
    
      printme(head);
      cleanup(head);
    
      exit(0); 
    }
    And the output...

    [cd@localhost oakland]$ gcc -Wall -Wextra -Wshadow -g bored.c -o bored
    [cd@localhost oakland]$ ./bored
    Do you want to add a name? Enter either y|Y to enter one or n|N to exit: y
    Enter a name: cd
    Enter a pasword: nope
    Do you want to add another person? y
    Enter a name: mary
    Enter a pasword: yes
    Do you want to add another person? y
    Enter a name: joe
    Enter a pasword: yes
    Do you want to add another person? n

    The name is: joe
    The password is: yes
    The name is: mary
    The password is: yes
    The name is: cd
    The password is: nope
    [cd@localhost oakland]$
    Last edited by Overworked_PhD; 04-14-2010 at 09:37 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How to limit user input to a certain number of digits?
    By NewbGuy in forum C Programming
    Replies: 7
    Last Post: 05-08-2009, 09:57 PM
  2. timed user input
    By sainiabhishek in forum C Programming
    Replies: 4
    Last Post: 04-01-2009, 11:59 AM
  3. creating a struct based on user input
    By rodrigorules in forum C Programming
    Replies: 1
    Last Post: 09-15-2005, 06:16 PM
  4. Creating a user defined number of variables
    By beanroaster in forum C++ Programming
    Replies: 10
    Last Post: 09-14-2005, 03:53 PM
  5. Creating a user account...
    By xiao guang in forum Tech Board
    Replies: 2
    Last Post: 07-14-2003, 06:49 AM