Introduction to C language pointers learning _c language

Source: Internet
Author: User

It seems to be a very dignified topic, but it's really interesting.

1. A pointer is a type of thing, any one, as long as the whole can have its own unique pointer type, so the type of pointer is in fact approximate infinite

2. The function name is always rendered as a function pointer in an expression, except for the address operator and the sizeof

3. The most obscure thing about C is its complex declaration: void (*signal (int sig, Void (*FUNC) (int))) (int), try to rewrite it in a form that is easy to understand.

4. For pointers, use const to protect it to the maximum extent possible, whether it is passed to the function or used by itself

Take a look at a special pointer, let's call it a pointer, because it depends on the environment: NULL, is a magical thing. With a definition, there are two types of NULL in the compiler (each has a uniquely determined null):

#define NULL 0
#define NULL ((void*) 0)

What difference does it have? It doesn't look any different. It's 0, but one is a constant, and the other is a pointer to the address 0.

When they are the values of the pointers, there is no error or warning, that is, the compiler or the C standard considers this to be legal:

int* temp_int_1 = 0; No warning
int* temp_int_2 = (void*) 0;//No warning
int* temp_int_3 = 10;//Warnings present

Why? Why 0 can assign a value to a pointer, but 10 not? They are all constants.

Because the C language stipulates that when the compiler that handles the context finds that the constant 0 appears in the pointer assignment statement, it is used as a pointer, but it seems to be a nonsense.

Back to the beginning, what is the difference between the two cases of NULL? In the case of a string, I actually thought of the character array as a C-style string.

In C, a character array is used to store a series of meaningful characters, which by default add ' yes ' to the end of these characters, so there's another 0 value.

For some people, when using a character array, it is always wrong to distinguish between null and ' yes ', and using null at the end of a character array is absolutely incorrect! Although they are essentially constant 0, they have different meanings because of their different positions.

The appetizer is over.

For a function, we do parameter passing, and there are two forms of arguments: the formal participation argument

int function (int value)
{
    /*...*/
}
//...
function (11);

Where the value is the formal parameter, 11 is the argument, we know that in the scene, C language has two ways to pass: by value delivery and by address, but have you seriously studied? Here gives a substance, in fact, the C language only by value transmission, the so-called by-address transmission is only a false impression by value. As for the reason, a little thought will be able to understand.

For formal parameters and arguments, two are closely related, so it is always true that a copy of arguments is passed to the formal parameter, so that the parameter can safely use the value of the argument, but it also brings us some trouble, the most classic exchange of two

void Swap_v1 (int* val_1, int* val_2)
{
  int temp = *val_1;
  *val_1 = *val_2;
  *val_2 = *val_1;
}

This is called by-address, and actually just a copy of the value of the external pointer (argument), passed to the formal parameter val_1 and val_2, we actually use:

#define SWAP_V2 (A, b) (A = b, b = a-b, A-= b)
#define SWAP_V3 (x, y) {x ^= y; y ^= x; x ^= y}

It's magical to try, and it eliminates the time and space overhead of the function call. The principle of these two kinds of writing is essentially the same.

But, thinking about it, is there really no flaw in the wording? What happens if you enter two parameters that point to the same memory?

