1

I saw code as written below.

typedef struct abc {
    int a;
    char b;
    float c;
} abc;

int main()
{
  abc *ab;
  int *i;
  i = (int*)malloc(sizeof(int));
  *i = 0;
  ab = (abc*) i;

  return 0;
}

In the penultimate line ab = (abc*) i;, what does the code try to do?

If we want to set the value of ab->a, then why is it done in this way, rather than:

ab->a = (int)i;

If ab = (abc*) i; updates the value of ab->a, then how will the other two structure members get initialized without intitializing them exclusively?

jogojapan
  • 68,383
  • 11
  • 101
  • 131
Rasmi Ranjan Nayak
  • 11,510
  • 29
  • 82
  • 122
  • What does it *try* to do? or what does it *do*.? It casts the address of `i` to be "compatible" with an `abc *` pointer-type, then stores that address in the `ab` pointer. It then promptly does nothing with that cast, returns zero, and leaks the memory allocated on the third line of `main()`. – WhozCraig Mar 29 '13 at 07:33
  • You're asking why obfuscated code does things in an obfuscated way? `ab = (abc*)malloc(sizeof (int)); ab->a = 0;` would have the same effect as this nutty code, but equally pointlessly. – Jim Balter Mar 29 '13 at 07:43
  • I wonder why this was downvoted. – alk Mar 29 '13 at 12:56

5 Answers5

4

then how other two structure members will get initialized without intitializing them exclusively?

They won't.

You'd be getting garbage values in ab->b and ab->c because i does not represent a chunk of memory of a sufficient size to represent an instance of abc.

ab->a is equal to 0 because when you did: *i = 0, you stored the value 0 in the memory location that i pointed to. When you made abc point to the same memory location as i, no writes were done to memory, you just changed the position of the data.

Since 0 was previously stored in 4 bytes at the position that i pointed to, and since int ab::a happens to take up 4 bytes and also happens to be the first member of the struct, ab->a will be equal to 0.

In memory, relative to the position of the instance, your struct is ordered like this:

 ____ ____ ____ ____    ____    ____ ____ ____ ____
| 00 | 01 | 02 | 03 |  | 04 |  | 05 | 06 | 07 | 08 |
|____|____|____|____|  |____|  |____|____|____|____|
        int a          char b         float c

I hope this clears things up.

Note
You're not really guaranteed to have the struct completely packed up like I made it seem in the above representation. While order will be conserved, the space between consecutive members is not always going to be 0 memory address units. Read up on Alignment.

user123
  • 8,970
  • 2
  • 31
  • 52
  • Actually, the order inside the struct is not compiler specific. It must be as defined. So technically, `ab->a` is initialized to 0, but of course, accessing `ab->a` is illegal (and causes undefined behaviour), because the address pointed to by `ab` isn't actually an `abc` object. – jogojapan Mar 29 '13 at 07:56
  • Thanks for clearing that up. – user123 Mar 29 '13 at 08:20
  • @jogojapan: I'm curious where the C standard says accessing `ab->a` leads to UB. Somehow I did not manage to find the paragraph. – alk Mar 29 '13 at 09:44
  • @alk I was thinking that the strict aliasing rule forbids it. But, as it turns out, it does actually allow this specific case. So perhaps it isn't undefined behaviour after all. (Anyway my comment mainly referred to another part of the answer that has meanwhile been changed.) – jogojapan Mar 29 '13 at 11:16
  • 1
    @jogojapan: You might like to take a look at *Jim Balter*'s comments to another answer to this question here: http://stackoverflow.com/a/15699267/694576 – alk Mar 31 '13 at 11:24
3

The line ab = (abc*) i; is casting the pointer variable i of type int* to type abc*, and making that assignment to pointer variable ab. This is certainly not how you want to go about initializing the data members in the struct, though, particularly because only enough room for an int was allocated and we're using a struct that takes up significantly more space than an int.

