Thread: argv and its "formal" type

  1. #31
    Registered User
    Join Date
    Apr 2012
    Posts
    13
    Quoted from the C99 Rationale document you have referred to (6.3.2.3):
    A pointer to void may be converted to a pointer to an object of any type.
    A pointer to any object of any type may be converted to a pointer to void.
    So if for whatever reason the assignment "foo=(void *)argv" is invalid or undefined, then it breaks the above two statements; so I don't care to have support for those non-conforming systems.

    @whiteflags What new program? I just posted an abbreviated form of the code I posted in Post #6.
    In any case try doing it right by invoking it with a command-line parameter.

  2. #32
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    So if for whatever reason the assignment "foo=(void *)argv" is invalid or undefined, then it breaks the above two statements; so I don't care to have support for those non-conforming systems.
    Swing and a miss!

    Buying your head in the sand doesn't make you right; it makes you silly.

    Soma

  3. #33
    Registered User
    Join Date
    Apr 2012
    Posts
    13
    Quote Originally Posted by phantomotap View Post
    Swing and a miss!

    Buying your head in the sand doesn't make you right; it makes you silly.

    Soma
    Bicycles are a vegetable.

  4. #34
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by yuklair
    So if for whatever reason the assignment "foo=(void *)argv" is invalid or undefined, then it breaks the above two statements; so I don't care to have support for those non-conforming systems.
    Besides the part that I quoted about alignment, the following sentence reads: "Otherwise, when converted back again, the result shall compare equal to the original pointer." So, the conversion from void* to a pointer type could result in a loss of information, unless the pointer type is the original pointer type. This is similiar to say, conversion from an unsigned long to unsigned short and back again to unsigned long. If the value is outside of the range of unsigned short, you won't get back the original. Depending on your purposes, this can be okay for an integer, but not so for pointers.

    EDIT:
    Actually, no. A better analogy for A* to void* to B* where the sizes may differ is going from unsigned long to unsigned long then to unsigned short, and thus a possible loss of information.
    Last edited by laserlight; 04-20-2012 at 09:54 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #35
    Registered User
    Join Date
    Apr 2012
    Posts
    13
    Quote Originally Posted by laserlight View Post
    A better analogy for A* to void* to B* where the sizes may differ is going from unsigned long to unsigned long then to unsigned short, and thus a possible loss of information.
    You can't ignore the two statements that I quoted though; in that case "A pointer to void may NOT be converted to a pointer of an object of type B".

    Can you explain an example scenario where alignment will cause it to fail?

  6. #36
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by yuklair View Post
    Quoted from the C99 Rationale document you have referred to (6.3.2.3):

    So if for whatever reason the assignment "foo=(void *)argv" is invalid or undefined, then it breaks the above two statements; so I don't care to have support for those non-conforming systems.
    Those two statments do not, in any way imply that converting something to a void *, then converting it to a different type is safe. Along with what laserlight mentioned, I would also like to quote, from the C99 Rationale, the relevant part of section 6.2.5 that I suggested you read, and which proves you are unequivocally wrong:
    Quote Originally Posted by C99 Rationale, p37, lines 13-15
    It is undefined what will happen if a pointer of some type is converted to void* , and then the void* pointer is
    converted to a type with a stricter alignment requirement.
    In case you did read that, and just didn't understand it, it means, if you cast A* to void *, and then cast that to B*, it may result in undefined behavior. Systems that do weird stuff under those circumstances are not "non-conforming". They are perfectly conforming. Your casting business wont work on them.

    That is effectively what you are doing in your code, by the way: casting argv to void *, which then is cast to the type of foo. The rationale uses the term "silently coerced" (see p37, lines 11-13) because once you have something of the type void *, you don't need a cast to assign it to another type. That cast to a void *, as I now repeat to the point of exhaustion, covers up what is a potentially serious bug.

  7. #37
    Registered User
    Join Date
    Apr 2012
    Posts
    13
    Quote Originally Posted by anduril462 View Post
    Those two statments do not, in any way imply that converting something to a void *, then converting it to a different type is safe. Along with what laserlight mentioned, I would also like to quote, from the C99 Rationale, the relevant part of section 6.2.5 that I suggested you read, and which proves you are unequivocally wrong:

    In case you did read that, and just didn't understand it, it means, if you cast A* to void *, and then cast that to B*, it may result in undefined behavior. Systems that do weird stuff under those circumstances are not "non-conforming". They are perfectly conforming. Your casting business wont work on them.

    That is effectively what you are doing in your code, by the way: casting argv to void *, which then is cast to the type of foo. The rationale uses the term "silently coerced" (see p37, lines 11-13) because once you have something of the type void *, you don't need a cast to assign it to another type. That cast to a void *, as I now repeat to the point of exhaustion, covers up what is a potentially serious bug.
    Then let me ask, what is the purpose of the generic pointer? If what you say is true then the only defined behavior would be when a pointer is casted to a generic pointer then can only be casted back to the original pointer type. What's the point?

  8. #38
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Perhaps a function interface accepts a void pointer, which is then turned back into the type of object that you actually passed. This happens frequently in code which uses callbacks. The qsort function for example, asks you to pass in a function pointer of a certain type that allows it to make comparisons. The const void* parameters in the function pointer allow you to write a comparison function for any data type.

  9. #39
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Then let me ask, what is the purpose of the generic pointer?
    Simplicity of callbacks with a modicum of type safety related to `malloc' and friends.

    Back in the dawn of time `malloc' returned something else (`char *' generally) which some compilers wanted you to cast while others couldn't care less.

    Making a type, `void *', which guarantees "round trip" convertibility to and from any other pointer allows `malloc' and friends to operate on a reasonable type without the conceptually meaningless cast.

    *shrug*

    But because I'm sure you'd rather not understand that simple concept, I'll offer a different explanation.

    Money equals power.
    Power equals ponies.
    Ponies equals Pinke Pie.
    Pinkie Pie has the cheat codes.

    Q.E.D.

    Soma

  10. #40
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by yuklair View Post
    Then let me ask, what is the purpose of the generic pointer? If what you say is true then the only defined behavior would be when a pointer is casted to a generic pointer then can only be casted back to the original pointer type. What's the point?
    Wow, are you awful at reading. Actually, it's not so much that you're bad at it, you just don't do it. If you would have read all the relevant sections of the rationale that I mentioned in post #29, you wouldn't have to ask this question. Read the second paragraph of section 6.3.2.3 of the rationale.

  11. #41
    Registered User
    Join Date
    Mar 2010
    Posts
    583
    Casting between pointers is risky: here's a concrete example of how it can blow up on ARM:

    Code:
    void fooi(int * i)
    {
       *i = 5;
    }
    void badcast()
    {
       char str[] = "hello there";
       char *c = &str[3];   
       fooi((void*)c);
    }
    The ARM ABI says that ints must be 4 byte aligned and chars can be 1 byte aligned. In this example I construct a valid char pointer to an unaligned address then try to access it through an int*. We've told the compiler that the address is word aligned with the type.

    An ARM compiler will generate a store like:
    Code:
      mov     r2, #5
      str     r2, [r3]
    STR is a word-sized store to an unaligned address. What this does depends on the configuration of the core and version of the architecture:
    • On later ARM cores (v6,v7), if the core is configured to allow unaligned accesses, it'll succeed (like it does on x86)
    • On later ARM cores if the core is configured to check alignment, it'll generate a data abort. I.e. it will crash your program.
    • On earlier ARM cores unaligned accesses aren't supported at all. I think some still have the ability to check alignment and generate an abort if you set it up that way, but by default they'll treat the address as if it were aligned: i.e. ignore the bottom 2 bits of a word access. That'd store 0x0000005 to &str[0]. Bad!! Horrible to debug!

    Here's a table in all its horror. ARM Information Center

    If you did:
    Code:
       fooi(c);
    it would error, because it knows that the pointer types are incompatible.
    If GCC had a keyword to mark this as an unaligned pointer (it doesn't) then it'd have to generate 4 stores of 1 byte.

    Functions like memcpy that take void*s have to assume the pointer is unaligned, so will check and if necessary do byte copies. I expect this is still true of ARM cores with unaligned access support, as it's always more efficient to do aligned accesses.

    I went over yuklair's last bit of code a bit myself too and scratched my head a bit.

    Code:
    #include<stdio.h>
    char (*(*foo)[])[];
    int
    main(int argc,char **argv){
      foo=(void *)argv;
      puts(*(*foo)[1]);
      return(0);
    }
    If I am understanding this correctly....

    foo is a pointer to an array of pointers to arrays of chars.
    Deferencing foo gives an array of pointers to an array of chars. Then that's converted back into a pointer to the first elenent for the indexing operation. Then the pointer to char is dereferenced, but that's ok because when we get there we've been honest with the compiler about getting a char array. Which is then converted back into a pointer to the first char for the call.

    Pointers to arrays of pointers and pointers to pointers must be the same size, right?

    So if I have understod this right, which I may not have done, this is safe. But I wouldn't say it's an accurate representation of argv. I think the most helpful way to write it in terms of indicating the two arrays would be char argv[][]. Of course this isn't allowed, and even if it was I don't really approve -- I think that whether a pointer points to a single object or to an array of objects should be clear from the context. I think it usually is!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 14
    Last Post: 12-26-2004, 11:18 AM
  2. "Any" type using "Boost"
    By Trauts in forum C++ Programming
    Replies: 1
    Last Post: 05-01-2003, 10:53 AM
  3. "itoa"-"_itoa" , "inp"-"_inp", Why some functions have "
    By L.O.K. in forum Windows Programming
    Replies: 5
    Last Post: 12-08-2002, 08:25 AM
  4. "CWnd"-"HWnd","CBitmap"-"HBitmap"...., What is mean by "
    By L.O.K. in forum Windows Programming
    Replies: 2
    Last Post: 12-04-2002, 07:59 AM
  5. "int main(int argc,char* argv[])" when can I use it
    By Tonyukuk in forum C++ Programming
    Replies: 2
    Last Post: 11-19-2002, 05:40 AM