Pointers and arrays in C

Go To Last Post
24 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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!

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

Last Edited: Thu. Jul 15, 2021 - 04:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

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:

 

  1. the fact that array is an unmodificable lvalue (end of story, no need to consider it further)
  2. array-to-pointer conversion, whose result is an rvalue (as I decribed above)

 

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.

 

 

Dessine-moi un mouton

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Knew I should have locked this.........

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

More on arrays & pointers in the C FAQ:

 

http://c-faq.com/aryptr/index.html

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
There's no need to involve "constants" here. 
Others use the terms mutable an immutable for this ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

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...

Dessine-moi un mouton

Last Edited: Fri. Jul 16, 2021 - 09:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

jgmdesign wrote:
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.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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, so they are indeed different.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
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.
Thank you.

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.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:

Similarly, since ++ptr and ptr++ are both equivalent to ptr + 1

MarkThomas wrote:
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.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

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.

 

Dessine-moi un mouton

Last Edited: Sat. Jul 24, 2021 - 07:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IANAL, however, in many disciplines, multiple paths that give the same results are called equivalent.

 

https://en.wikipedia.org/wiki/Eq...(chemistry)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ron_sutherland wrote:

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.

Dessine-moi un mouton

Last Edited: Sun. Jul 25, 2021 - 03:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

Computers don't make errors - What they do they do on purpose.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

El Tangas wrote:

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. 

Dessine-moi un mouton

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

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 (*). 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
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).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:
It's the programmer's responsibility to keep within bounds ...
maybe aided by C's Annex K (bounds checking)

ISO/IEC 9899:201x (page 600)

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.

Last Edited: Mon. Nov 15, 2021 - 10:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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.