0

I am learning about Dynamic Memory Allocate now and it's hard to understand, especially when the lecturer mentioned about DMA and structure using pointer.

First, at the line 1 in my code, i get confused what "&ptr->eno" means. we initiate ptr as a pointer so it means ptr hold the address of the memory we reserved, lets say ptr hold the address 2046, is that "&ptr->eno" means writing value to the address of 2046 it point to?

Second, at the line 2, how i can print out the value, cause "ptr->eno" contains value "2046" than when i print it out, it gonna give me the number 2046, not the value i try to stored in the memory location 2046.

My code was copied from the lecture note, i tried to run it on Visual studio, it crashed after i input the values. I am so new with C, my assumption may looks stupid and painful to understand. if you can't understand my explanation, please tell me how to use pointer with structure, may be i can figure out my mistake. Thank you

#include<stdio.h>
#include<stdlib.h>
struct emp
{
    int eno;
    char ename[20];
    float esal;
};
void main()
{
    struct emp* ptr;
    ptr = (struct emp*)malloc(sizeof(struct emp));
    if (ptr=NULL)
    {
        printf("out of memory error");
    }
    else
    {
        printf("enter the value of employee: \n");
        scanf_s("%d%s%f", &ptr->eno, ptr->ename, &ptr->esal); // line 1
    }
    printf("the name is: %s \t the number: %d \t the salary: %f", ptr->ename, ptr->eno, ptr->esal); //line2
}
Khoa.Huynh
  • 41
  • 6
  • @StoryTeller my mistake, fixed it – Khoa.Huynh Nov 22 '17 at 09:51
  • 1
    Just remember that a pointer is just a variable that stores the address of something else as its value. So with `int a=5;` and `int *b = &a;` the pointer `b` just stores the address of `a` as its value. When you dynamically allocate with `malloc, calloc or realloc`, those functions reserve a block of memory of the requested size and return *the address* of the beginning of the block of memory. So you are just storing the address to the block of memory in the pointer variable you assign the return to. – David C. Rankin Nov 22 '17 at 10:07
  • @DavidC.Rankin: This is the second (that I have come across) pointer related comment you have made today, and I am fan of these! +1 to it. – WedaPashi Nov 22 '17 at 10:17
  • How could it crash *there*?! your code has `ptr = NULL` there, it doesn't even go to the *input* branch. – Antti Haapala -- Слава Україні Nov 22 '17 at 10:17
  • 1
    @WedaPashi - yes, that just seems to be one of the biggest stumbling blocks new C programmers have with pointers. So I try to drop a comment if it looks like that is what they are struggling with. (which means I end up dropping quite a few `:)` – David C. Rankin Nov 22 '17 at 10:19
  • @DavidC.Rankin: Absolutely fair and a nice thing to do. Thanks! – WedaPashi Nov 22 '17 at 10:21
  • I'd also add that the abbreviation DMA usually refers to Direct Memory Access, not Dynamic Memory Allocate, so be aware of that when doing your research. – David Hoelzer Nov 22 '17 at 10:44

3 Answers3

4

This:

if (ptr=NULL)

doesn't do what you want. You want the comparison operator ==, not the assignment operator =. This will always set ptr to NULL, making the rest of the code have undefined behavior (which often leads to crashes in practice). Enabling, and looking at, compiler warnings is a very good way of avoiding this problem.

For the actual question, &ptr->eno means "the address of the field eno in the struct pointed to by ptr". In practice it will evaluate to the value of ptr, plus the offset from the start of the structure to the field eno, which in this case will be 0 since eno is the first field.

Furthermore, the printf() should of course be inside the else, since you can only run that if ptr is not NULL.

And, importantly: you must provide a buffer size for the string to scanf_s().):

Unlike scanf and wscanf, scanf_s and wscanf_s require the buffer size to be specified for all input parameters of type c, C, s, S, or string control sets that are enclosed in []. The buffer size in characters is passed as an additional parameter immediately following the pointer to the buffer or variable.

So, it should be:

const int num = scanf_s("%d%s%f", &ptr->eno, ptr->ename, (unsigned int) sizeof ptr->ename, &ptr->esal);
if (num == 3)  // All conversions succeeded, we can rely on them
{
  printf("the name is: %s \t the number: %d \t the salary: %f", ptr->ename, ptr->eno, ptr->esal); //line2
}

