|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
I commonly use pointers to iterate thru an array. For example: int my_array[X]; int *p = my_array; int const *const pend = my_array + sizeof my_array/sizeof*my_array; do *p++ = 42; while (pend != p); (Yes I realise the lack of spaces in the sizeof thing above is disgusting, but I've gotten so sick of writing it out that I make it as compact as possible) I can't count how many times I use this construct in my code every day. It's a right pain in the ass to always have to write out the long-winded intialiser for pend, so I'm considering switching to initialising pend as follows: int const *const pend = *(&my_array+1); 1) my_array is an int[X] 2) &my_array is an int(*)[X] 3) &my_array+1 is the address of the non-existant array located after the current one. 4) *(&my_array+ 1) decays to the address of the first element in the non-existant array after the current one, which is also the "pend" address for the array that actually exists. It's a hell of a lot shorter to write, and also I think it's a little less vulnerable to typos because you'll most likely get a type mismatch if its written wrongly. Anyway, just wondering what people think of the alternative. Saves me that little rush of pissed-off-ness every time I've to write out the tedious sizeof thing. (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array) because then I'd have to worry about including the necessary header file... which is also the reason why I don't use NULL.) -- Tomás Ó hÉilidhe |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
Tomás Ó hÉilidhe wrote:
> I commonly use pointers to iterate thru an array. For example: > > int my_array[X]; > > int *p = my_array; > int const *const pend = my_array + sizeof my_array/sizeof*my_array; > > do *p++ = 42; > while (pend != p); Use a understandable macro for the array size, then you can have pend = my_array + X. (Also, I'd write it as `for (p = my_array; p < pend; p++) *p = 42;`. Or even directly p < my_array + X, since a decent compiler should optimize that, shouldn't it?) -- Army1987 (Replace "NOSPAM" with "email") |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote:
> I commonly use pointers to iterate thru an array. For example: > > int my_array[X]; > > int *p = my_array; > int const *const pend = my_array + sizeof my_array/sizeof*my_array; > > do *p++ = 42; > while (pend != p); > > (Yes I realise the lack of spaces in the sizeof thing above is > disgusting, but I've gotten so sick of writing it out that I make it as > compact as possible) > > I can't count how many times I use this construct in my code every day. > It's a right pain in the ass to always have to write out the long-winded > intialiser for pend, so I'm considering switching to initialising pend > as follows: > > int const *const pend = *(&my_array+1); > > 1) my_array is an int[X] > 2) &my_array is an int(*)[X] > 3) &my_array+1 is the address of the non-existant array located after > the current one. And you invoke undefined behavior. > It's a hell of a lot shorter to write, and also I think it's a little > less vulnerable to typos because you'll most likely get a type mismatch > if its written wrongly. You could have a macro, or not use pointers (use that X). > Anyway, just wondering what people think of the alternative. Saves me > that little rush of pissed-off-ness every time I've to write out the > tedious sizeof thing. But it invokes undefined behavior. > (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array) > because then I'd have to worry about including the necessary header > file... which is also the reason why I don't use NULL.) NULL is declared in many standard C header files, among them is <stdio.h> I cannot believe you don't use NULL because you worry about it not being defined. |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
vippstar@gmail.com writes:
> On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: >> I commonly use pointers to iterate thru an array. For example: >> >> int my_array[X]; [snip] >> 1) my_array is an int[X] >> 2) &my_array is an int(*)[X] >> 3) &my_array+1 is the address of the non-existant array located after >> the current one. > And you invoke undefined behavior. [...] No, he doesn't. &my_array+1 is a valid address, just past the end of my_array. Computing this address is ok; attempting to dereference it would invoke UB. -- Keith Thompson (The_Other_Keith) <kst-u@mib.org> Nokia "We must do something. This is something. Therefore, we must do this." -- Antony Jay and Jonathan Lynn, "Yes Minister" |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
vippstar@gmail.com wrote:
> On Feb 6, 12:40 am, "Tom?s ? h?ilidhe" <t...@lavabit.com> wrote: <snip> > > (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array) > > because then I'd have to worry about including the necessary header > > file... which is also the reason why I don't use NULL.) > NULL is declared in many standard C header files, among them is > <stdio.h> > I cannot believe you don't use NULL because you worry about it not > being defined. I don't use NULL 'cause I don't know to what it's defined. Usually the NULL macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to be cast when used from a comma expression and the return type of your function is a pointer, otherwise it might be evaluated as an int, which might not be wide enough if (sizeof int != sizeof (void *)). if (some_test_fails) return (errno = ESOMETHING), (void *)NULL; Likewise, you have to cast it when passing as a vararg. execl("/bin/true", "true", (char *)NULL); Usually I just use an unadorned `0' in my code. I do this in part because, like the OP, I don't feel like including <stddef.h> or <stdio.h> or any other header that I don't need. I comment my include statements to describe what I'm [trying] to import; it got really old doing: #include <stddef.h> /* NULL */ But, to each his own. I don't know very many people who do this. I'm okay being alone ![]() |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote:
> vipps...@gmail.com writes: > > On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: > >> I commonly use pointers to iterate thru an array. For example: > > >> int my_array[X]; > [snip] > >> 1) my_array is an int[X] > >> 2) &my_array is an int(*)[X] > >> 3) &my_array+1 is the address of the non-existant array located after > >> the current one. > > And you invoke undefined behavior. > > [...] > > No, he doesn't. &my_array+1 is a valid address, just past the end of > my_array. Computing this address is ok; attempting to dereference it > would invoke UB. I think that when the result of an expression is a non-valid pointer, the behavior is undefined. Correct me if I am wrong, but I have also seen comments in GNU code like this: -- /* ANSI C violation */ char * s = p - 1; -- Where p points to the start of a string passed. 's' is never dereferenced, only used for comparing purposes (while(p > s) or something similar) |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
Tom?s ? h?ilidhe <toe@lavabit.com> wrote:
<snip> > (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array) > because then I'd have to worry about including the necessary header > file... which is also the reason why I don't use NULL.) I keep all my macros in a single file, e.g. `eval.h'. It has ARRAYLEN, STRINGIFY, PASTE, XPASTE, PP_NARG (thanks Laurent!), most of *BSD's <sys/param.h> (MIN, MAX, howmany, etc), and a few other gems I've concocted. It's rare that I don't use at least one of these in a project. I've never satisfactorily packaged my other tools, but this seems to work well (though I'm still torn between `arraylen' and `ARRAYLEN'). |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 2:09 am, William Ahern <will...@wilbur.25thandClement.com>
wrote: > vipps...@gmail.com wrote: > > On Feb 6, 12:40 am, "Tom?s ? h?ilidhe" <t...@lavabit.com> wrote: > <snip> > > > (Oh and by the way, I wouldn't use a macro such as ARRLEN(my_array) > > > because then I'd have to worry about including the necessary header > > > file... which is also the reason why I don't use NULL.) > > NULL is declared in many standard C header files, among them is > > <stdio.h> > > I cannot believe you don't use NULL because you worry about it not > > being defined. > > I don't use NULL 'cause I don't know to what it's defined. Usually the NULL > macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to > be cast when used from a comma expression and the return type of your > function is a pointer, otherwise it might be evaluated as an int, which > might not be wide enough if (sizeof int != sizeof (void *)). I don't think that can happend. > > if (some_test_fails) > return (errno = ESOMETHING), (void *)NULL; As i said, NULL does not need to be casted here. You don't need the comma operator either. the expression '(x, y, z)' evaluates to the type of z with value z. > Likewise, you have to cast it when passing as a vararg. You have to cast 0 to (char *)0 too. > execl("/bin/true", "true", (char *)NULL); > > Usually I just use an unadorned `0' in my code. I do this in part because, > like the OP, I don't feel like including <stddef.h> or <stdio.h> or any > other header that I don't need. That in my opinion is very bad practise for a project. > I comment my include statements to describe > what I'm [trying] to import; it got really old doing: > > #include <stddef.h> /* NULL */ > > But, to each his own. I don't know very many people who do this. I'm okay > being alone ![]() Other programmers in your project might not be okay with that thought, ask them first. As for me, I wouldn't. Consider -- char *p; /* ... */ if(p == 0) /* did you really mean p == 0 or *p == 0 ? */ if(p == NULL) /* clearly ment p == NULL */ -- |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
In article <8fan75-8gn.ln1@wilbur.25thandClement.com>,
William Ahern <william@wilbur.25thandClement.com> wrote: >I don't use NULL 'cause I don't know to what it's defined. Usually the NULL >macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to >be cast when used from a comma expression and the return type of your >function is a pointer, otherwise it might be evaluated as an int, which >might not be wide enough if (sizeof int != sizeof (void *)). > if (some_test_fails) > return (errno = ESOMETHING), (void *)NULL; No, if the return type of your function is a pointer then the return value will be converted to the appropriate type as if by assignment. Assigning a compile-time 0 to a pointer type is guaranteed to result in a NULL pointer constant. -- "Okay, buzzwords only. Two syllables, tops." -- Laurie Anderson |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
vipps...@gmail.com wrote:
> Keith Thompson <ks...@mib.org> wrote: > > vipps...@gmail.com writes: > > > "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: > > > > I commonly use pointers to iterate thru an array. > > > > For example: > > > > int my_array[X]; > > [snip] > > > > 1) my_array is an int[X] > > > > 2) &my_array is an int(*)[X] > > > > 3) &my_array+1 is the address of the non-existant > > > > array located after the current one. > > > > > > And you invoke undefined behavior. > > > > No, he doesn't. &my_array+1 is a valid address, Though it requires a conversion back to int * if used as a pointer 'index' compared against an int * iterator. > > just past the end of my_array. Computing this address > > is ok; attempting to dereference it would invoke UB. > > I think that when the result of an expression is a non- > valid pointer, the behavior is undefined. True, but one byte past the end of an array _is_ a valid pointer. > Correct me if I am wrong, Keith already has. > but I have also seen comments in GNU code like this: > -- > /* ANSI C violation */ > char * s = p - 1; One byte beyond is fine (though you can't dereference it), but there are no special rights for one (or more) byte(s) _before_. > -- > Where p points to the start of a string passed. 's' is > never dereferenced, ... Doesn't matter. Implementations do not have to cope with 'one byte before'. An allocation can be made at the start of a memory page. Merely calculating an address prior to such a page may trap. In contrast, implementations _must_ cope with one byte beyond, which simply means that at least one byte (but typically _at most_ one byte) is reserved at the end of the memory space. -- Peter |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 2:34 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote: > > Keith Thompson <ks...@mib.org> wrote: > > > vipps...@gmail.com writes: > > > > "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: > > > > > I commonly use pointers to iterate thru an array. > > > > > For example: > > > > > int my_array[X]; > > > [snip] > > > > > 1) my_array is an int[X] > > > > > 2) &my_array is an int(*)[X] > > > > > 3) &my_array+1 is the address of the non-existant > > > > > array located after the current one. > > > > > And you invoke undefined behavior. > > > > No, he doesn't. &my_array+1 is a valid address, > > Though it requires a conversion back to int * if used > as a pointer 'index' compared against an int * iterator. > > > > just past the end of my_array. Computing this address > > > is ok; attempting to dereference it would invoke UB. > > > I think that when the result of an expression is a non- > > valid pointer, the behavior is undefined. > > True, but one byte past the end of an array _is_ a valid > pointer. Ah I see, thanks a lot. > > Correct me if I am wrong, > > Keith already has. Hehe, sorry mr Keith for doubting :-P. However, as you said mr Nilsson, he (the OP) is then dereferencing it, and invoking undefined behavior. Also, the whole topic is moot. if sizeof foo / sizeof *foo gives you the elements of foo, then foo must be an array. If it's an array it must be defined as 'foo[X]' or similar. If X is a constant, foo + X is the last pointer (that is not valid to dereference), if X is an object, it is still foo + X, however foo is then a variable length array. Therefore, there is no need for foo + sizeof foo / sizeof *foo. |
|
|
|
#12 |
|
Messages: n/a
Hébergeur: |
vippstar@gmail.com wrote in
news:a6def63a-34ea-4248-a6d4-0c13e642e59b@1g2000hsl.googlegroups.com: > On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote: >> vipps...@gmail.com writes: >> > On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: >> >> I commonly use pointers to iterate thru an array. For example: >> >> >> int my_array[X]; >> [snip] >> >> 1) my_array is an int[X] >> >> 2) &my_array is an int(*)[X] >> >> 3) &my_array+1 is the address of the non-existant array located >> >> after the current one. >> > And you invoke undefined behavior. >> >> [...] >> >> No, he doesn't. &my_array+1 is a valid address, just past the end of >> my_array. Computing this address is ok; attempting to dereference it >> would invoke UB. > I think that when the result of an expression is a non-valid pointer, > the behavior is undefined. > Correct me if I am wrong, but I have also seen comments in GNU code > like this: > -- > /* ANSI C violation */ > char * s = p - 1; > -- > Where p points to the start of a string passed. 's' is never > dereferenced, only used for comparing purposes (while(p > s) or > something similar) That is different, though. Pointing one past the end of the array is valid whereas pointing oen before the start of the array is not. http://www.open-std.org/jtc1/sc22/wg...docs/n1124.pdf §6.5.6, page 83 Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated. Sinan -- A. Sinan Unur <1usa@llenroc.ude.invalid> (remove .invalid and reverse each component for email address) clpmisc guidelines: <URL:http://www.rehabitation.com/clpmisc.shtml> |
|
|
|
#13 |
|
Messages: n/a
Hébergeur: |
vippstar@gmail.com writes:
> On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: >> I commonly use pointers to iterate thru an array. For example: >> >> int my_array[X]; >> >> int *p = my_array; >> int const *const pend = my_array + sizeof my_array/sizeof*my_array; <snip> >> It's a right pain in the ass to always have to write out the long-winded >> intialiser for pend, so I'm considering switching to initialising pend >> as follows: >> >> int const *const pend = *(&my_array+1); >> >> 1) my_array is an int[X] >> 2) &my_array is an int(*)[X] >> 3) &my_array+1 is the address of the non-existant array located after >> the current one. > And you invoke undefined behavior. Presumably not from the pointer arithmetic alone? Is it the deference that you think causes UB? That is my worry. One can construct a pointer "one past the end" of any object, but * can't be applied "if it is evaluated" (6.5.6 p8). I take that to refer to the rule that says that when & is applied to *, neither are evaluated (6.5.3.2 p3). I can't see anything wrong with: int *pend = (void *)(&my_array + 1); from a language point of view, but it is fragile in that it breaks when the code moves to a function and my_array becomes a pointer. Since "C has no array values" is one way of looking at the while array/pointer relationship in C, one could argue that the * is not evaluated in int *pend = *(&my_array + 1); Is the utility of this code sufficient to warrant a change to the meaning of * (or definition of pointer arithmetic) to permit its use when the "value" would be an array and would therefore be immediately converted to a pointer? -- Ben. |
|
|
|
#14 |
|
Messages: n/a
Hébergeur: |
In article <foav6k$pfa$1@canopus.cc.umanitoba.ca>,
Walter Roberson <roberson@ibd.nrc-cnrc.gc.ca> wrote: >> if (some_test_fails) >> return (errno = ESOMETHING), (void *)NULL; >No, if the return type of your function is a pointer then the >return value will be converted to the appropriate type as if by >assignment. But the code is revolting anyway. I can see the temptation to do something like if(condition) a=1, b=2; to avoid a block, but shoving an extra assignment into a return statement is an unprovoked assault on readability. -- Richard -- :wq |
|
|
|
#15 |
|
Messages: n/a
Hébergeur: |
vippstar@gmail.com writes:
> On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote: >> vipps...@gmail.com writes: >> > On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: >> >> I commonly use pointers to iterate thru an array. For example: >> >> >> int my_array[X]; >> [snip] >> >> 1) my_array is an int[X] >> >> 2) &my_array is an int(*)[X] >> >> 3) &my_array+1 is the address of the non-existant array located after >> >> the current one. >> > And you invoke undefined behavior. >> >> [...] >> >> No, he doesn't. &my_array+1 is a valid address, just past the end of >> my_array. Computing this address is ok; attempting to dereference it >> would invoke UB. > I think that when the result of an expression is a non-valid pointer, > the behavior is undefined. > Correct me if I am wrong, but I have also seen comments in GNU code > like this: > -- > /* ANSI C violation */ > char * s = p - 1; > -- > Where p points to the start of a string passed. 's' is never > dereferenced, only used for comparing purposes (while(p > s) or > something similar) Computing a pointer before the beginning of an array invokes UB; computing a pointer just past the end of an array does not. This is mentioned in passing in the answer to question 6.17 in the comp.lang.c FAQ, <http://www.c-faq.com/> (can anyone find a more explicit reference?). The standard's rather long-winded explanation of this is in C99 6.5.6p8 (quoting from n1256; there are no change bars on this paragraph): When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and in-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated. -- Keith Thompson (The_Other_Keith) <kst-u@mib.org> Nokia "We must do something. This is something. Therefore, we must do this." -- Antony Jay and Jonathan Lynn, "Yes Minister" |
|
|
|
#16 |
|
Messages: n/a
Hébergeur: |
vipps...@gmail.com wrote:
> Peter Nilsson <ai...@acay.com.au> wrote: > > ... one byte past the end of an array _is_ a valid > > pointer. > > Hehe, sorry mr Keith for doubting :-P. > > However, as you said mr Nilsson, he (the OP) is then > dereferencing it, and invoking undefined behavior. I never said the OP dereferenced it. The actual code had been snipped by that point. Here it is... int *p = my_array; int const *const pend = my_array + sizeof my_array/ sizeof*my_array; do *p++ = 42; while (pend != p); At no stage is pend dereferenced; and the loop exits on p == pend, so the address is not dereferenced by p either. -- Peter |
|
|
|
#17 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 4:01 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote: > > Peter Nilsson <ai...@acay.com.au> wrote: > > > ... one byte past the end of an array _is_ a valid > > > pointer. > > > Hehe, sorry mr Keith for doubting :-P. > > > However, as you said mr Nilsson, he (the OP) is then > > dereferencing it, and invoking undefined behavior. > > I never said the OP dereferenced it. The actual code had > been snipped by that point. Here it is... > > int *p = my_array; > int const *const pend = my_array + sizeof my_array/ > sizeof*my_array; > do *p++ = 42; > while (pend != p); > > At no stage is pend dereferenced; and the loop exits on > p == pend, so the address is not dereferenced by p > either. Ah, I was a bit confused I guess. However, OP _does_ dereference it. Not in that snipped, but in 4) > 4) *(&my_array+ 1) decays to the address of the first element in the > non-existant array after the current one, which is also the "pend" > address for the array that actually exists. |
|
|
|
#18 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 3:05 am, Keith Thompson <ks...@mib.org> wrote:
> vipps...@gmail.com writes: > > On Feb 6, 2:08 am, Keith Thompson <ks...@mib.org> wrote: > >> vipps...@gmail.com writes: > >> > On Feb 6, 12:40 am, "Tomás Ó hÉilidhe" <t...@lavabit.com> wrote: > >> >> I commonly use pointers to iterate thru an array. For example: > > >> >> int my_array[X]; > >> [snip] > >> >> 1) my_array is an int[X] > >> >> 2) &my_array is an int(*)[X] > >> >> 3) &my_array+1 is the address of the non-existant array located after > >> >> the current one. > >> > And you invoke undefined behavior. > > >> [...] > > >> No, he doesn't. &my_array+1 is a valid address, just past the end of > >> my_array. Computing this address is ok; attempting to dereference it > >> would invoke UB. > > I think that when the result of an expression is a non-valid pointer, > > the behavior is undefined. > > Correct me if I am wrong, but I have also seen comments in GNU code > > like this: > > -- > > /* ANSI C violation */ > > char * s = p - 1; > > -- > > Where p points to the start of a string passed. 's' is never > > dereferenced, only used for comparing purposes (while(p > s) or > > something similar) > > Computing a pointer before the beginning of an array invokes UB; > computing a pointer just past the end of an array does not. > > This is mentioned in passing in the answer to question 6.17 in the > comp.lang.c FAQ, <http://www.c-faq.com/> (can anyone find a more > explicit reference?). > > The standard's rather long-winded explanation of this is in C99 > 6.5.6p8 (quoting from n1256; there are no change bars on this > paragraph): > > When an expression that has integer type is added to or subtracted > from a pointer, the result has the type of the pointer operand. If > the pointer operand points to an element of an array object, and > the array is large enough, the result points to an element offset > from the original element such that the difference of the > subscripts of the resulting and original array elements equals the > integer expression. In other words, if the expression P points to > the i-th element of an array object, the expressions (P)+N > (equivalently, N+(P)) and (P)-N (where N has the value n) point > to, respectively, the i+n-th and in-th elements of the array > object, provided they exist. Moreover, if the expression P points > to the last element of an array object, the expression (P)+1 > points one past the last element of the array object, and if the > expression Q points one past the last element of an array object, > the expression (Q)-1 points to the last element of the array > object. If both the pointer operand and the result point to > elements of the same array object, or one past the last element of > the array object, the evaluation shall not produce an overflow; > otherwise, the behavior is undefined. If the result points one > past the last element of the array object, it shall not be used as > the operand of a unary * operator that is evaluated. I have thought about this, and clearly the standard talks about arrays. If we have foo[N], then foo + N is a valid pointer that cannot be dereferenced. However, in foo = &bar; foo+1 is *not* a valid pointer because &bar is a pointer, not an array. Therefore, in OPs example, the expression cannot be computed and does invoke undefined behavior. Here is an example of what i am trying to say -- int * foo; int bar; int baz[N]; foo = baz + N; /* valid */ foo = &bar + 1; /* invalid */ foo = &bar; /* valid */ foo++; /* invalid */ -- |
|
|
|
#19 |
|
Messages: n/a
Hébergeur: |
vipps...@gmail.com wrote:
> Peter Nilsson <ai...@acay.com.au> wrote: > > ... > > int *p = my_array; > > int const *const pend = my_array + sizeof my_array/ > > sizeof*my_array; > > do *p++ = 42; > > while (pend != p); > > > At no stage is pend dereferenced; and the loop exits on > > p == pend, so the address is not dereferenced by p > > either. > > Ah, I was a bit confused I guess. > However, OP _does_ dereference it. Not in that snipped, > but in 4) > > > 4) *(&my_array+ 1) decays to the address of the first > > element in the non-existant array after the current one, > > which is also the "pend" address for the array that > > actually exists. Again, it is not derefenced... The type of my_array is int[] The type of &my_array is int (*)[] The type of &my_array + 1 is int (*)[] The type of *(&my_array + 1) is int [] The last expression will decay to an int * when used in the assignment to pend. At no stage is that pointer dereferenced. -- Peter |
|
|
|
#20 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 4:15 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote: > > Peter Nilsson <ai...@acay.com.au> wrote: > > > ... > > > int *p = my_array; > > > int const *const pend = my_array + sizeof my_array/ > > > sizeof*my_array; > > > do *p++ = 42; > > > while (pend != p); > > > > At no stage is pend dereferenced; and the loop exits on > > > p == pend, so the address is not dereferenced by p > > > either. > > > Ah, I was a bit confused I guess. > > However, OP _does_ dereference it. Not in that snipped, > > but in 4) > > > > 4) *(&my_array+ 1) decays to the address of the first > > > element in the non-existant array after the current one, > > > which is also the "pend" address for the array that > > > actually exists. > > Again, it is not derefenced... > > The type of my_array is int[] > The type of &my_array is int (*)[] > The type of &my_array + 1 is int (*)[] > The type of *(&my_array + 1) is int [] > > The last expression will decay to an int * when used in the > assignment to pend. At no stage is that pointer dereferenced. What i ment is that (&myarray + 1) is dereferenced, which would be invalid, and if my reply to mr Thompson is correct, then even computing &my_array+1 is invalid. Remember; We are no longer talking about arrays but pointers, the pointer-after-the-last-element rule does not apply. |
|
|
|
#21 |
|
Messages: n/a
Hébergeur: |
vipps...@gmail.com wrote:
> Keith Thompson <ks...@mib.org> wrote: > > ...The standard's rather long-winded explanation of this > > is in C99 6.5.6p8 ... > > I have thought about this, and clearly the standard talks > about arrays. If we have foo[N], then foo + N is a valid > pointer that cannot be dereferenced. > However, in foo = &bar; foo+1 is *not* a valid pointer > because &bar is a pointer, not an array. The cited paragraph is specifically about adding an integer to a pointer. Arrays decaying to pointers is in 6.3.2.1p3. -- Peter |
|
|
|
#22 |
|
Messages: n/a
Hébergeur: |
On Feb 6, 4:24 am, Peter Nilsson <ai...@acay.com.au> wrote:
> vipps...@gmail.com wrote: > > Keith Thompson <ks...@mib.org> wrote: > > > ...The standard's rather long-winded explanation of this > > > is in C99 6.5.6p8 ... > > > I have thought about this, and clearly the standard talks > > about arrays. If we have foo[N], then foo + N is a valid > > pointer that cannot be dereferenced. > > However, in foo = &bar; foo+1 is *not* a valid pointer > > because &bar is a pointer, not an array. > > The cited paragraph is specifically about adding an integer > to a pointer. Arrays decaying to pointers is in 6.3.2.1p3. I don't think you understand what I am trying to say. -- char foo[N]; /* array of N length */ char c; char *bar = foo + N; /* valid */ bar = &c + 1; /* invalid */ -- I understand the matter with arrays and the pointer over the last element, but I believe that does not apply for pointers. Furthermore, I think you don't understand arrays since you claimed my_array to be int[] while it is int[X], and &my_array to be int (*)[] while it is int (*)[X]. |
|
|
|
#23 |
|
Messages: n/a
Hébergeur: |
vippstar@gmail.com wrote: > On Feb 6, 2:09 am, William Ahern <will...@wilbur.25thandClement.com> > wrote: <snip> > > I don't use NULL 'cause I don't know to what it's defined. Usually the NULL > > macro expands to `0' or `(void *)0'. The former, typical on *BSD, needs to > > be cast when used from a comma expression and the return type of your > > function is a pointer, otherwise it might be evaluated as an int, which > > might not be wide enough if (sizeof int != sizeof (void *)). > I don't think that can happend. It does on most of my machines. > > > > if (some_test_fails) > > return (errno = ESOMETHING), (void *)NULL; > As i said, NULL does not need to be casted here. > You don't need the comma operator either. the expression '(x, y, z)' > evaluates to the type of z with value z. Indeed, exactly as 6.5.17 states. But, what is the type of an unadorned `0'? (Note, as I mentioned earlier, on *BSD NULL is defined as simply `0'.) Is it a pointer or an integer? In a comma expression, it's an integer, because there's no other context to suggest otherwise. Why? I assume because the language is too rigid. 6.5.17 must be satisified before 6.8.6.4(3)--which specifies how expression types are coerced when used with return. Try it in your compiler. GCC, for instance, exhibits this behavior. cc test.c -o test test.c: In function ‘foo’: test.c:4: warning: return makes pointer from integer without a cast This is one of the primary reasons I don't use NULL. Maybe its foolhardy (likely, even), but if you truly grok how type coercion works in C, then the visual syntactical benefit of using NULL loses much of its value. It's sort of like seeing the red headed woman in a screen of falling green glyphs. (Obligatory The Matrix reference.) > > Likewise, you have to cast it when passing as a vararg. > You have to cast 0 to (char *)0 too. > > execl("/bin/true", "true", (char *)NULL); > > > > Usually I just use an unadorned `0' in my code. I do this in part because, > > like the OP, I don't feel like including <stddef.h> or <stdio.h> or any > > other header that I don't need. > That in my opinion is very bad practise for a project. > > I comment my include statements to describe > > what I'm [trying] to import; it got really old doing: > > > > #include <stddef.h> /* NULL */ > > > > But, to each his own. I don't know very many people who do this. I'm okay > > being alone ![]() > Other programmers in your project might not be okay with that thought, > ask them first. > As for me, I wouldn't. Consider > -- > char *p; > /* ... */ > if(p == 0) /* did you really mean p == 0 or *p == 0 ? */ > if(p == NULL) /* clearly ment p == NULL */ (1) Though you snipped the imaginary intermediate code, I strive to make all of my function definitions fit within an 80x24 terminal window. So the declaration is usually as easy to spot as it is here. (2) How does that compare to? if(!p) ... Granted, I do indeed use the former, but my point is that very likely you (or most other people) would find it less objectionable. But I don't expend any effort to ease a programmer's cognitive load a |