0

What i am trying to do ?

I am trying to allocate memory to a char type two dimensional array which is declared as a member of a structure.

What is the problem?

I am not sure how to do the same.

My code:

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

  typedef struct information {
       int id ;
       char **name ;
  } info ;
  
  void main () {
       info s1 ;
       int i , size ;
       printf ("\n Enter max size of students name : ") ;
       scanf ("%d",&size) ;
       s1.name = ( char** ) malloc ( size * sizeof ( char* ) ) ;
       for ( i = 0 ; i < size ; i++ )
            s1.name [ i ] = ( char** ) malloc ( size * sizeof ( char* ) ) ;
       printf ("\n Enter Id : ") ;
       scanf ("%d",&s1.id) ;
       printf ("\n Enter Name : ") ;
       scanf ("%s",s1.name) ;
       getche () ;
  }

Is this right? Am I allocating it correctly?

I also want to know why we don't use & when getting input in the form of a string. For example, in my code at the third from bottom line, why can't I write this: scanf ("%s",&s1.name); ?

Community
  • 1
  • 1
Ahtisham
  • 9,170
  • 4
  • 43
  • 57
  • You do it exactly the same way you would declare memory for it were it not a member of a struct. You do not need a double-pointer, `char *name;` is fine, then `s1.name = malloc ( size * sizeof *(s1.name) );` – David C. Rankin Jan 18 '16 at 03:20
  • @DavidC.Rankin Didn't get you – Ahtisham Jan 18 '16 at 03:22
  • why do so many people cast malloc – M.M Jan 18 '16 at 03:26
  • @M.M because its common i can use calloc too but i was raised with malloc that is why i use it most of the time ;) – Ahtisham Jan 18 '16 at 03:28
  • @M.M it happens, because they aren't use a proper compiler, maybe a C++ one. – Michi Jan 18 '16 at 04:13
  • Possible duplicate of [Correctly allocating multi-dimensional arrays](http://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin Feb 14 '17 at 15:08
  • @Lundin I asked it 1 year ago and the guy you are referring to in the link asked it 6 days ago. and they are different question – Ahtisham Feb 14 '17 at 15:12
  • Yes I know. I just wanted to make you aware that the code in this question has fundamental problems, which the posted answers don't help explaining. – Lundin Feb 14 '17 at 15:17
  • @Lundin what do you think about this one : http://stackoverflow.com/questions/42228517/getting-attributeerror-while-registering-my-model-to-django-admin ? – Ahtisham Feb 14 '17 at 15:21
  • 1
    What does that have to do with anything? There's nothing wrong with your question as such. – Lundin Feb 14 '17 at 15:26
  • @Lundin no i mean how was the question structured ? – Ahtisham Feb 14 '17 at 15:26
  • 1
    @AhtishamChishti Looks good to me, though you should avoid posting pictures of error messages. It is better to include the error message as text, if possible. If not, a picture is better than nothing. – Lundin Feb 14 '17 at 15:28

1 Answers1

2

As explained in the comment, you will allocate/free exactly the same as you would if the pointer were not a member to a struct. While it appears you do not need a char **name (pointer-to-pointer-to-char) unless one id is associated with many names. Simply declare a pointer to name (e.g. char *name;) would be sufficient to provide a one-to-one relationship with id.

Putting those pieces together, and adjusting the format-specifiers for scanf to read/discard the trailing newlines after the read of size, you could do something like:

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

typedef struct information {
    int id ;
    char *name ;
} info ;

int main (void) {

    info s1;
    int size;

    printf ("\n Enter max size of students name : ");
    scanf ("%d",&size);

    s1.name = malloc (size * sizeof *s1.name);

    printf ("\n Enter Id : ");
    scanf ("%d%*c", &s1.id);

    printf ("\n Enter Name : ");
    scanf ("%[^\n]%*c", s1.name);

    printf ("\n You entered, id: %d, name: %s\n\n", s1.id, s1.name);

    free (s1.name);

    // getche () ;
    return 0;
}

Example Use/Output

$ ./bin/struct_stdnm

 Enter max size of students name : 29

 Enter Id : 231

 Enter Name : Alfred Q. Myres

 You entered, id: 231, name: Alfred Q. Myres

Let me know if you have further questions or further needs.

Note: if you need an array of id and names, then you need an array of struct information, not a double-pointer to name -- if I interpret what makes sense for what you are doing correctly.


Protect Against Input Beyond Allocation

As noted in the comments below, scanf is not optimal for reading s1.name because it does not allow a maximum field width by variable. To properly protect against against writing beyond the end of your allocation, you should use fgets and limit your read to size:

printf ("\n Enter Name : ");
fgets (s1.name, size, stdin);

Example

$ ./bin/struct_stdnm

 Enter max size of students name : 5

 Enter Id : 123

 Enter Name : Johnny Jones

 You entered, id: 123, name: John

Validation Required on All Allocation & Input

Regardless of whether you stick with scanf for input or use fgets, you must validate all allocations and input to insure you actually allocate the memory you intend to use, and you actually read what you think you have read. Think of this is just another layer of completeness for your code. While learning it may be convenient to use examples without complete validation, you should never consider your code complete until you provide sufficient checks for all allocations and input. Here is an update showing minimal validation checks for your code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// # include <conio.h>

typedef struct information {
    int id ;
    char *name ;
} info ;

int main (void) {

    info s1;
    int size;
    size_t len;

    printf ("\n Enter max size of students name : ");
    if (scanf ("%d",&size) != 1) { /* validate converison */
        fprintf (stderr, "error: conversion of size failed.\n");
        return 1;
    }
    if (size < 1) { /* validate size */
        fprintf (stderr, "error: invalid size entered.\n");
        return 1;
    }

    /* always validate allocations */
    if (!(s1.name = malloc (size * sizeof *s1.name))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    printf ("\n Enter Id : ");
    if (scanf ("%d%*c", &s1.id) != 1) { /* validate converison */
        fprintf (stderr, "error: conversion of size failed.\n");
        return 1;
    }

    printf ("\n Enter Name : ");
    if (fgets (s1.name, size, stdin)) {
        len = strlen (s1.name);

        /* validate all characters fit within 'size' allocation */
        if (len + 1 == (size_t)size && (s1.name)[len-1] != '\n')
            printf (" warning: name exceeded allocation.\n");

        /* remove trailing newline from name */
        if (len && (s1.name)[len-1] == '\n')
            (s1.name)[--len] = 0;
    }
    else {
        fprintf (stderr, "error: name entry failed.\n");
        return 1;
    }

    printf ("\n You entered, id: %d, name: %s\n\n", s1.id, s1.name);

    free (s1.name);

    // getche () ;
    return 0;
}

This also flags any time you have not allocated enough space to hold the entire name entered:

Short Allocation Example

$ ./bin/struct_stdnm

 Enter max size of students name : 5

 Enter Id : 123

 Enter Name : Johnny Jones
 warning: name exceeded allocation.

 You entered, id: 123, name: John

Let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • scanf ("%d%*c", &s1.id); what is the intension of using " %d%*c" ?? how does it work ?? and what does it mean ?? – Ahtisham Jan 18 '16 at 03:44
  • Also i wanna know when i enter size of string as 5 and enter a name of student which has more than 5 characters then why doesn't it show error ?? why it works without any error or warning ?? shouldn't it at least display only the first five characters but instead it displays exactly what the user enters as a student name no matter what size he/she enters it doesn't matter why this happens & how to fix it ??? – Ahtisham Jan 18 '16 at 03:53
  • `scanf ("%d%*c", &s1.id);` the `%*c` say to read `1-char` and discard (which is the trailing `'\n'`). It is done so the `'\n'` that remains in the *input-buffer* (`stdin`) is not taken as input to your next *character* conversion (like `%s`). It is also not added to the match-count returned by `scanf`. – David C. Rankin Jan 18 '16 at 03:54
  • 1
    That is a check you should make. Reading with `scanf` is not-optimal here. Why? because you cannot use a variable as a width-modifier in the format-specifier. Ideally you would want `%4[^\n]%*c` if you specified `5` as size. What would be better in your case would be to use `fgets` for your read of `name`. That way you could use `fgets (s1.name, size, stdin)` and limit your read to the size of your allocation. (the technical reason it doesn't explode is that `malloc` will allocate a block that is of a minimum size for your hardware for `s1.name`. That is likely greater than `5`. – David C. Rankin Jan 18 '16 at 03:57
  • @AhtishamChishti see the addition to the answer showing the use of `fgets` to read `s1.name`. – David C. Rankin Jan 18 '16 at 04:06
  • can you please send me your facebook id or twitter whatever you are using on my mail which is " Ahtishamshafi9906@gmail.com" please i would need your help in most of the times as i'm a beginner in programming please do send me your id so that i can consult you for further questions btw thanks alot for your kind & wonderful replies :) – Ahtisham Jan 18 '16 at 06:11
  • and using "%*c" can we just use " %c" notice the space in front of %c that way we can also discard the trailing "\n" ( newline character ). thanks again for your kind replies :D – Ahtisham Jan 18 '16 at 06:16
  • You really need to `'*'` in the format-specifier. It is the "*assignment suppression operator*" for `scanf`. If you use `' %c'` you will run the risk of the conversion failing if there is no `' '` after the prior input ...and... you will be adding `1` to the *match-count* returned by `scanf`. You "can" do it that way, you must insure your input matches and you must adjust the return count accordingly. – David C. Rankin Jan 18 '16 at 07:18