-3

I have a struct call Client , and a function initClient that create such struct.

Now I want to implement a function that creates a table[20000] of this struct, but I couldn't figure out the right syntax of the returned data. Is it Client** or Client[] ?... This is my code: client.h:

 struct sClient
 {
     int num_tel;
     int nbr_appel;
     int cout;
  };

  typedef struct sClient Client;
  Client * initClient(int num_tel, int nbr_appel,int cout);
  Client *createData();

and main.c:

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

Client* initClient(int num_tel, int nbr_appel,int cout){
    Client *c = (Client *) malloc(sizeof(Client)); 
    c-> num_tel =num_tel;
    c-> nbr_appel= nbr_appel; 
    c-> cout= cout;

    return c; 
}

Client* createData(){
    // Number of clients
    #define NBCLIENT 20 
    Client* tab[NBCLIENT];
    Client *c; 
    int i,numeroTel,prixAppel;

    for(i=0;i<NBCLIENT;i++)
    {
        // Generation of a random num_tel
        numeroTel = 600000000+(rand() % NBCLIENT);   
        prixAppel = (rand() % 400)+1; 
        c=initClient(numeroTel,1,prixAppel);
        tab[i]=c;      
    }   
    return tab;    
}

int main(int argc, char *argv[])
{
    createData();
    system("PAUSE");    
    return 0;
}
dragosht
  • 3,237
  • 2
  • 23
  • 32