...
int test_1 = ten, test_2 = n;
SWAP_V2 (test_1, test_2);          
printf ("Now the test_1 are%d, test_2 is%d\n", test_1, test_2);
.../* Restore the original value * *
swap_v2 (test_1, test_1);
printf ("Now's test_1 is%d\n", test_1);  

What will it output? :

$: Now the test_1 are test_2 $: Now the
test_1 is 0

Yes, the output is 0, why? A little bit of brain can connect, then for the back of the swap_v3 is also so, at the discretion of the solution should be as short as possible:

static inline void swap_final (int* val_1, int* val_2)
{
  if (Val_1 = = val_2) return
    ;
  *val_1 ^= *val_2;
  *val_2 ^= *val_1;
  *val_1 ^= *val_2;
}
#define SWAP (x, y) \
do{         \
  if (&x = = &y)  \ break
    ;   \
  x ^= y;   \
  y ^= x;   \
  x ^= y;   \
}while (0)

This is the best exchange function we can find at the moment, and we can think about it a little further on this basis, how to make this exchange function more general? is the scope of application more large? Floating point type is not considered. Tip: Available void*

Similar to the above, occasional inadvertently can cause serious consequences:

int Combine_1 (int* dest, int* Add)
{
  *dest = *add;
  *dest + = *add;
  return *dest;
}
int combine_2 (int* dest, int* Add)
{
  *dest = 2* (*add);//The parentheses are a wise choice to return *dest when the priority is not set
  ;

Are the functions of the above two functions the same? Yes, it looks the same.

int test_3 = ten, Test_4 = n;

Combine_1 (&test_3, &test_4);
printf ("after combine_1, test_3 =%d\n", test_3);
.../* Restore the original value * *
combine_2 (&test_3, &test_4);
printf ("after combine_2, test_3 =%d\n", test_3);

Output

$: After combine_1, test_3 = 210

$: After combine_2, test_3 = 210

What if you pass in two identical objects?

.../* Restore Test_3 Original value * *
combine_1 (&test_3, &test_3);
printf ("After the second times combine_1, test_3 =%d\n", test_3);
...
Combine_2 (&test_3, &test_3);
printf ("After the second times combine_2, test_3 =%d\n", test_3);

Output

$: After second times combine_1, Test_3 =

$: Over second times combine_2, test_3 = 20

It's always amazing to know the truth, and the pointers are so hateful and loving.

C99 Standard after a new keyword, restrict, is used to modify the pointer, it does not have much explicit role, or even add and not, in your opinion, the effect is no different. But in the code of the standard library, the keyword is used in many places, which is why

    • First, this keyword is addressed to the compiler.
    • Second, the role of this keyword is to assist the compiler to better optimize the program
    • Finally, if you are unfamiliar, never use this keyword indiscriminately.

The things about the array

are arrays the same as pointers?

Not the same

Always remember that arrays are different things from pointers. But why is the following code correct?

int arr[10] = {9, 8, 7};
int* Parr = arr;

We're still saying that, combined with the context, the compiler launches arr on the right side of the assignment operator, silently converts him to the corresponding type of pointer, and we always use arr as a pointer to the top of the array memory block.

int function2 (const int TEST_ARR[10]
//int function2 (const int test_arr[]) consider whether these three types of notation are the same
int function2 (const int* Test_arr)
{return
  sizeof (Test_arr);
}
...
int size_out = sizeof (arr);
int size_in = function2 (arr);

printf ("Size_out =%d, size_in =%d\n", size_out, size_in);

Output:

Size_out = Size_in = 8

That's why arrays are different from pointers, in a code block that defines an array externally, the compiler finds here arr is an array through the context, and ARR represents a pointer to an array of 10 int types, only the first code is correct, just because it is more usage, is part of the standard. Just like there is no road in the world, walk more and become the road. "Right" how to write

int (*p) [+] = &arr;

At this point P's type is a pointer to an array of 10 elements, at which point (*p) [0] produces an effect of arr[0], that is parr[0], but (*p)? Here is not recorded, the result will overflow, why?

This is the difference and connection between arrays and pointers, but since we can use pointers like Parr, why write an int (*p) [10] as an ugly pattern? The reasons are as follows:

To go back to the way it was first said, passing by value in passing arr is simply passing its value, and losing context is just a normal pointer, except that our programmer knows it points to the starting position of a meaningful memory, and I want to pass the information of the array together. In addition to adding an extra parameter to record the length of an array, you can also use this method to pass a pointer to an array so that we can just pass one argument and keep all the information. But there are limits to doing so: for different sizes, or arrays of different storage types, they have different types.

 int arr_2[5];
 int (*p_2) [5] = &arr_2;
 float arr_3[5];
 Float (*p_3) [5] = &arr_3;

As shown above, pointers to arrays must explicitly specify the size of the array, the array storage type, which makes the pointer to the array more restrictive.

This usage is more used in multidimensional arrays, but generally not much used, as far as I am concerned, more inclined to use one-dimensional arrays to represent multidimensional arrays, in fact as mentioned above, C language is a very concise language, it does not have much nonsense, in essence, C language does not have multidimensional arrays, Because memory is a linear existence, even multidimensional arrays are implemented in the form of one-dimensional arrays.

Explain the multidimensional array here. A multidimensional array is the combination of several descending one-dimensional arrays, and the descending one-dimensional array is grouped together by several descending one-dimensional arrays until the lowest one-dimensional array, for example:

int dou_arr[5][3];

In the case of this two-dimensional array, how do you want to point to this array by combining 5 of each array of 3 int types?

  int (*p) [3]    = &dou_arr[0];
  int (*dou_p) [5][3] = &dou_arr;
  int (*what_p) [3]  = Dou_arr;

In fact, multidimensional arrays simply combine multiple descending one-dimensional arrays to make the index more intuitive. When you really understand the use of memory, you will feel that the multidimensional array gives itself more restrictions on the interpretation of the third sentence, when the array name appears to the right of the assignment number, it is a pointer, the type is the type of the array element, and for a multidimensional array, the element type is its descending one-dimensional array. The pointer type that points to the descending one-dimensional array. This explanation is a bit around, write yourself a lot better.

For some form of operation, we always think of the similar behavior together in a natural way. Consider the following code:

int* Arr_3[5] = {1, 2, 3, 4, 5};
int* p_4   = arr_3;

printf ("%d = =%d = =%d \ n", arr_3[2], * (P_4 + 2), * (Arr_3 + 2));

Output: 3 = 3 = 3? In fact, for arrays and pointers, [] operations can have the same result in most cases, and for pointers * (P_4 + 2) is equivalent to p_4[2], that is to say [] is the grammatical sugar of pointer arithmetic, and interestingly 2[p_4] is also equivalent to p_4[2], "iamastring" [2 = = ' m ', but this is just entertainment, in practice please do not do so, unless it is a code Chaos contest or some special purpose. Here, it should be stated that the execution efficiency of these kinds of writing is exactly the same, there is no pointer operation is faster than [] operation, these statements are the last century, with the development of the times, we should pay more attention to the code neat way

There is also a singular and practical technique for manipulating pointer operations in char arrays, extracting different types of data, or using char* pointers to extract content from different types of arrays, which is the purpose of displaying pointer operations. However, when using different types of pointers to manipulate memory blocks, you need to be aware that you do not manipulate meaningless areas or cross-border operations.

In fact, one of the simplest security studies is to exploit an overflow.

Advance: For an array of functions in the direction of growth, always toward the return address, the middle may be separated by many other automatic variables, we only need to continue the overflow test, until one time, the function does not return to normal! That proves we found the return address storage area of the function, at which point we can do something, such as overwriting the return address we want, which is one of the so-called overflow attacks.

The memory of the use of those things

You always thought you were working with real physical memory, in fact, you're only operating the qualifying virtual address that the operating system assigns to you, but that doesn't mean we can use the memory indefinitely, that's what the memory is selling, and actually storing the data or physical memory, except in the case of an operating system Different program windows (can be the same program) can share the same memory area, once the use of a silly large program to make the physical memory is not enough, we will have some useless data written to your hard drive, and then use, read back from the hard disk. What does this feature cause? Let's say you've used multiple windows on Windows to open two of the same programs:

...
int stay_here;
Char tran_to_int[100];
printf ("Address:%p\n", &stay_here);

Fgets (Tran_to_int, sizeof (Tran_to_int), stdin);
SSCANF (Tran_to_int, "%d", &stay_here);

for (;;)
{
  printf ("%d\n", stay_here);
  GetChar ();
  ++stay_here;
}
...

For this program (refer to the front axle and the example), every time a carriage return is struck, the value plus 1. When you open two of these programs at the same time, you will find that two of the program's stay_here are in the same address, but for it to operate separately, the resulting results are independent! This verifies the rationality of virtual address in one respect. The meaning of a virtual address is that even if a program has an error, the memory is ruined and it does not affect other processes. For the two read statements in the middle of the program, it is a good example to understand the essence of the C language input stream, it is suggested that the query use, here a little explanation:

In layman's terms, fgets the input stream from the call, stdin input into the place where the starting address is tran_to_int, and read up to sizeof (Tran_to_int), and in the rear sscanf function, the data just read in accordance with the% The format of D is stored in Stay_here, which is the meaning of the stream concept that C language has been emphasizing, and the combination of these two statements seems to be as simple as reading a data, but we need to know a problem, a question about scanf.

 scanf ("%d", &stay_here);

This statement will read the keyboard input until all the data before the carriage return, what does it mean? The carriage return will be left in the input stream and read or discarded by the next input. This has the potential to affect our program and produce unexpected results. And the use of a combination of two sentences will not.

Functions and function pointers, those things

In fact, the function name appears on the right side of the assignment symbol to represent the address of the function.

int function (int argc) {/*...*/
}
...
Int (*p_fun) (int) = function;
Int (*P_FUC) (int) = &function;//is consistent with the previous sentence