And you should check its return value, otherwise you risk using non-initialized variables if the input fails for some reason.

Note that the size of the buffer seems to be specified as unsigned int, and not size_t, which is ... not optimal in my point of view so the cast is needed.

By the way, "DMA" typically means "direct memory access" and has nothing to do with this; that was confusing me for a while.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    BTW, if OP enabled all warnings at compilation time (e.g. `gcc -Wall -Wextra -g`) he would have been warned for that wrong `if` test – Basile Starynkevitch Nov 22 '17 at 09:54
  • 1
    hi, i edited it, it still crash after i input values. – Khoa.Huynh Nov 22 '17 at 09:55
  • 1
    `scanf_s("%d%s%f", &ptr->eno, ptr->ename, &ptr->esal); ` is dubious. See the **Remarks** section of [\[ scanf_s documentation \]](https://msdn.microsoft.com/en-us/library/w40768et.aspx) – sjsam Nov 22 '17 at 10:28
  • @sjsam it is not dubious, but wrong, I converted my deleted comment into an answer. – Weather Vane Nov 22 '17 at 10:31
  • @WeatherVane Indeed, I just meant the practice of writing such statements. It is pretty hard to find out the mistake there. – sjsam Nov 22 '17 at 10:35
  • @sjsam those `_s` functions are really no safer at all. They might be safer in execution, but are just as easy to get wrong when coding. – Weather Vane Nov 22 '17 at 10:37
  • Thank you for your very kind explanation, one mistake but it has so many theoretical aspects to concern about. – Khoa.Huynh Nov 22 '17 at 12:18
4

Assuming that

if (ptr=NULL)

was a typo (since the subsequent "it crashed after i input the values" could not have happened), there is a fault here:

scanf_s("%d%s%f", &ptr->eno, ptr->ename, &ptr->esal);

is missing the required size argument for %s

scanf_s("%d%s%f", &ptr->eno, ptr->ename, 20, &ptr->esal);

or instead of hard coding, perhaps

scanf_s("%d%s%f", &ptr->eno, ptr->ename, (unsigned)sizeof ptr->ename, &ptr->esal);
Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • This mades it work as expect, i try to use %19s only but it didn't work, but add sizeof (ptr->ename) , or simply add 20, made it work as expected. – Khoa.Huynh Nov 22 '17 at 12:06
  • `%19s` would be the solution with plain `scanf`. – Weather Vane Nov 22 '17 at 12:09
  • Note that the cast `(unsigned)` or `(unsigned int)` is needed because the result of `sizeof` may not itself be the correct size for the argument. On some systems `sizeof` may return a value of 8 bytes size, but `scanf_s` is expecting an argument of `int` size. – Weather Vane Nov 22 '17 at 12:11
  • hello, i am curious, the size of memory is bytes and suppose to return unsigned int value. Its safe but is it necessary? – Khoa.Huynh Nov 22 '17 at 12:27
  • `sizeof` returns a value of type `size_t`, not `unsigned int`. On my system they are both 4 bytes, but may not be, which is why the cast should be used. For the same reason when printing a size, you should use `%zu` and not `%u`. – Weather Vane Nov 22 '17 at 12:28
  • Thank you, i just did a bit of research about different of size_t and unsigned int. My lecture note considers size_t is similar to unsigned int, that made me confused – Khoa.Huynh Nov 22 '17 at 12:32
  • They are similar in that they are both unsigned integers but that is all. – Weather Vane Nov 22 '17 at 12:36
  • Thank you for your very kind explanation, i understand much more now – Khoa.Huynh Nov 22 '17 at 12:38
1

It is not a good practice to cast the output of malloc (check [ this ] ), the simplest way to do it would be

struct emp *ptr;
ptr = malloc(1 * sizeof *ptr);

Then, == is used for equal to logical comparison, so

if (NULL == ptr)
/* = changed to ==, also put the value first which will help you easily
* fix accidental omission of the second =
*/
{
    printf("out of memory error");
    exit(1); // Exiting with a non-zero status
} 

Note This should be taken as a supplement to [ this ] answer

sjsam
  • 21,411
  • 5
  • 55
  • 102