0

My query is relatively straightforward. There exists a structure called war_name with the following definition:

struct war_name
{
    char name[40];
    char location[20];
    int deaths;
    int injuries;
    char victor_name[20];
    char loser_name[20];
};

The input function for the aforementioned structure I have created is as follows:

void enter_details(struct war_name *d)
{
    printf("\n________________________________________\n");
    printf("Enter the name of the battle: ");
    fgets(d->name,sizeof(d->name),stdin);
    printf("Enter the location: ");
    fgets(d->location,sizeof(d->location),stdin);
    printf("Enter the number of deaths: ");
    scanf("%d",&d->deaths);
    printf("Enter the number of injuries: ");
    scanf("%d",&d->injuries);
    printf("Enter the victor's name: ");
    fgets(d->victor_name,sizeof(d->victor_name),stdin);
    printf("Enter the defeated party's name: ");
    fgets(d->loser_name,sizeof(d->loser_name),stdin);
    printf("\n________________________________________\n");
}

Now, the main() part has the following composition:

int main(void)
{
    int n;
    printf("How many war details do you wish to input?\n");
    scanf("%d",&n);
    struct war_name *ptr=(struct war_name *)malloc(n*sizeof(struct war_name));
    if(ptr==NULL)
        printf("\nInitialization unsuccessful\n");
    for(int i=0;i<n;i++)
    {
        enter_details(ptr[i]);
    }
    exit(EXIT_SUCCESS);
}

Two queries arrive at this juncture.

Firstly, why does my compiler spit the error cannot convert 'war_name' to 'war_name*' for argument '1' to 'void enter_details(war_name*)' when I pass the ptr value directly to the enter_details() function? To test the hypothesis that merely passing ptr[i] to the function sends the location of the ith structure (which is what the function demands), I printed the value of ptr directly, and lo and behold, the address of the said structure was printed. Why is it that despite this development, the compiler spits and error and demands that I pass the address of the structure with the & operator, given that ptr[i] itself holds the value of the ith structure variable?