The above code declares and initializes the function pointer, which is a pointer to a function that returns a value of type int and a parameter of type P_fun

P_fun (one);
(*p_fun) (one);
function (11);

The above three code also has the same meaning, we can also use the concept of function pointer array

Int (*p_func_arr[]) (int) = {func1, Func2,};

Where Func1,func2 is a function that returns an int with an int parameter, we can then use this function like an array index.

Tips: We always ignore function declarations, which is not a good thing.

In C, because the compiler does not delve into or even indulge in a function declaration, this does not include an inline function (inline) because it is only available in this file.
For example, when we call a function somewhere, we don't declare it:

 Callwithoutdeclare (100); parameter 100 is int type

So, the C compiler would speculate that this function, which uses the int parameter, must have an int parameter list, and that if the parameter list in the function definition does not conform to it, it will cause the parameter information to pass the error (the compiler always believes that they are right!) , we know that C is a strongly typed language, and once the type is incorrect, it can cause many unexpected results (often bugs) to occur.

The same is true for calls to function pointers
The things that malloc in C language

We often see this type of wording:

int* pointer = (int*) malloc (sizeof (int));

What's so strange about that? Look at the following example:

int* pointer_2 = malloc (sizeof (int));

Which is the correct wording? All two are correct, this is why, this is also the pursuit of the ancient time of the C language, at that time, void* this type has not yet appeared, malloc return is the type of char*, then the programmer in the call to this function always add force type conversion, in order to correctly use this function, But after the advent of standard C, this problem no longer has, because any type of pointer can and void* to each other, and the C standard does not support the use of forced type conversion in unnecessary places, so the more orthodox in C language is the second type.

