Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused about the string pointer iteration in my lecture notes

Tags:

c

This is the chunk of code I've copied from my lecture notes

/* 2D processing */
printf("\n");
for (i=0; i<3; i++)
    for (j=0; j<3; j++)
        printf("%d ", *(*(ar+i)+j) );

Since ar is the pointer referring the address location and *(ar+i) is actually refers to the content of the address location of ar+i, but I don't understand how it will work with *(ar+i)+j, it's like content + a number.

one more thing is,

(1) char *ptr; ptr = "This is a string";
(2) char *ptr = "This is a string";

Why (1) can not be char *ptr; *ptr="this a string" when declaration and assignment are separated?

Thank you very much in advance.

like image 466
Steven Li Avatar asked Jan 12 '23 00:01

Steven Li


2 Answers

In the first case ar is most probably a pointer to a pointer (declared as int **ar;), thus *(a + i) is a pointer or an array and *(*(a + i) + j) is the element.

In C and C++ however there is a rule that says that an array can implicitly "decay" to a pointer to the first element in many cases and thus it's also possible that ar has been declared in other ways:

int **ar;     // (1) A pointer to a pointer
int *ar[3];   // (2) An array of pointers
int ar[3][3]; // (3) An array of arrays

When writing ar + i in case ar is an array it decays to a pointer to the first element (case 2 and 3) and the result is the address of a pointer (case 2) or the address of an array (3). Using the dereference operator * then gets the element that is either a pointer or an array.

When you the add j and the element is an array (case 3) this array also decays to a pointer to its first element before computing the addition. Thus to recap, dependin on how ar is defined:

ar                // (1) a pointer to a pointer,
                  // (2) a pointer to array,
                  // (3) an array of array

ar + i            // (1) and (2) address of a pointer
                  // (3) address of array

*(ar + i)         // (1) and (2) a pointer
                  // (3) an array

*(ar + i) + j     // address of an integer in any case

*(*(ar + i) + j)  // an integer in any case

Don't worry if this seems confusing, because it is. Normally when you use *(x + i) is because x is a pointer however and that's why I guessed that ar has been declared as int **ar;. Note also that in C and C++ *(x + i) is perfectly equivalent to x[i] or even to i[x].

In the second case the reason is how the declaration are written in C.

char * s = "foo";

should be read as

(char *s) = ("foo");  // Not legal C, just to show type declaration and
                      // initialization

in other words the * is part of the type declaration of s, not an operation applied to s.

Note however that

char *s, t;

declares s as a char * and t as a char.

Type declarations are probably the most powerful but difficult part of C syntax because it's not obvious what is the part being declared. For example

int *(*f)(int *(*g)(int x));

is a valid declaration for the type of f and the names g and x are instead irrelevant and could be omitted.

int *(*f)(int *(*)(int));  // Same as above

The type of f in this case is a pointer to a function accepting a pointer to a function accepting an int and returning a pointer to an int, and returning a pointer to an int.

My guess is that the vast majority of C programmers would however need to think a bit before being able to decipher that :-D

like image 83
6502 Avatar answered Feb 02 '23 07:02

6502


To elaborate a bit:

  • a[b] is always a shorthand of *(a+b).

If you combine that with the pointer arithmetics rules (or derive these from there), any of a and b can be a pointer, the other one an integer, denoting the offset.

With this rule, you can transform an expression like *(*(ar + i) + j) to *(ar[i] + j) and then to ar[i][j].

Alas, you don't tell us what your ar is, but we can tell the following:

  • As ar[i][j] is a valid expression, ar[i] is a pointer or an array which has decayed into a pointer in this expression.
  • The same counts for ar.
  • In order for this to be true, ar can be any of

    • type** ar
    • type (*ar)[]
    • type ar[][]
    • type * ar[]

    where type is the type of each of the components.

    These four alternatives all have a different memory layout, but are accessed the same way.

That's how pointer arithmetics and array decaying works. Best have a look at a good book or tutorial.

The second question should better be a separate question.

It is just because of the assignment and initialization rules of C.

You define a pointer

  • either uninitialized: type * p;
  • or initialized: type * p = <init value>;

but

p = <new value>;

is how you assign it.

Keep in mind that a string literal is transformed into the address of that string in its readonly memory section.

like image 38
glglgl Avatar answered Feb 02 '23 07:02

glglgl