Secondly, apropos the conception of the ptr pointer (which points to the structure war_name) and the usage of malloc(), is the way I have initialized ptr with the free space granted by the said function acceptable? For the sake of reference, be advised that the book I am following first creates a pointer of arrays to the war_name struct and then passes the address of the said structures to the function? Is my method reasonable? Is it necessary to conceive an array of pointers to the said structures and then pass each structure variable's address to the enter_details() function, given that my method saves valuable memory?

  • 8
    `ptr[i]` returns the value of the i-th element in the array, not its address – UnholySheep Sep 13 '21 at 16:36
  • 1
    It's really better to show rather than tell. *"a pointer of arrays"* is kinda ambiguous, and since you are (by your own admission) still learning, you may be misrepresenting your textbook. – StoryTeller - Unslander Monica Sep 13 '21 at 16:39
  • 2
    "passing ptr[i] to the function sends the location of the ith structure" ---> No, `ptr[i]` is not _the location of_ the ith structure. `ptr[i]` is the ith structure. `&ptr[i]` is the location of the ith structure. – chux - Reinstate Monica Sep 13 '21 at 16:40
  • 3
    Bug: `fgets()` will read in the left-over `'\n'` after `scanf("%d",...);`. Suggest droping `scanf()` usage. – chux - Reinstate Monica Sep 13 '21 at 16:45
  • @UnholySheep isn't ```ptr[i]``` storing the address of the i-th structure variable though? ```ptr[i]``` has been defined as a pointer to the structure ```war_name```. – LordObnoxious Sep 13 '21 at 17:01
  • 1
    You can use `fgets` followed by `sscanf` to read the numbers. Another bug is that `fgets` will store the newline in the string, so if the user enters `bob` as their name, the `name` array in the struct will contain `bob\n`. See https://stackoverflow.com/questions/2693776 for advice on how to remove the newline. – user3386109 Sep 13 '21 at 17:02
  • 1
    @LordObnoxious no `ptr[i]` has not been defined as a pointer. `ptr` is a pointer, `ptr[i]`is dereferencing it, it's the same as `*(ptr + i)` – UnholySheep Sep 13 '21 at 17:04
  • You have allocated memory for an **array of structures** and `ptr` is a pointer to the first element of that array. Based on the information you've given, the code in the book allocates memory for an **array of pointers to structures**. And I assume that there are additional allocations for each structure. So in the book `ptr[i]` is a **pointer to a struct**, but in your code `ptr[i]` is an **instance of a struct**. – user3386109 Sep 13 '21 at 17:11
  • @chux-ReinstateMonica if I may ask, why will this occur? What causes the lingering '\n' to stay extant? – LordObnoxious Sep 15 '21 at 19:31
  • @LordObnoxious `scanf("%d",...);` reads the numeric text and then the `'\n'`. As `'\n'` is not numeric text, `scanf()` puts it back in `stdin` for the next I/O function. Thus `'\n'` is not read _and_ consumed. Best to not use `scanf()` until you understand why it is bad. – chux - Reinstate Monica Sep 15 '21 at 19:38
  • @chux-ReinstateMonica is the lingering newline extant in the buffer? And if so, does there exist a mechanism to flush it? – LordObnoxious Sep 15 '21 at 19:50
  • Yes, a lingering newline may exist in `stdin`. Common way to read _rest of line_: `int ch; while ((ch = fgetc(stdin)) != '\n' && ch != EOF) { ; }`. Better to not use `scanf()` and use `fgets()`. – chux - Reinstate Monica Sep 15 '21 at 20:37
  • @chux-ReinstateMonica this has genuinely shifted my perspective about the relevance of ```scanf()```. The book I am following paid scant regard to bugs of this kind, something that became relevant when I conceived my own programs with the aforementioned function. Since the book doesn't make a single reference to buffers in C, where do I start to gain a modicum of grasp over the aforementioned? Do you have any recommendations or resources that you'd suggest? – LordObnoxious Sep 16 '21 at 01:10
  • @LordObnoxious My best guide has been the C spec. Might be a bit much for a beginner. – chux - Reinstate Monica Sep 16 '21 at 01:12
  • @chux-ReinstateMonica I'm not a beginner per se, but I'm a wee reluctant to copiously go through the cumbersome standards. Do you have any other sources in mind? – LordObnoxious Sep 16 '21 at 03:09
  • @LordObnoxious Perhaps after reading some of the C standard, your assessment might change? No other reccomendation. – chux - Reinstate Monica Sep 16 '21 at 03:50

1 Answers1

3

As said in the comments, there are issues in your code:


First:

enter_details(ptr[i]);

is wrong. enter_details() accepts a pointer to struct war_name while ptr[i] is of type struct war_name. So what you should do is pass the address of ptr[i]:

enter_details(&ptr[i]);

Second, fgets() reads the last \n, so you should replace it with \0:

fgets(d->name, sizeof(d->name), stdin);
d->name[strcspn(d->name, "\n")] = '\0';

Third, you should also avoid using scanf() to read integers. Use fgets() then parse the input with sscanf():

char buffer[255];
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = '\0';

if (sscanf(buffer, "%d", &d->deaths) != 1)
    printf("Could not read int\n");

Fourth,

struct war_name *ptr=(struct war_name *)malloc(n*sizeof(struct war_name));

is too long and too verbose. You don't need to cast the return value of malloc() except if you use a C++ compiler:

