As the topic comes up rather often
Some great reading in this:
https://pdos.csail.mit.edu/6.828...
Jim
EDIT: Thanks Lee for the link!
As the topic comes up rather often
Some great reading in this:
https://pdos.csail.mit.edu/6.828...
Jim
EDIT: Thanks Lee for the link!
Written with great patience, but sometimes takes shortcuts that might lead to confusion.
Similarly, since ++ptr and ptr++ are both equivalent to ptr + 1
I guess I get what the author was trying to say, but this is rather sloppy writing.
For example, while we can write `ptr = my_array;` we cannot write `my_array = ptr;` The reason is that while `ptr` is a variable, `my_array` is a constant.
This is misleading. There's no need to involve "constants" here. The term "constant" is reserved in C. It has very specific meaning. Hijacking formal terms is not a good idea - it will only breed extra confusion. Yes, I see that later in the text the author does provide additional explanations for their use of this term, but still...
The reason we cannot do `my_array = ptr;` is quite different: array on the left-hand side will be subjected to an implict array-to-pointer conversion, which is what produces the aforementioned pointer. But conversion results in C are always rvalues. You cannot assign to an rvalue. That's all there is to it.
For another example, you can do `c = a + b`, but you cannot do `a + b = c`. For the very same reason.
This raises an interesting problem. Since my_array is a named region of storage, why is my_array in the above assignment statement not an lvalue? To resolve this problem, some refer to my_array as an "unmodifiable lvalue".
Yes, that's correct. Array is a unmodifiable lvalue. But, again, this has nothing to do with "constants". And... it is actually an interesting language-lawyer-grade question: in case of `my_array = ptr;` what kicks in first to trigger the error:
I'm pretty sure 2 is the correct answer. First, we apply all necessary implict conversions. And only after that we start verifying the constraints of the assignment operator.
For example, while C will not permit the comparison of a pointer to type integer with a pointer to type character, for example, either of these can be compared to a void pointer
Kudos to the author for reiterating the fact that C type system is sufficienly strict to prevent direct comparisons between incompatible pointer types. There is a widespread (and incorrect) belief that only C++ is strict about it, while C allows mixing pointer types arbitrarily.
char my_string[40]; my_string[0] = 'T'; my_string[1] = 'e'; my_string[2] = 'd': my_string[3] = '\0';Since writing the above code would be very time consuming, C permits two alternate ways of achieving the same thing. First, one might write:
char my_string[40] = {'T', 'e', 'd', '\0',};But this also takes more typing than is convenient. So, C permits:
char my_string[40] = "Ted";In all of the above cases, the same thing happens. The compiler sets aside an contiguous block of memory 40 bytes long to hold characters and initialized it such that the first 4 characters are Ted\0.
Well... One might remark that there's quite a difference between the first version and the latter two. The first version does not initialize the whole array. The latter two are required to initialize the whole thing, i.e. fill the tail portion of the array with zeros all the way to the end. But this is beside the topic of the article.
Knew I should have locked this.........
JIm
There's no need to involve "constants" here.
AndreyT wrote:
There's no need to involve "constants" here.
Others use the terms mutable an immutable for this ;-)
That still misses the point. The result of automatic implicit array-to-pointer conversion is an rvalue (in C all conversions produce rvalues). Rvalues cannot be mutable or immutable. In the realm of C "mutability" is simply not applicable to rvalues at all.
Probably the only way to "challenge" this state of affairs in C (С99 and later) is by doing something like this
struct S { int a[10]; }; struct S foo(void) { return (struct S) {}; } int main(void) { foo().a[5] = 0; // what is this? }
The language standard cops out of this conundrum by simply stating that the behavior is undefined. http://port70.net/~nsz/c/c11/n15...
Knew I should have locked this.........
Why? Fortunately the amount of language lawyers in this forum is way below critical mass. I don't think an unhealthy discussion can actually gain momentum on this array vs pointer subject.
Similarly, since ++ptr and ptr++ are both equivalent to ptr + 1
I believe one of them increments the ptr after it is used and the other before it is used, so they are indeed different.
That still misses the point. The result of automatic implicit array-to-pointer conversion is an rvalue (in C all conversions produce rvalues). Rvalues cannot be mutable or immutable. In the realm of C "mutability" is simply not applicable to rvalues at all.
I've had the occasional discussion with someone claiming that pointers and arrays are the same.
This might help.
Though one cannot assign to an array by its name,
one can do so by the name of a containing struct:
struct S { int a[10]; } fred, greg; void func(void) { greg=fred; } // func
Recent C++ STL libraries have an array template for just this reason.
Regarding increments:
Among rvalues ++ptr, ptr++ and ptr+1;
ptr++ is the odd man out: it produces ptr, not ptr+1.
Among the corresponding side-effects, ptr+1 is the odd man out:
'tis the only one that does not increment ptr.
The three are distinct.
That said, ptr++ and ++ptr are often used as complete expressions.
In that context, their rvalues do matter and they are equivalent.
I've probably been stating what to many avrfreaks is the obvious.
To someone struggling with arrays and pointers,
perhaps it is not.
AndreyT wrote:
Similarly, since ++ptr and ptr++ are both equivalent to ptr + 1
I believe one of them increments the ptr after it is used and the other before it is used
Correct
so they are indeed different.
but the point was that they both increment the pointer by one. So they are both equivalent to ptr + 1 - the only difference is whether you place the ptr + 1 before or after.
but the point was that they both increment the pointer by one. So they are both equivalent to ptr + 1 -
`++` designates a modifying operation, `+` designates a non-modifying operation. This is an important fundamental difference that should not be ignored in any context.
There's nothing wrong with referring to natural similarities between `++` and `+ 1`. After all, the former is defined through the latter. But calling them equivalent goes a bit too far.
IANAL, however, in many disciplines, multiple paths that give the same results are called equivalent.
https://en.wikipedia.org/wiki/Eq...(chemistry)
IANAL, however, in many disciplines, multiple paths that give the same results are called equivalent.
Well, firstly `++a` and `a++` do not give the same result (using the formal C concept of result of an expression).
And, secondly, a more general multi-discipline idea of "result" normally includes the full set of consequences. In which case the "result" of `++a` is different from the "result" of `a + 1` since, as I said before, `++a` modifies `a` and `a + 1` doesn't.
In other words, whatever idea of "result" you adopt, the results of `++a`, `a++` and `a + 1` are never identical.
Similarly, since ++ptr and ptr++ are both equivalent to ptr + 1 (though the point in the
program when ptr is incremented may be different), incrementing a pointer using the
unary ++ operator, either pre- or post-, increments the address it stores by the amount
sizeof(type) where "type" is the type of the object pointed to. (i.e. 2 for an integer, 4 for a
long, etc.).
The author more likely wanted to point out that incrementing pointer by 1 results the address is incremented by sizeof(type), which means it points to the next element and it won't get "shifted"/"misaligned". Sure they are not equivalent to ptr+1 but they are "roughly" equivalent to ptr+=1; ++ptr; ptr++; in this case resulting ptr will be moved to the next element in all cases...
Btw in the C++ is highly recommended to use ++whatever; for incrementation, as whatever might have quite expensive copy constructor and by using whatever++; we just created a copy that will be discarded immediately
In other words, whatever idea of "result" you adopt, the results of `++a`, `a++` and `a + 1` are never identical.
Would you say a+=1 and ++a are equivalent?
AndreyT wrote:
In other words, whatever idea of "result" you adopt, the results of `++a`, `a++` and `a + 1` are never identical.
Would you say a+=1 and ++a are equivalent?
Yes. These are equivalent by definition.
I have a question related to pointers and arrays.
Suppose that I have a set of arrays of differing lengths. The arrays are all arrays of the same data type (uint8_t). The question is whether or not I can use a single pointer to point to one chosen array out of the set.
I guess that the question is really about how pointers work. I know that when you increment a pointer, that increment depends on the data type. But I am not clear about about whether length is part of pointer behavior. I seem to recall that there is no length checking for C pointers, but whether there is some other length-dependent behavior is unclear to me. Or, for that matter, when the pointer-referenced objects are not strictly clones of each other.
Thanks
Jim
You are over thinking things. A pointer is nothing more than the address of something in memory. The same pointer could point anywhere in any of the three arrays or even somewhere else entirely. It really is nothing but a 16 bit (AVR) number that is interpreted as an address.
Of course the type that it is pointing at (given when you declare/define the pointer) also serves two purposes. One is to say how many bytes the 16 bit number increments/decrements by if you ++/-- (or use [n] access) and also how many bytes are read/written if you make an access through the pointer (*).
But I am not clear about about whether length is part of pointer behavior.
Nope. Pointers do not carry any information regarding de boundaries of the array. It's the programmer's responsibility to keep within bounds (or else bad things will probably happen).
It's the programmer's responsibility to keep within bounds ...
ISO/IEC 9899:201x (page 600)
Some examples..
char text[] = "Hello World"; int nums[] = { 1347, 2831, 4266, 5953 }; typedef struct { char c; // 1 byte int n; // 2 bytes long l; // 4 bytes float f; // 4 bytes (so 11 bytes in total) } data_t; data_t data[] = { {'A', 1234, 0xBABEFACE, 3.14}, {'X', 456, 0xACEB1ADE, 2.17}, {'Z', 9876, 0xDEADBEEF, 1.0}, }; char * pChar; int * pInts; data_t * pData; int main(void) { data_t localDat; pChar = &text[2]; // char pointer point into chars (first 'l') pChar += 2; // move pointer on 2 elements so now holds addr 'o' pInts = &nums[3]; // points to last value (5953) TCNT1 = *pInts; // the 2 bytes that make up 5993 read and written to TCNT1 pInts -= 3; // pointer moved back 3 elements so points to 1347 pData = &data[0]; // or just "pData = data" in fact localDat = *pData; // 11 bytes ('a', 1234 etc) copied to local var pData += 2; // pointer moved on by 22 bytes to point at 'Z', 9876 etc // but you can do "naughty" things too: pChar = (char *)&data[1]; // points to 'X' in 'X', 456 etc PORTB = *pChar; // write just 'X' to PORTB pChar++; // point to lower byte of 456 (0xC8 of 0x1C8) PORTB = *pChar; // write 0xC8 to PORTB (only 1 byte copied source to dest) pInts = (int *)pChar; // while pChar happens to point to lowest address of 456 assign to int pointer TCNT1 = *pInts; // 456 written to TCNT1 as 2 bytes read/written pData = (data_t *)text; // pointer the data_t pointer to a bad place *pData = localDat; // then write 11 bytes through it - this is VERY bad indeed! }
Pointers are very powerful but also very dangerous as there's basically no control over them. You can point them anywhere in fact you could even just pick a random number:
pData = (data_t *)394; *pdata = localDat;
and blast 11 bytes into the middle of memory somewhere.
But at the end of the day pChar, pInts, pData are all just 2 byte numbers - it's how you abuse those 2 bytes numbers that really matters.
As Cliff said, a pointer just points - it neither knows nor cares what other data structures the pointed-to location might happen to fall within.
This is both the power and the danger of C pointer!
If you are concerned about only letting your pointers point to certain things, then that's something that you will have to implement in your code...
My concern was really simple: whether or not there would be problems with a single pointer pointing to arrays of varying lengths. That was thoroughly resolved with the answer I expected but did not know with certainty.
Thanks, folks! Now I can proceed!
Jim
It is better to collect common formulas about this in a short article. So we just replace the parameters and apply to our own codes.
Microchip also has some good lessons on C.