0
typedef struct Calendar {
    int day;
    int month;
    int year;
}Calendar;

typedef struct Person {
    char name[40];
    int age;
    float salary;
    Calendar birth;
}Person;
void add(Person * a, int * tam);

If I want to pass structure as argument and modify its original value I have to pass as pointer, but a array of structure shouldn't be passed as pointer of a pointer like a array of array? Why the compiler works with a pointer and gives error with a pointer of a pointer?

for (int i = 0; i < *tam + 1; i++)
{
        printf("Insert your name:\n");
        fgets(name, 30, stdin);
        *a[i].name = name;
}

The last line gives warning C4047: char differs in levels of indirections. If I remove '*' it doesn't compile. How do I correctly access a array member from a array of structure?

EDIT 1: To clarify here is the full add() function and main() function.

void add(Person * a, int * tam) {

    char name[30];
    int op = 0;
    int c, day = 0, month = 0, year = 0, Calendar = -1;
    float sal = -1;

    for (int i = 0; i < *tam + 1; i++)
    {
        printf("Insert your name:\n");
        fgets(name, 30, stdin);
        *a[i].name = name;

        while ((c = getchar()) != '\n' && c != EOF)
            ;
        do
        {
            printf("Insert %s's age:\n", name);
            scanf("%d", &op);
            while ((c = getchar()) != '\n' && c != EOF)
                ;
            if (!op)
                printf("invalid age!\n");
            else
                a[i].age = op;
        } while (op <= 0);
        do
        {
            printf("Insert %s's salary:\n", name);
            scanf("%f", &sal);
            while ((c = getchar()) != '\n' && c != EOF)
                ;
            if (sal < 0)
                printf("Invalid salary!\n");
            else
                a[i].salary = sal;

        } while (sal < 0);
        do
        {
            printf("Insert %s's date birth\n", name);
            printf("Day:\n");
            scanf("%d", &day);
            while ((c = getchar()) != '\n' && c != EOF)
                ;
            printf("Month:\n");
            scanf("%d", &month);
            while ((c = getchar()) != '\n' && c != EOF)
                ;
            printf("Year:\n");
            scanf("%d", &year);
            while ((c = getchar()) != '\n' && c != EOF)
                ;

            if ((day > 0 && day < 31) && (month > 0 && month < 12) && (year > 0))
            {
                Calendar = 1;
                a[i].birth.day = day;
                a[i].birth.month = month;
                a[i].birth.year = year;
            }
            else
                printf("Invalid calendar!\n");

        } while (Calendar != 1);

    }
    *tam++;
}