struct war_name *ptr = malloc(n * sizeof(*ptr));
Zakk
  • 1,935
  • 1
  • 6
  • 17
  • You don't need parentheses, `sizeof *ptr`, and reference-dereference `&ptr[i]` could be `ptr + i`. (Not that you should save 2 bytes, but whatever feels more natural to you, or is dictated by the style guide of the project.) – Neil Sep 13 '21 at 19:05
  • Apropos "```enter_details()``` accepts a pointer to struct ```war_name``` while ```ptr[i]``` is of type struct ```war_name```": no, this is not what is present in my code. I've declared ```ptr[i]``` as a pointer to struct ```war_name```. This is what I declared in main: ```struct war_name *ptr=(struct war_name *)malloc(n*sizeof(struct war_name));``` Since this is a pointer, why is it causing an error? – LordObnoxious Sep 14 '21 at 23:12
  • 1
    @LordObnoxious No, you didn't. Let's break it into pieces: 1. `struct war_name *ptr` : you are declaring a pointer to `struct war_name` (an array of `struct war_name`) 2. `= (struct war_name *)`: useless casting (unless you are using a C++ compiler) 3. `malloc(n * sizeof(struct war_name))`: you are allocating `n` elements of type `struct war_name`. Now, when you write `ptr[i]`, you are accessing the `i`th element which is of type `struct war_name` (see point 3). So, **ptr[i] is NOT a pointer**. Therefore, you need to pass its address to `enter_details()`. – Zakk Sep 14 '21 at 23:36
  • @Zakk that makes sense. This really helps. Thanks! Alternatively, I could also pass ```ptr``` as well to the said function and increment it like this (```ptr++```) to cycle through all the structure variables, couldn't I? – LordObnoxious Sep 15 '21 at 10:53
  • 1
    @LordObnoxious Yes, you could. Just make sure you don't go beyond the last element (out of range), i.e. always count the number of `++`. – Zakk Sep 15 '21 at 12:39
  • @Zakk apropos the fourth observation of yours, my compiler spits the following error when I don't typecast the return value of ```malloc()```: ```invalid conversion from 'void*' to 'war_name*' [-fpermissive]```. – LordObnoxious Sep 15 '21 at 16:37
  • @LordObnoxious Are you using a C++ compiler? – Zakk Sep 15 '21 at 16:38
  • @Zakk I use the GCC compiler for C. – LordObnoxious Sep 15 '21 at 18:47
  • @LordObnoxious What is the command you use to compile your code? – Zakk Sep 15 '21 at 19:41
  • @Zakk ```g++ .c``` – LordObnoxious Sep 15 '21 at 19:49
  • @LordObnoxious `g++` is a C++ compiler. Use `gcc` like this: `gcc filename.c -o output` – Zakk Sep 15 '21 at 20:00
  • @Zakk what does the ```-o output``` represent? – LordObnoxious Sep 15 '21 at 20:03
  • @LordObnoxious The name of your executable file. It generates an `.exe` on window and a `.run` on linux. – Zakk Sep 15 '21 at 20:19
  • @Zakk how is this different from the command I've cited though? Both yield an executable file, don't they? – LordObnoxious Sep 15 '21 at 20:21
  • @LordObnoxious with `-o` you can specify its name. – Zakk Sep 15 '21 at 20:21
  • @Zakk where can I find the manual enumerating all the commands of the GCC compiler? Secondly, do you recommend using an IDE or resorting to the good ol' Notepad++ and GCC compilation duo? – LordObnoxious Sep 16 '21 at 01:07
  • 1
    Consder `ptr = malloc(sizeof *ptr * n);` – chux - Reinstate Monica Sep 16 '21 at 01:13
  • @LordObnoxious For the manual: use `gcc --help`, and see https://gcc.gnu.org/onlinedocs/gcc-11.2.0/gcc/#toc-GCC-Command-Options – Zakk Sep 16 '21 at 08:44
  • 1
    @LordObnoxious For the IDE: it's really a personal choice. My suggestion is to use Visual Studio Code wtih C/C++ extensions. It's an all-in-one editor and supports a lot of programming languages. But this is **just my suggestion**. You can try yourself and see what works best for you. – Zakk Sep 16 '21 at 08:48