The stumbling block of C language pointer syntax

I will introduce the points that are easy to trip in the C language pointer syntax, and I will also write my own method that it is easy to understand if you think in this way. Please note that it does not explain the contents of the standard.

What is a pointer?

A pointer is a reference to an object when it can be accessed with some logical location information. A well-known example is a pointer to a memory address in C / C ++. (ja.wikipedia.org)

Hmmmm. Pointer = memory address. Note) Pointed out in the comments.

Pointer type variable

int *pointer;

Yes, this is a pointer. I don't really understand what it means. Is * a pointer? Some people think that * pointer is a pointer! Some people may think. No, did you just say that pointers are memory addresses in the first place? Where is the memory address in this? ** What is a pointer! ?? ** **

In order not to be, here, "pointer type" and "pointer type variable" are described separately. By the way, in English wikipedia, pointers are written as objects, so when you simply express them as pointers, you can think of them as "pointer type variables".

In computer science, a pointer is a programming language object, whose value refers to (or "points to") another value stored elsewhere in the computer memory using its address.(en.wikipedia.org)

Since the C language declaration is variable type variable name, the variable type is ʻint *and the variable name ispointer`.

pointer is ** pointer type variable **. Not * pointer. And ʻint * becomes ** pointer type **. By the way, what should I call *` at this time? Pointer type qualifier?

In order to clearly separate the type and variable name, it becomes clearer to write the pointer type declaration as follows.

int* pointer;

But

int* pointer1, other;

If you write, other becomes an int type. Let's be careful.

Indirect reference

int val;
int* pointer_of_val = &val;

If you write, pointer_of_val is initialized with the address of val. To access val's memory using pointer_of_val

*pointer_of_val = 10;

Write. At this time, * is called an indirect operator. Yes, the * at the time of ** pointer type declaration and the * at the time of indirect reference are different. ** Even if they are the same *, the roles are opposite. Since it is the opposite, you can remember that "when ʻint * p;, * p` is an int type". It's done well, but that doesn't make me realize that the two are different. Gununu.

Passing arguments by reference

It is said that function arguments can be passed by value or by reference, but ** function arguments can only be passed by value **.

void func(int* pointer){
	*pointer = 0;
}

At this time, a pointer type variable called pointer receives the address to the int type passed by value. * pointer = 0 indirectly refers to the passed address and rewrites the value. This is just called passing by reference for convenience. ** Passing by reference is not a grammar, but a kind of usage. ** **

In addition, C ++ has reference types, but that's another story. It's complicated.

Array pointer type

int *array_of_pointer[10];

Is this an int pointer type array? Is it a pointer type of int array? Which one? This is a puzzle that arises because the array symbol is on the right side of the variable.

I read like this. Be careful because it is my style.

Translation in the brain


int *array_of_pointer[10];
↓
int* array_of_pointer[10];
↓
int*[10] array_of_pointer; /*How to read*/

It's not actually replaced like this, but if you bring the array to the right end of the type, it may be easier to understand that the int pointer type element is an array of 10.

Well then, what about this?

int (*pointer_of_array)[10];

This replaces this in the brain like this

Translation in the brain


int (*pointer_of_array)[10];
↓
int[10](*pointer_of_array);
↓
int[10]* pointer_of_array;

Since () has priority in the type definition, [10] comes to the left side first and becomes ʻint [10](* pointer_of_array) `. Then () is solved and it becomes as above. In this case, it can be interpreted as a pointer type to 10 arrays of int type.

Maybe to improve this, in C #

csharp.cs


int[] array = new int[5];

The array symbol is written on the left side. Now I like C #.

Pointer type to const type

(This chapter is confusing, so I modified it) const can be modified on either the right or left side.

/*Example where const qualifies the left side*/
int const constant_int1;

/*Example of const qualifying the right side (same meaning as above)*/
const int constant_int2;

When combined with the pointer type, it becomes as follows.

/* (constant int)Pointer type to type*/
int const *pointer_to_constant_int1;
const int *pointer_to_constant_int2;

/*Constant pointer type to int type*/
int * const constant_pointer_to_int = NULL;

It will be difficult to understand if you are not familiar with it. The modifier is different depending on whether it is on the left or right side of *. If there is const on the left side of *, it qualifies the type pointed to by the pointer, but if const is on the right side of *, it qualifies the pointer type itself.

When const is attached to the right side of *, read it as ~ (pointer type on the left side) ~ is const.

Translation in the brain


/* (constant int)Pointer type to type*/
(const int)* pointer_to_constant_int1;
(int const)* pointer_to_constant_int2;

/*Constant pointer type to int type*/
((int *) is const) constant_pointer_to_int = NULL;

Is it a little easier to understand? In this case, pointer_to_constant_int1 (2) is a pointer type to const int type, so even if * pointer_to_constant_int1 = 0, a compile error will occur. Also, pointer_to_constant_int1 = NULL does not result in an error. On the other hand, constant_pointer_to_int is a const pointer type to int type, so constant_pointer_to_int = & some_int; will result in a compilation error.

The const pointer type is a super-basic technique because it is useful as a function argument. Since you can make read-only references, you don't have to worry about changing the value passed by the function user, and you can't pass a const type address unless it's a const pointer type. However, even if the input-only reference is not a const pointer type, the program will run, so the const cult family is still frustrated with the pointer type without const of others today.

String literal

It's not a syntax, but it's an introduction. A string literal such as "hello" returns the address to its first character. So, you can receive it as follows.

const char* str = "hello";

Since the compiler prepares the memory itself containing h, e, l, l, o of "hello" somewhere, it receives the start address with a pointer variable. However, since some memory containing the "hello" is a rewrite-protected area, it should be received as a const char * type (pointer type to (constant char)).

Other literals return that value, but only the string address. In particular, those who have first touched a language that can handle string types as immutable objects may be confused.

in conclusion

Thank you for reading to the end.

There are few programs that can handle pointers perfectly (there are few programmers who can handle pointers perfectly). The C language becomes less simple to write as the proficiency level increases, such as setting the T * argument to const T * in order to make it read-only. You can make a difference. The place works, but this difference later becomes a negative legacy as it causes bugs and is confused by another programmer when using it.

Rust is now a read-only reference unless you specify mutable. This policy is good. I want Rust to be popular.

Postscript

I wrote my own way of interpreting pointer types, but this may not have been good. Now that I have found the JIS C99 standard, I would like to try to interpret it according to the specifications from the front in the near future. In that case, paste the link here. → Posted. Understanding the difference between const int * p and int * const p from syntax rules

Recommended Posts