Digression: pointer conversions in C + + need to use coercion type conversion rather than the second example, but there is a better memory allocation method in C + +, so this problem is no longer a problem.

Tips:

C language of three functions malloc, calloc, realloc are very risky functions, in the use of the time must remember to check their results, the best way or to their packaging, you can choose macro Packaging, you can choose the function of packaging.
The ReAlloc function is one of the most criticized functions, because its function is too broad, can allocate space, also can release space, although it seems to be a good function, but it may inadvertently help us do some unexpected things, such as multiple free space. The right thing to do is to use the repackaging to castrate its functionality so that he can only expand or shrink the heap memory block size.
Pointer and structure body

typedef struct tag{
    int value;
    Long vari_store[1];
} Vari_struct;

At first glance, it seems to be a very structured structure.

...
Vari_struct vari_1;
vari_struct* vari_p_1 = &vari_1;
vari_struct* vari_p_2 = malloc (sizeof (vari_struct)) (

It seems to be the case, but there are always some people who come up with some strange usage.

int     what_spa_want = ten;
vari_struct* vari_p_3 = malloc (sizeof (vari_struct) + sizeof (long) *what_spa_want);

What do you mean by doing this? This is called a variable-length structure, and even if we go beyond the scope of the structure, it's not a cross in the allocation space. What_spa_want explains how much space you need, that is, how much space is needed in addition to the size of a struct, the space is used to store a long type, and because the allocated memory is contiguous, direct indexing can be used directly using the array Vari_store. And because of the C language, the compiler does not do the bounds of the array, so for a number of arrays arr, expression &arr[n] is the standard allowed behavior, but to remember Arr[n] is illegal. This usage is not entertainment, but a part of the standard (C99), applied to the actual

Understanding of Memory

In the process of allocating memory, we use malloc to distribute, release with free, but is this the distribution and release in our understanding? When malloc is invoked, the function either uses BRK () or uses nmap () to apply a piece of memory to the operating system, which is allocated to the desired location when used, and corresponds to free, as in the case of our hard drive, in effect:

int* value = malloc (sizeof (int) *5);
...
Free (value);
printf ("%d\n", value[0]);

In the code, why do I continue to use this memory after free? Because free is simply a token that frees the memory tag to indicate the function that allocates the memory, I can use it, but it does not break the contents of the current memory until there is an action to write to it. This has given a few questions:

Bugs are more difficult to discover, let's assume that if we have two pointers p1,p2 point to the same memory, if we use Free (p1) on one of these pointers; Operation, but forget that there is another pointer to it, which can lead to a very serious security risks, and this vulnerability is very difficult to find, because the bug is not revealed at the time, but it is possible at some point in the future, inadvertently let your program crash.
It is possible to simplify some issues, such as releasing a linked list field.
In general, or that sentence C language is a double-edged sword.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.