At the end of the day, it's legal code but very scary. I'm not even sure you can be guaranteed to have data member int a stored in the address pointed to by ab. I want to say it's implementation dependent, but maybe somebody else can clear that up for me.

  • 1
    "At the end of the day, it's legal code" -- I think not. ab points to an incomplete object and I'm pretty sure that's UB by the standard, even if you don't access the other fields. – Jim Balter Mar 29 '13 at 07:50
  • @JimBalter: The standard says: "*A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type.*" – alk Mar 29 '13 at 09:18
  • @alk Nothing there about an incomplete *object*, which is very different from an incomplete type. – Jim Balter Mar 29 '13 at 09:24
  • @JimBalter: You are right, absolutly. But this very absence of "*incomplete object*" in the C standard (at least in this context) makes me doubt that accessing the `ab->a` would lead to UB. A theory holds as long as it hasn't been disproved. So I'd appreciate you'd do the latter by citing the standard. – alk Mar 29 '13 at 09:35
  • "A theory holds as long as it hasn't been disproved" -- This is wrong on several levels but I won't debate or discuss it further. If you want to believe that it's well-defined by the Standard, that's up to you; I merely expressed what I believe to be the case. It's moot because, even if it is theoretically UB, there are no practical implementations where it will fail. – Jim Balter Mar 29 '13 at 09:41
  • 1
    The two citations of language from the Standard are not relevant to what I wrote ... assigning an `int*` pointer to an `abc*` pointer is not of itself problematic -- no disagreement about that. What is problematic is if, as a result of the assignment, the `abc*` pointer does not point to memory that can be interpreted as an object of type `abc`. The Standard says "objects are composed of contiguous sequences of one or more bytes, the number, order, and encoding of which are either explicitly specified or implementation-defined". But, `sizeof(int)` bytes aren't enough to represent an `abc`. – Jim Balter Mar 30 '13 at 20:11
  • 1
    @alk "this very absence of "incomplete object" in the C standard" -- the words are absent but not the concept. Given, say, `struct {char a; char b;}* p = malloc(1)`, the value of `p` is not a pointer to an *object* of required type, because it doesn't satisfy the Standard's requirements for objects. The Standard says "if an lvalue does not designate an object when it is evaluated, the behavior is undefined". In `p->a`, `p` is evaluated but it doesn't designate an object. A possible implementation could load `*p` into working memory and then extract the `a` member ... but `*p` might fail. – Jim Balter Mar 30 '13 at 20:28
  • @JimBalter: Thank you very much for explicitly pointing out the key issue (I missed): That is the possible `*p` before the intended `p->a`. And thanks even more for digging out the relevant lines form the Standard!-) – alk Mar 31 '13 at 11:18
  • @alk You're welcome. I should have spelled out my point better in my original comment. I'm still not certain that the authors of the Standard intended this to be UB, but I think there's a good argument that it is. Thanks for being a sane professional, unlike that other guy (whose profile is an interesting read). – Jim Balter Apr 01 '13 at 04:50
2

That penultimate line causes a conversion from (int *) to (abc *), and an assignment of the result to ab. It's not a very good line of code, because the old pointed-to type is smaller in size than the new pointed-to type; Some attempts to use the result of this conversion will be undefined behaviour. Leaving it as an (int *), or declaring a prefix struct to convert to would be a far better idea.

The * in abc * indicates that the type is a "pointer to abc". ab doesn't point to an abc object until you tell it to point to one. ab = /* something */ assigns ab to point to something. ab = malloc(sizeof *ab); would make sense, in this example.

This is silly: i = (int*)malloc(sizeof(int));. You don't need to cast the return value of malloc. The only justifiable reason for this is that the author of this code has neglected to #include <stdlib.h>. I suggest including that library, rather than casting the return value. You can feel free to ignore my advice, but don't ask any questions about strange errors until you've read this page.

autistic
  • 1
  • 3
  • 35
  • 80
1

abc *ab; ab is a pointer of type struct abc; int *i; is a pointer to the int returned by malloc

It's value is set using *i = 0;

ab = (abc*) i;

This line is assigning the address of location allocated by malloc to ab. By typecasting i to (abc*) it is indicated that ab will be used to read memory chunks of the size of struct abc ab = (abc*) i; does not assign value to a. To assign value to a u will do ab->a = 5;