main() {
    Person book[10];
    int c, tam = 0;
    char op, out = 0;
    while (!out)
    {

        do
        {
            printf("1: Insert a profile\n");
            printf("2: Change a profile\n");
            printf("3: Erase a Profile\n");
            printf("4: Show all profiles\n");
            printf("5: Search\n");
            printf("0: Exit\n");
            scanf("%c", &op);
            while ((c = getchar()) != '\n' && c != EOF)
                ;

        } while (op < '0' || op > '5');

        switch (op)
        {
        case '1':
            add(book, &tam);
            break;
        case '2':

            break;
        case '3':

            break;
        case '4':
            read(book, &tam);
            break;
        case '5':

            break;
        case '0':
            out = 1;
            break;
        default:
            break;
        }

    }

}
raijin
  • 148
  • 1
  • 8
  • What is `tam`? Maybe a helpful name would help understand what you want to do. Also serves as documentation for the code. `a` is also a bad name for `struct Person` I think – alx - recommends codidact Feb 20 '19 at 00:39
  • You remove the '\*'. But then you have a *different* problem: whole arrays such as `a[i].name` cannot be assigned to. In that particular case, perhaps you want `strcpy()`. – John Bollinger Feb 20 '19 at 00:40
  • Please show the full function that contains the for loop. Is it supposed to be part of `add()`? – alx - recommends codidact Feb 20 '19 at 00:42
  • @CacahueteFrito yes the `for` loop is inside the `add()` function, and `*tam` variable is just to count the size of the structure array – raijin Feb 20 '19 at 00:55
  • @JohnBollinger probably you mean `strncpy()`. Usually you should avoid `strcpy()` – alx - recommends codidact Feb 20 '19 at 00:56
  • @JohnBollinger If I have to modify a array member the way is strncpy()? – raijin Feb 20 '19 at 00:59
  • Do you mean something like this? `void add(int n, struct Person a[n]);` – alx - recommends codidact Feb 20 '19 at 00:59
  • @CacahueteFrito I mean I dont understand why not `void add (Person ** a, int * size)` – raijin Feb 20 '19 at 01:03
  • A) why do you want `tam` to be a pointer? B) why do you want to use pointers instead of an array for `a`, if you are trying to deal with an array? – alx - recommends codidact Feb 20 '19 at 01:09
  • @CacahueteFrito `tam` is the index that will be passed to main. B) dont know if this is bad programming habit, but wont i pass the adress either way? `void something( int a[])` or `void something(int * a)` – raijin Feb 20 '19 at 01:20
  • I still think you want this `void add(int tam, struct Person a[tam]);` and then `strncpy(a[i].name, name, BUFF_SIZE - 1)` – alx - recommends codidact Feb 20 '19 at 01:25
  • Yes, in the end it is the same (not in arrays with more than 1 dimension, but true in this case), but it is a lot more clear, and you wouldn't have had many of this problems if you had used the array form. – alx - recommends codidact Feb 20 '19 at 01:34
  • How can it be the index and the size at the same time? – alx - recommends codidact Feb 20 '19 at 01:36
  • @CacahueteFrito srry `tam` is the array size not index – raijin Feb 20 '19 at 01:44
  • Are you modifying `*tam` inside the function? If not, you shouldn't use a pointer. Also, why are you iterating until `i < *tam+1` and not `i < *tam`? You will be accessing one element outside the array size. – alx - recommends codidact Feb 20 '19 at 01:50
  • @CacahueteFrito I edit the question to clarify the current situation with `main()` and `add()` . I declared `tam = 0` thats why i need `for(int i = 0; i < *tam +1;i++)` and it isnt complete yet, because my confusion with structures. – raijin Feb 20 '19 at 02:15
  • Problems: A) You have an `int` named `Calendar`, and also a `typedef struct Calendar Calendar;` You should rename the former to `int calendar`. – alx - recommends codidact Feb 20 '19 at 08:55
  • B) If you are not very confident with `struct`s and are still learning them, I suggest that you start without using `typedef`. It only serves to obscure the `struct`. – alx - recommends codidact Feb 20 '19 at 08:57
  • C) You should pass the maximum size of the array, so that `add()`doesn't try to write more than it, and to keep track of the number of elements used, another variable (by ref): `void add(size_t n, struct Person a[n], int *tam);` – alx - recommends codidact Feb 20 '19 at 09:00
  • D) Please do NOT declare `main()` like that. First of all, the implicit `int` is deprecated (read this: https://stackoverflow.com/questions/13935104/what-if-i-omit-the-return-type-of-main-function-in-c). There are **only** 2 portable forms accepted in standard: `int main(void)` and `int main(int argc, char *argv[])` (read this: https://stackoverflow.com/a/2108208/6872717). Other implementation defined prototypes for `main()` may be accepted, but you shouldn't rely on them without a good reason. – alx - recommends codidact Feb 20 '19 at 10:10
  • E) I guess you want your `add()` function to only add one element to the array, so I don't see why you need the `for` loop. I think you may want `strncpy(a[*tam].name, name, n - 1); (*tam)++;` and get rid of the `for` loop. – alx - recommends codidact Feb 20 '19 at 10:22
  • @CacahueteFrito you are right. the `for` loop is absolutely unnecessary, thanks. – raijin Feb 20 '19 at 11:09
  • `scanf("%c", &op);`: You should add a space just before the `%c`: `scanf(" %c", &op);` (read this: http://man7.org/linux/man-pages/man3/scanf.3.html) – alx - recommends codidact Feb 20 '19 at 11:17
  • @CacahueteFrito, regarding "Usually you should avoid strcpy()": no, I do not accept that as a general rule. It is a question of your requirements and what you can rely upon about your data. Whereas `strcpy()` may overrun array bounds if you are not careful, `strncpy()` may leave the destination unterminated, setting the stage for a *future* buffer overrun (and it may itself overrun the buffer, too, if you don't get its length right). In practice, the failure behavior attending `strncpy()` can be worse than the failure behavior attending `strcpy()`. – John Bollinger Feb 20 '19 at 14:05
  • The `-1` is exactly for that purpose: It means "`strncpy()` please do leave one last `char` to write a `'\0'` there". – alx - recommends codidact Feb 20 '19 at 15:24
  • Of course, `strlcpy()` is better, but it isn't standard (I hope it is in the future). – alx - recommends codidact Feb 20 '19 at 23:35

1 Answers1

0

The prototype for your function should make clear that it works with an array:

void add(size_t n, struct Person arr[n], int *tam);

This way, you know the size of the array (n), and you can make sure that you don't try to access beyond that size, which would end up in a segmentation fault if you are lucky (read this: How dangerous is it to access an array out of bounds?).


The for loop would then loop over the array:

for (int i = 0, *tam = 0; i < n; i++, (*tam)++) {
    printf("Insert your name:\n");
    fgets(name, 30, stdin);
    /* a[i].name = name; */
}
(*tam)++;

You need the parentheses around (*tam)++ because of operator precedance (read this: How to increment a pointer address and pointer's value?).


As for the access to the structs inside the array, you have to know that you are accessing an array of structs; that is just many structs put together, as an array of ints would be:

a is an array of structs.

a[i] is the ith struct in the array.

a[i].name is the element name in the struct a[i].


To copy a string into another, you have to copy it element by element, because internally they are an array of chars, and because of the way arrays work in C, you can not copy them with = but by iterating and copying element by element.

In case of normal arrays, function memcpy() does that for you (read this: http://man7.org/linux/man-pages/man3/memcpy.3.html).

When you are dealing with strings, the function you want to use to copy them is strncpy() (read this: http://man7.org/linux/man-pages/man3/strncpy.3p.html).

The correct usage of strncpy() is explained in this answer: https://stackoverflow.com/a/14067026/6872717

Basically you would use it like this:

strncpy(a[i].name, name, n - 1);

so your for loop should look like this:

for (int i = 0, *tam = 0; i < n; i++, (*tam)++) {
    printf("Insert your name:\n");
    fgets(name, 30, stdin);
    strncpy(a[i].name, name, 40 - 1);
    a[i].name[40 - 1] = '\0';
}
(*tam)++;

This is more a comment, and I may remove it later if it isn't what you want, but here I can format better the code:

I am not sure why the for loop is there, but for the context, I guess this function is meant to only write into one struct in the array. So maybe instead of the for loop you may want something like this:

if (*tam = (n - 1))
    goto err_array_full;
if (*tam > (n - 1)  ||  *tam < 0)
    goto err_invalid_index;

printf("Insert your name:\n");
fgets(name, 30, stdin);
strncpy(a[*tam].name, name, 40 - 1);
a[*tam].name[40 - 1] = '\0';
(*tam)++;

Final note: I used 40 in strncpy() because you used 40 to create the array, but that should be a macro which you would use in both places so that when you change the size of the array you don't forget to change it also in strncpy(), which would introduce a bug. With a macro you are safe because you only change one number and all your code is updated.

  • About the prototype if I pass a single structure as argument to modify it value it would be `void add(Person * a)`. What I don't understand is why when it is an array of structure the compiler gives error with this `void add(Person *a[])`, but works with this instead `void add(Person a[])` or `void add(Person * a)`. An array of array would be `void func(int a[][])` or `void func(int *a[])`. Doesn't both, structure or array, pass arguments as references? What am I missing? – raijin Feb 20 '19 at 11:23
  • No, if you pass a `struct` your are really passing a copy of it (read this answer: https://stackoverflow.com/a/13555817/6872717) – alx - recommends codidact Feb 20 '19 at 11:45
  • So yes, your new prototype `void add(struct Person *a)` would work, if you passed a pointer to a struct like this: `add(&(book[tam]));` but then you would need to increment `tam` in main. Also you would need to use it differently inside of `add()`: `strncpy(a->name, name, 40 - 1);` (which is short for `strncpy((*a).name, name, 40 - 1);`) (read this: https://stackoverflow.com/questions/13366083/why-does-the-arrow-operator-in-c-exist/13366168). – alx - recommends codidact Feb 20 '19 at 11:48
  • Read better this one about the `->` operator: https://stackoverflow.com/a/2575050/6872717 – alx - recommends codidact Feb 20 '19 at 11:56
  • Passing a `struct` to a function behaves like passing an `int`. It just makes a copy of it, and if you want to modify it you need to pass a pointer to it. Arrays are the only weird things that behave differently when passed to a function: instead of passing a copy, they pass a pointer to its first element, so that you can modify them without explicitly using pointers. And therefore you need to use `const` if you want to pass an array that cannot be modified. – alx - recommends codidact Feb 20 '19 at 12:03
  • So, if you want to pass a copy of a `struct`, you use: `void add(struct Person a);` and use it like this: `x = a.age;` (note that this is a copy, so usually you would only read from it). If you want to pass a pointer to a single `struct`, you use: `void add(struct Person *a);` and use it like this: `a->age = 7;`. And if you want to pass an array of `struct`s, you use: `void add(size_t n, struct Person a[n]);` and use it like this: `a[i].age = 7;` – alx - recommends codidact Feb 20 '19 at 12:12
  • Usually when passing `struct`s to a function you don't want to pass a copy of them even if you only want to read from them, because that would use more RAM and would be slow; The solution is to pass a `const struct Struct_Tag *some_struct` so that you pass a pointer to the original struct, but you don't allow the function to write into it. – alx - recommends codidact Feb 20 '19 at 12:18
  • This: `struct Person *a[]` is an array of pointers to `struct`s. You clearly do NOT have an array of pointers to `struct`s. The whole thing of considering that arrays are pointers is misleading you. Arrays decay to pointers, but that shouldn't bother you, and `struct`s have nothing to do with pointers. – alx - recommends codidact Feb 20 '19 at 12:41