0

I'm studying pointers in C language with Ted Jensen's "A TUTORIAL ON POINTERS AND ARRAYS IN C" manual. It's a very prickly argument.

Question 1. I have come to program 3.1 and would like a clarification. This is the program:

#include <stdio.h> 
char strA[80] = "A string to be used for demonstration purposes";
char strB[80]; 
int main(void) { 
char *pA; 
char *pB;
puts(strA); 
pA = strA; 
puts(pA); 
pB = strB;
putchar('\n');
while(*pA != '\0')
{ 
*pB++ = *pA++; 
} 
*pB = '\0';
puts(strB);
return 0; 
}

Regarding the line pA = strA;, the book says "We then [point] the pointer pA at strA. That is, by means of the assignment statement we copy the address of strA [0] into our variable pA", which is what I don't understand.

To copy the address of strA[0] into our variable pA via the assignment declaration, shouldn't we write pA = & strA?

Question 2. The expression c = * ++ p; increases the value of p or the address of p?

Does the indirection operator (*) not indicate the value of the pointed variable?

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
claud
  • 1
  • 2
  • 2
    Types of the variables are relevant. Please [edit] your question and post the actual code. – user694733 Nov 05 '21 at 12:22
  • [Relevant and probably a dupe](https://stackoverflow.com/q/2094666/332733) – Mgetz Nov 05 '21 at 12:25
  • 2
    Your question needs to be self-contained - it should show the code you are asking about. I have never heard of this tutorial and I'm not gonna go look for it either. – Lundin Nov 05 '21 at 12:25
  • For arrays always applies that their address and the address of their first element is the same – alike for structs and their first member, by the way. However the *type* of the pointers containing these addresses is not the same, `&strA` is a pointer to array of specific size while `&strA[0]` is a pointer to the underlying type (to which arrays decay implicitly as well in the appropriate context). This is why you have to be careful when taking addresses of arrays instead of letting them decay to pointer. – Aconcagua Nov 05 '21 at 12:26
  • `++p` does not increase any addresses. `p` contains a value that is equal to some specific memory address. `++p` adds the size of the underlying type to this value, which then gets equal to *another* memory address – precisely the one of the element immediately neighbouring the one `p` pointed to before being incremented. `*++p` first increments the pointer and dereferences it afterwards, i.e. you access the object being stored at that specific address. – Aconcagua Nov 05 '21 at 12:30
  • @claud you should attach code, Problem is not clear –  Nov 05 '21 at 12:43
  • It might help to draw an array on paper and the addresses below. Assume a 16-bit value (`short` on typical modern HW or `int` on quite a number of micro-controllers) starting at address 1024. You'll notice that array itself and its first element (short/int) share exactly the same address. So if *only considering the value* it doesn't actually matter if you assign `&strA` or `&strA[0]`, however, as mentioned already, it matters as far as concerning the *type* of the pointer. The latter gives you the correct type of pointer, that's why it has been used in the book. – Aconcagua Nov 05 '21 at 12:50
  • If you now have a variable `p` and you assign it the array (which decays to pointer to its first element then), then you copy the value 1024 into. `++p` increases the value stored in the pointer by the size of one element, in this case two bytes, so it would contain 1026. If you now dereference (`*++p;` is equivalent to `++p; *p;`), you access (read or write to) the value at this location, i. e. the next element in the array. – Aconcagua Nov 05 '21 at 12:53
  • OT: Your code is poorly formatted. Proper code formatting is very important. – Jabberwocky Nov 05 '21 at 13:50

4 Answers4

2

To copy the address of strA [0] into our variable pA via the assignment declaration, shouldn't we write pA = & strA?

&strA is the address of strA. &strA[0] is the address of strA[0]. These are the “same” in the sense they point to the same place in memory, but they have different types, and the compiler would complain if we wrote pA = &strA when the type of pA is a pointer to the type of the elements of strA.

When we write pA = strA, the array strA is automatically converted to a pointer to its first element, so pA = strA is equivalent to pA = &strA[0].

Question 2: the expression c = * ++ p; increases the value of p or the address of p?

The C grammar organizes this as c = *(++p);, and ++p increases the value of p. If p is a pointer, it increases the value of that pointer. The * operator uses the increased value.

Be careful about speaking of the address of a pointer. The value of the pointer is an address, but you should not say that is the address of the pointer. The pointer is itself an object in memory, and so it has an address in memory where its value is stored. The address where a pointer is stored is different from the address stored in it. The “address of a pointer” is the address where the pointer is stored, not the value of the pointer.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

It's simply because strA, which is char array, is a pointer. An array's "value" is actually nothing more than the address of the first element of array.

When you assign address of primitive data type(int, char, etc..), you should assign it in the way you described.

int x;
int *pA;
pA = &x;

When you assign address of pointer data type(array, etc..), you should assign it without & operator, since the value is in itself the address.

int x[10];
int* pA;
pA = x;

Original code

#include <stdio.h>
char strA[80] = "A string to be used for demonstration purposes";
char strB[80];
int main(void)
{
    char *pA; /* a pointer to type character */
    char *pB; /* another pointer to type character */
    puts(strA); /* show string A */
    pA = strA; /* point pA at string A */
    puts(pA); /* show what pA is pointing to */
    pB = strB; /* point pB at string B */
    putchar('\n'); /* move down one line on the screen */
    while(*pA != '\0') /* line A (see text) */
    {
        *pB++ = *pA++; /* line B (see text) */
    }
    *pB = '\0'; /* line C (see text) */
    puts(strB); /* show strB on screen */
    return 0;
}
Shambhav
  • 813
  • 7
  • 20
jeff pentagon
  • 796
  • 3
  • 12
  • 2
    This is C, there are no *'references'*. This wording is dangerous, as other languages (C++, Java, C#, ...) *do* know about, but understand a different (even though similar) concept by. – Aconcagua Nov 05 '21 at 12:55
  • @Aconcagua thx for correction, correct term would be derived data type – jeff pentagon Nov 05 '21 at 13:17
  • @jeffpentagon, C does have a concept of type derivation, but the term "derived data type" is almost never used with respect to C, and I am sure that would not be correct here. I think you have in mind the type of the object to which a given pointer points, and oddly enough, C doesn't actually have a specific term for that. – John Bollinger Nov 05 '21 at 13:41
0

In C, when you write :

char strA[80];

Memory is allocated for your table. This is an example for you to try and visualize what it looks like.

[0] 1st Element [1] 2nd Element [2] 3rd Element [....] .... [n] nth Element
0000 0001 0002 0003 n

strA is a pointer to the address where your table starts in the memory (0000 in our example), which is the same as the address of its first element strA[0].

So when you write

pA = strA

you are actually copying the first element's address (0000 in our example) to pA

0

To copy the address of strA [0] into our variable pA via the assignment declaration, shouldn't we write pA = & strA?

Arrays are weird and don't behave like other types.

The expression strA "decays" from type "N-element array of char" to "pointer to char", and the value of the expression is the address of the first element. This "decay" doesn't happen when the array expression is the operand of the sizeof or unary & operators, so the expression &strA has type "pointer to N-element array of char", or char (*)[N], which is not the same as char *. The address value is the same (the address of an array is the same as the address of its first element), but the types are different.

Assuming the declarations

char str[N]; // for some size N
char *p;

when you write

p = str; // char * = char *

the expression str has type "N-element array of char"; however, since str is not the operand of the sizeof or unary & operators, it "decays" to type char * and evaluates to the address of the first element. It’s equivalent to writing

p = &str[0];  // char * = char *

There is a reason for this - C was derived from an earlier language named B, and in B array types had a dedicated pointer to the first element - when you declared an array in B like

auto a[N];

what you got in memory was

   +---+
a: |   | -------+
   +---+        |   
    ...         |
   +---+        |
   |   | a[0] <-+
   +---+
   |   | a[1]
   +---+
    ...

and the array subscript operation a[i] was defined as *(a + i) - given the starting address a, offset i elements from that address and dereference the result.

When he was designing C, Ritchie wanted to keep B's array behavior, but he didn't want to keep the explicit pointer that behavior required. When you declare an array in C like

int a[N];

what you get in memory is

   +---+
a: |   | a[0]
   +---+  
   |   | a[1]
   +---+
    ...

Instead of setting aside space for an explicit pointer to the first element, the compiler replaces any occurrences of the expression a with a pointer to a[0]. So a[i] is still defined as *(a + i). Unfortunately, this means that arrays lose their "array-ness" under most circumstances and most of the time what you're dealing with is a pointer.

Question 2: the expression c = * ++ p; increases the value of p or the address of p?

This gets a little complicated. To answer as asked, it increases the value of p - it sets p to point to the next object in a sequence. This is probably better explained with a concrete example.

Assume the following declarations:

char str[] = "foo";
char *p = str;

then the following are true:

     +---+
str: |'f'| str[0]   <--- p
     +---+
     |'o'| str[1]   <--- p + 1
     +---+
     |'o'| str[2]   <--- p + 2
     +---+
     | 0 | str[3]   <--- p + 3
     +---+

 p == &str[0]             // char * == char *
*p ==  str[0] == 'f'      // char   == char   == char

  p + 1  == &str[1]
*(p + 1) ==  str[1] == 'o'

  p + 2  == &str[2]
*(p + 2) ==  str[2] == 'o'

The result of the expression p + 1 is a pointer to str[1], the result of the expression p + 2 is a pointer to str[2], etc.

c = *++p; is roughly equivalent to writing

tmp = p + 1;
c = *tmp;
p = p + 1;

with the caveat that the updates to c and p can happen in any order - they may even be evaluated simultaneously (either interleaved or in parallel).

In this case, it means we assign the value of *(p + 1) ('o') to c, and update p to point to str[1], leaving us with this:

     +---+
str: |'f'| str[0]   
     +---+
     |'o'| str[1]   <--- p
     +---+
     |'o'| str[2]   <--- p + 1
     +---+
     | 0 | str[3]   <--- p + 2
     +---+

 p == &str[1]
*p ==  str[1] == 'o'

  p + 1  == &str[2]
*(p + 1) ==  str[2] == 'o'

  p + 2  == &str[3]
*(p + 2) ==  str[3] == 0
John Bode
  • 119,563
  • 19
  • 122
  • 198