The values already present are garbage values (default random value)

Suvarna Pattayil
  • 5,136
  • 5
  • 32
  • 59
-2
ab = (abc*) i; updates the value of ab->a

If we look at struct layout, int is int is first member, so it will be initialize with 0, so ans is yes! I tried with VC and it initialize it with 0, but this is tricky, in case of struct if member change order then you got grabage!

There is also memory leak, memory allocated with malloc never freed!

Saqlain
  • 17,490
  • 4
  • 27
  • 33
  • "never freed": Not really: http://stackoverflow.com/questions/5612095/is-freeing-allocated-memory-needed-when-exiting-a-program-in-c – Jim Balter Mar 29 '13 at 07:45
  • Its not a good practice to depend on OS for memory cleanup! – Saqlain Mar 29 '13 at 07:50
  • Sorry, but you're wrong. In a professional, commercial setting, it's a mistake to track down and deallocate all allocated memory just to satisfy purists when the OS guarantees that the entire address space of your process will be destroyed ... such code can be very expensive to write, as some of that memory is allocated at startup, or is part of complex data structures, and freeing it all can greatly slow down the shutdown of the program and introduce many points of possible error. – Jim Balter Mar 29 '13 at 07:56
  • 1
    We can debate! This is not something on which you can declare someone wrong! You are going to establish your dependency on OS never a good practice, i worked on very renowned commercial projects, i know what you mean, but as i said this is something debatable! – Saqlain Mar 29 '13 at 07:59
  • Logical failure. You made an assertion that something is true. I made an assertion that it is false. My assertion was actually based on analytical argument, and 45 years of experience as a professional programmer. Your assertion was based on nothing, except apparently a belief that adding exclamation marks to claims makes them true. – Jim Balter Mar 29 '13 at 08:00
  • You can debate whether the Earth orbits around the sun, but it does, just as it is the case that it is perfectly good practice to depend on the OS for memory cleanup when the OS guarantees memory cleanup. – Jim Balter Mar 29 '13 at 08:03
  • "You are going to establish your dependency on OS never a good practice" -- You are the one who claimed that it is never good practice, a claim for which you have offered zero support. – Jim Balter Mar 29 '13 at 08:04
  • 1
    Finally, I started this with a *true* assertion correcting your *false* assertion -- that the memory is "never freed". You immediately changed the subject to whether depending on the OS to free the memory is good practice, dodging the point that the OS *does* free the memory (on most hosted platforms). That was bad behavior. Goodbye. – Jim Balter Mar 29 '13 at 08:11
  • 1
    @JimBalter Very well, I'll enter this debate. Where does the C standard guarantee that "the entire address space of your process will be destroyed"? Where does the C standard guarantee that there'll be an OS to perform such an action? – autistic Mar 29 '13 at 08:21
  • And you should take it up with the folks who posted at my citation -- http://stackoverflow.com/questions/5612095/is-freeing-allocated-memory-needed-when-exiting-a-program-in-c ... I'm done here. – Jim Balter Mar 29 '13 at 08:29
  • @JimBalter, you just seems to have worked with Linux and Windows only :), there are lot lot more OS and RTOS and it is never considered good practice to leave all memory behind you! If some question on stack overflow consider it reasonable practice in some cases, it does not mean to a standard for all OS! I wish i could hv time to debate with you... – Saqlain Mar 29 '13 at 10:23
  • "you just seems to have worked with Linux and Windows only " -- Wrong. "it is never considered good practice to leave all memory behind you!" -- Again with the assertions supported only by exclamation marks. " it does not mean to a standard for all OS!" -- Strawman with exclamation marks. " I wish i could hv time to debate with you..." -- You seem to think that debate is saying the same thing over and over again, without providing reasons, just exclamation marks. – Jim Balter Mar 29 '13 at 10:26
  • http://www.hpl.hp.com/personal/Hans_Boehm/gc/myths.ps Myth #4. Your opinion of my or Hans Boehm's skills are irrelevant. – Jim Balter Mar 30 '13 at 19:24