Ya Ssou
  • 25
  • 5
  • 3
    In `createData` the variable `tab` is a *local variable*, it will cease to exist once the function returns. Returning a pointer to its first element (which is what you do) will make that pointer immediately invalid. – Some programmer dude Jan 10 '18 at 07:21
  • I have tried to return a different variable instead of tab that point to its first element , but it didn't work – Ya Ssou Jan 10 '18 at 07:27
  • The solution is dynamic allocation. – Some programmer dude Jan 10 '18 at 07:29
  • Actually my assignement says that the return type must be Client[] , is that even possible !! – Ya Ssou Jan 10 '18 at 07:30
  • @Someprogrammerdude well in case of a predefined array size, there are alternatives... https://stackoverflow.com/a/4581061/5265292 – grek40 Jan 10 '18 at 07:31
  • @YaSsou A return type of `Client[]` sounds like trouble if you ask me... – grek40 Jan 10 '18 at 07:35
  • Well, yes it is a predifined size, @grek40 yes it did cause me troubles in the other hands when I used Client* some problems where solved but still not working – Ya Ssou Jan 10 '18 at 07:37
  • Since you have a specific assignment, the exact wording is important... are you asked to return `Client[]`, an array of clients, an array of client pointers or something different? – grek40 Jan 10 '18 at 07:39
  • An array of 20000 Clients to be exact – Ya Ssou Jan 10 '18 at 07:39
  • Is `initClient` with this function signature your invention or is it part of your assignment requirements? Some related question on array of X: https://stackoverflow.com/questions/9995564/function-with-return-type-array-in-c – grek40 Jan 10 '18 at 07:41
  • The assignment requires this signature: struct Client[] createData() ; – Ya Ssou Jan 10 '18 at 07:42
  • For initClient yes it is in part of my assignment requirements – Ya Ssou Jan 10 '18 at 07:45
  • The only way to return a local array (which is not `malloc()`ed) safe from a function, I see, is to make it `static`. `static` extends the life-time of the local variable over the whole program life-time. Hence access after return of function is OK. On the other hand, every call of the function returns the one-and-only same instance of variable (with the same storage). – Scheff's Cat Jan 10 '18 at 07:54
  • @YaSsou I mean, is this specific function signature for `initClient` required (with return type `Client*` and parameter types as 3 integers) or is it only generally required to implement some function with name `initClient`? If you are not specific down to the last detail, then you can't get good answers. If it wasn't a homework question you could get an answer that completely changes the function signatures, but since you need them, you have to explain them in detail. – grek40 Jan 10 '18 at 07:56
  • No it is the specific signature – Ya Ssou Jan 10 '18 at 07:59
  • Arrays may not be passed as arguments nor returned - they decay to a pointer. Returning a local (non-`static`) array is [UB](https://stackoverflow.com/a/4105123/1505939) as it allows access to something for which life-time has ended. This changes if the array is wrapped in a `struct` (like demonstrated on [**ideone**](https://ideone.com/y1wSgB)). **BUT**, this means copying the array (bad performance). There was a reason why K&R decided not to support arrays for arguments/return. (May be, they even considered the trick with the `struct` as "back-door".) – Scheff's Cat Jan 10 '18 at 07:59
  • I see **, I just couldn't get it right :/ – Ya Ssou Jan 10 '18 at 08:11

1 Answers1

1

According to what has been discussed in the comments (and considering that I gave some probably confusing hints) I want to show the (only) solution I see to match all requirements:

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

typedef struct { int num_tel, nbr_appel, cout; } Client;

Client* initClient(int num_tel, int nbr_appel, int cout);
Client* createData();

#define NBCLIENT 20

Client* initClient(int num_tel, int nbr_appel, int cout)
{
  Client *pClient = malloc(sizeof (Client));
  /** @bug check for NULL pointer missing! */
  pClient->num_tel = num_tel;
  pClient->nbr_appel = nbr_appel;
  pClient->cout = cout;
  return pClient;
}

Client* createData()
{
  Client *clients = malloc(NBCLIENT * sizeof (Client));
  /** @bug check for NULL pointer missing! */
  for (int i = 0; i < NBCLIENT; ++i) {
    int numeroTel = 600000000 + (rand() % NBCLIENT);
    int prixAppel = (rand() % 400) + 1;
    Client *pClient = initClient(numeroTel,1,prixAppel);
    /* copy returned CONTENTS to malloc-ed array element */
    clients[i] = *pClient; /* assignment for struct-s is granted */
    /* release pClient to prevent memory leaks - it's not anymore needed */
    free(pClient);
  }
  return clients;
}

int main()
{
  Client *clients = createData();
  for (int i = 0; i < NBCLIENT; ++i) {
    Client *pClient = clients + i; /* or: &clients[i] would work as well */
    printf("%2d.: %d, %d, %d\n",
      i, pClient->num_tel, pClient->nbr_appel, pClient->cout);
  }
  free(clients);
  return 0;
}

Thereby, to match the required signatures the returned struct value of initClient() is copied into the array malloc()-ed in createData(). As doing it this way, the free() is important to prevent memory leaks as the memory of malloc() done in initClient() is neither used nor referenced afterwards.

Compiled and tested on ideone. Output was:

 0.: 600000003, 1, 87
 1.: 600000017, 1, 116
 2.: 600000013, 1, 336
 3.: 600000006, 1, 93
 4.: 600000009, 1, 222
 5.: 600000002, 1, 28
 6.: 600000010, 1, 60
 7.: 600000003, 1, 327
 8.: 600000000, 1, 227
 9.: 600000012, 1, 137
10.: 600000011, 1, 169
11.: 600000007, 1, 30
12.: 600000002, 1, 331
13.: 600000002, 1, 324
14.: 600000007, 1, 336
15.: 600000009, 1, 203
16.: 600000002, 1, 259
17.: 600000009, 1, 168
18.: 600000013, 1, 57
19.: 600000011, 1, 43

The temporary memory allocated in initClient() and free-d in createData() would be something which would bother me. This could be prevented with a slight change of the signature of initClient():

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

typedef struct { int num_tel, nbr_appel, cout; } Client;

void initClient(Client *pClient, int num_tel, int nbr_appel, int cout);
Client* createData();

#define NBCLIENT 20

void initClient(Client *pClient, int num_tel, int nbr_appel, int cout)
{
  pClient->num_tel = num_tel;
  pClient->nbr_appel = nbr_appel;
  pClient->cout = cout;
}

Client* createData()
{
  Client *clients = malloc(NBCLIENT * sizeof (Client));
  /** @bug check for NULL pointer missing! */
  for (int i = 0; i < NBCLIENT; ++i) {
    int numeroTel = 600000000 + (rand() % NBCLIENT);
    int prixAppel = (rand() % 400) + 1;
    initClient(clients + i, numeroTel, 1, prixAppel); 
    /* &clients[i] would've worked as well as clients + i */
  }
  return clients;
}

int main()
{
  Client *clients = createData();
  for (int i = 0; i < NBCLIENT; ++i) {
    Client *pClient = clients + i; /* or: &clients[i] would work as well */
    printf("%2d.: %d, %d, %d\n",
      i, pClient->num_tel, pClient->nbr_appel, pClient->cout);
  }
  free(clients);
  return 0;
}

Compiled and tested again on ideone. Output was:

 0.: 600000003, 1, 87
 1.: 600000017, 1, 116
 2.: 600000013, 1, 336
 3.: 600000006, 1, 93
 4.: 600000009, 1, 222
 5.: 600000002, 1, 28
 6.: 600000010, 1, 60
 7.: 600000003, 1, 327
 8.: 600000000, 1, 227
 9.: 600000012, 1, 137
10.: 600000011, 1, 169
11.: 600000007, 1, 30
12.: 600000002, 1, 331
13.: 600000002, 1, 324
14.: 600000007, 1, 336
15.: 600000009, 1, 203
16.: 600000002, 1, 259
17.: 600000009, 1, 168
18.: 600000013, 1, 57
19.: 600000011, 1, 43
Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • If that's the solution (instead of some error in the required signatures) then the responsible teacher is a terrible person :) – grek40 Jan 10 '18 at 08:33
  • @Scheff, Thank you very much it works fine, at first it gave me an error :for loop initial declaration used outside c99 But it was fixed when I moved the declaration of i outside of the loop – Ya Ssou Jan 10 '18 at 08:37
  • 1
    @grek40 Yepp. That's why I felt the necessity of an extension. – Scheff's Cat Jan 10 '18 at 08:37
  • @YaSsou Did you recognize that the 2nd sample did provide the exact same output like the first? This is no accident - I didn't seed the `rand()` and hence it is reproducible. This might be welcome or not. In the latter case, you have to use `srand()` with a a random value (e.g. current seconds of time). – Scheff's Cat Jan 10 '18 at 08:41
  • Okay, I see, for now rand() is okay (the teacher required it). Thanks for the information and for everything you were most helpful :) – Ya Ssou Jan 10 '18 at 08:51