c语言指针官方解释

Pointers are arguably the most difficult feature of C to understand. But, they are one of the features which make C an excellent language.

指针可以说是C语言最难理解的功能。 但是,它们是使C成为优秀语言的功能之一。

In this article, we will go from the very basics of pointers to their usage with arrays, functions, and structure.

在本文中,我们将从指针的最基本知识开始,将其与数组,函数和结构一起使用。

So relax, grab a coffee, and get ready to learn all about pointers.

因此,放松一下,喝杯咖啡,并准备学习有关指针的所有知识。

主题 (Topics)

A.基本原理 (A. Fundamentals)
  1. What exactly are pointers?

    指针到底是什么?

  2. Definition and Notation

    定义和符号

  3. Some Special Pointers

    一些特殊的指针

  4. Pointer Arithmetic

    指针算术

B.数组和字符串 (B. Arrays and Strings)
  1. Why pointers and arrays?

    为什么要使用指针和数组?

  2. 1-D Arrays

    一维阵列

  3. 2-D Arrays

    二维阵列

  4. Strings

    弦乐

  5. Array of Pointers

    指针数组

  6. Pointer to Array

    指向数组的指针

C.功能 (C. Functions)
  1. Call by Value v/s Call by Reference

    按价值致电与按参考致电

  2. Pointers as Function Arguments

    指针作为函数参数

  3. Pointers as Function Return

    指针作为函数返回

  4. Pointer to Function

    功能指针

  5. Array Of Pointers to Functions

    指向函数的指针数组

  6. Pointer to Function as an Argument

    指向参数的指针

D.结构 (D. Structure)
  1. Pointer to Structure

    结构指针

  2. Array of Structure

    结构数组

  3. Pointer to Structure as an Argument

    指向结构的参数

E.指针到指针 (E. Pointer to Pointer)
F.结论 (F. Conclusion)

A.定义,符号,类型和算法 (A. Definition, Notation, Types and Arithmetic)

1.指针到底是什么? (1. What exactly are pointers?)

Before we get to the definition of pointers, let us understand what happens when we write the following code:

在介绍指针的定义之前,让我们了解编写以下代码时会发生的情况:

int digit = 42;

A block of memory is reserved by the compiler to hold an int value. The name of this block is digit and the value stored in this block is 42.

编译器将保留一块内存来保存一个int值。 该块的名称是digit并且存储在该块中的值为42

Now, to remember the block, it is assigned with an address or a location number (say, 24650).

现在,要记住该块,将为其分配一个地址或位置号(例如24650)。

The value of the location number is not important for us, as it is a random value. But, we can access this address using the & (ampersand) or address of operator.

位置编号的值对我们而言并不重要,因为它是随机值。 但是,我们可以访问使用此地址& (符号) 运算符或地址

printf("The address of digit = %d.",&digit);/* prints "The address of digit = 24650. */

We can get the value of the variable digit from its address using another operator * (asterisk), called the indirection or dereferencing or value at address operator.

我们可以使用另一个运算符* (星号)从其地址获取变量digit的值,该运算符称为地址运算符的间接 寻址取消引用

printf("The value of digit = %d.", *(&digit);/* prints "The value of digit = 42. */

2.定义和符号 (2. Definition and Notation)

The address of a variable can be stored in another variable known as a pointer variable. The syntax for storing a variable's address to a pointer is:

变量的地址可以存储在另一个称为指针变量的变量中。 用于将变量的地址存储到指针的语法为:

dataType *pointerVariableName = &variableName;

For our digit variable, this can be written like this:

对于我们的digit变量,可以这样写:

int *addressOfDigit = &digit;

or like this:

或像这样:

int *addressOfDigit;
addressOfDigit= &digit;

This can be read as - A pointer to int (integer) addressOfDigit stores the address of(&) digit variable.

可以将其读取为- 指向int (整数) addressOfDigit指针, addressOfDigit存储address of(&) digit变量的address of(&)

几点要了解: (Few points to understand:)

dataType – We need to tell the computer what the data type of the variable is whose address we are going to store. Here, int was the data type of digit.

dataType –我们需要告诉计算机变量的数据类型是我们要存储的地址。 在这里, intdigit的数据类型。

It does not mean that addressOfDigit will store a value of type int. An integer pointer (like addressOfDigit) can only store the address of variables of integer type.

并不意味着addressOfDigit将存储一个int类型的值。 整数指针(例如addressOfDigit ) 只能存储整数类型的变量的地址。

int variable1;
int variable2;
char variable3;
int *addressOfVariables;

* – A pointer variable is a special variable in the sense that it is used to store an address of another variable. To differentiate it from other variables that do not store an address, we use * as a symbol in the declaration.

* –指针变量是一种特殊变量,从某种意义上说,它用于存储另一个变量的地址。 为了将其与不存储地址的其他变量区分开,我们在声明中使用*作为符号。

Here, we can assign the address of variable1 and variable2 to the integer pointer addressOfVariables but not to variable3 since it is of type char. We will need a character pointer variable to store its address.

在这里,我们可以将variable1variable2的地址分配给整数指针addressOfVariables而不是variable3因为它的类型为char 。 我们将需要一个字符指针变量来存储其地址。

We can use our addressOfDigit pointer variable to print the address and the value of digit as below:

我们可以使用我们的addressOfDigit指针变量来打印地址和digit的值,如下所示:

printf("The address of digit = %d.", addressOfDigit);/* prints "The address of digit = 24650." */
printf("The value of digit = %d.", *addressOfDigit);/*prints "The value of digit = 42. */

Here, *addressOfDigit can  be read as the value at the address stored in addressOfDigit.

在这里,可以将*addressOfDigit读取为addressOfDigit存储的地址上的值。

Notice we used %d as the format identifier for addressOfDigit. Well, this is not completely correct. The correct identifier would be %p.

注意,我们使用%d作为addressOfDigit格式标识符 。 好吧,这并不完全正确。 正确的标识符为%p

Using %p, the address is displayed as a hexadecimal value. But the memory address can be displayed in integers as well as octal values. Still, since it is not an entirely correct way, a warning is shown.

使用%p ,地址显示为十六进制值。 但是内存地址可以整数和八进制值显示。 但是,由于这不是完全正确的方法,因此会显示警告。

int num = 5;
int *p = #
printf("Address using %%p = %p",p);
printf("Address using %%d = %d",p);
printf("Address using %%o = %o",p);

The output according to the compiler I'm using is the following:

根据我使用的编译器的输出如下:

Address using %p = 000000000061FE00
Address using %d = 6422016
Address using %o = 30377000

This is the warning shown when you use  %d - " warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *' ".

这是在使用%d时显示的警告-“警告:格式'%d'期望类型为'int'的参数,但是参数2的类型为'int *'”。

3.一些特殊的指针 (3. Some Special Pointers)

野指针 (The Wild Pointer)

char *alphabetAddress; /* uninitialised or wild pointer */
char alphabet = "a";
alphabetAddress = &alphabet; /* now, not a wild pointer */

When we defined our character pointer alphabetAddress, we did not initialize it.

当我们定义字符指针alphabetAddress ,我们没有对其进行初始化。

Such pointers are known as wild pointers. They store a garbage value (that is, memory address) of a byte that we don't know is reserved or not (remember int digit = 42;, we reserved a memory address when we declared it).

这样的指针称为野生指针 。 它们存储一个我们不知道是否保留的字节的垃圾值(即内存地址)(记住int digit = 42;我们在声明时保留了一个内存地址)。

Suppose we dereference a wild pointer and assign a value to the memory address it is pointing at. This will lead to unexpected behaviour since we will write data at a  memory block that may be free or reserved.

假设我们取消引用野生指针,并为其指向的内存地址分配一个值。 这将导致意外行为,因为我们会将数据写入可能是空闲或保留的内存块中。

空指针 (Null Pointer)

To make sure that we do not have a wild pointer, we can initialize a pointer with a NULL value, making it a null pointer.

为确保没有野生指针,我们可以使用NULL值初始化指针,使其成为null指针

char *alphabetAddress = NULL /* Null pointer */

A null pointer points at nothing, or at a memory address that users can not access.

空指针指向任何内容或用户无法访问的内存地址。

空指针 (Void Pointer)

A void pointer can be used to point at a variable of any data type. It can be reused to point at any data type we want to. It is declared like this:

空指针可用于指向任何数据类型的变量。 可以重用它来指向我们想要的任何数据类型。 声明如下:

void *pointerVariableName = NULL;

Since they are very general in nature, they are also known as generic pointers.

由于它们本质上非常笼统 ,因此也称为泛型指针

With their flexibility, void pointers also bring some constraints. Void pointers cannot be dereferenced as any other pointer. Appropriate typecasting is necessary.

凭借其灵活性,空指针还带来了一些约束。 无效指针不能像其他任何指针一样被取消引用。 适当的类型转换是必要的。

void *pointer = NULL;
int number = 54;
char alphabet = "z";
pointer = &number;
printf("The value of number = ", *pointer); /* Compilation Error */
/* Correct Method */
printf("The value of number = ", *(int *)pointer); /* prints "The value at number = 54" */
pointer = &alphabet;
printf("The value of alphabet = ", *pointer); /* Compilation Error */
printf("The value of alphabet = ", *(char *)pointer); /* prints "The value at alphabet = z */

Similarly, void pointers need to be typecasted for performing arithmetic operations.

类似地,需要对void指针进行类型转换以执行算术运算。

Void pointers are of great use in C. Library functions malloc() and calloc() which dynamically allocate memory return void pointers. qsort(), an inbuilt sorting function in C, has a function as its argument which itself takes void pointers as its argument.

无效指针在C中很有用。库函数malloc()calloc()动态分配内存,返回空指针。 qsort()是C中的一个内置排序函数,具有一个函数作为其参数,而该函数本身将void指针作为其参数。

悬空指针 (Dangling Pointer)

A dangling pointer points to a memory address which used to hold a variable. Since the address it points at is no longer reserved, using it will lead to unexpected results.

悬空指针指向用于保存变量的内存地址。 由于它所指向的地址不再保留,因此使用它会导致意外的结果。

main(){int *ptr;ptr = (int *)malloc(sizeof(int));*ptr = 1;printf("%d",*ptr); /* prints 1 */free(ptr); /* deallocation */*ptr = 5;printf("%d",*ptr); /* may or may not print 5 */
}

Though the memory has been deallocated by free(ptr), the pointer to integer ptr still points to that unreserved memory address.

尽管已使用free(ptr)释放了内存,但指向整数ptr的指针仍指向该未保留的内存地址。

4.指针算术 (4. Pointer Arithmetic)

We know by now that pointers are not like any other variable. They do not store any value but the address of memory blocks.

到目前为止,我们知道指针与其他任何变量都不一样。 它们不存储任何值,但存储块的地址。

So it should be quite clear that not all arithmetic operations would be valid with them. Would multiplying or dividing two pointers (having addresses) make sense?

因此,应该很清楚,并非所有算术运算都对它们有效。 将两个指针相乘或相除( 具有地址 )是否有意义?

指针很少但非常有用的有效操作: (Pointers have few but immensely useful valid operations:)

  1. You can assign the value of one pointer to another only if they are of the same type (unless they're typecasted or one of them is void *).

    您只能将一个指针的值分配给另一个指针(如果它们是相同类型的(除非已被强制转换,或者其中一个为void * ))。

int ManU = 1;
int *addressOfManU = &ManU;
int *anotherAddressOfManU = NULL;
anotherAddressOfManU = addressOfManU; /* Valid */
double *wrongAddressOfManU = addressOfManU; /* Invalid */

2.   You can only add or subtract integers to pointers.

2.您只能添加或减去指向指针的整数

int myArray = {3,6,9,12,15};
int *pointerToMyArray = &myArray[0];
pointerToMyArray += 3; /* Valid */
pointerToMyArray *= 3; /* Invalid */

When you add (or subtract) an integer (say n) to a pointer, you are not actually adding (or subtracting) n bytes to the pointer value. You are actually adding (or subtracting) n-times the size of the data type of the variable being pointed bytes.

当您向指针添加(或减去)整数(例如n)时,实际上并没有向指针值添加(或减去)n个字节。 您实际上是在将(或减去)n 倍于指向字节的变量的数据类型的大小

int number = 5;/* Suppose the address of number is 100 */
int *ptr = &number;
int newAddress = ptr + 3;/* Same as ptr + 3 * sizeof(int) */

The value stored in newAddress will not be 103, rather 112.

存储在newAddress的值将不是 103,而是112

3.  Subtraction and comparison of pointers is valid only if both are members of the same array. The subtraction of pointers gives the number of elements separating them.

3. 指针的减法和比较仅在两者都是同一数组的成员时才有效。 指针的减法给出分隔它们的元素的数量。

int myArray = {3,6,9,12,15};
int sixthMultiple = 18;
int *pointer1 = &myArray[0];
int *pointer2 = &myArray[1];
int *pointer6 = &sixthMuliple;/* Valid Expressions */
if(pointer1 == pointer2)
pointer2 - pointer1;/* Invalid Expressions
if(pointer1 == pointer6)
pointer2 - pointer6

4.  You can assign or compare a pointer with NULL.

4.您可以分配或将指针与NULL进行比较。

The only exception to the above rules is that the address of the first memory block after the last element of an array follows pointer arithmetic.

上述规则的唯一例外是,数组最后一个元素之后的第一个内存块的地址遵循指针算法。

Pointer and arrays exist together. These valid manipulations of pointers are immensely useful with arrays, which will be discussed in the next section.

指针和数组一起存在。 这些有效的指针操作对数组非常有用,这将在下一节中讨论。

B.数组和字符串 (B. Arrays and Strings)

1.为什么要使用指针和数组? (1. Why pointers and arrays?)

In C, pointers and arrays have quite a strong relationship.

在C语言中,指针和数组有很强的关系。

The reason they should be discussed together is because what you can achieve with array notation (arrayName[index]) can also be achieved with pointers, but generally faster.

应该一起讨论它们的原因是因为您可以使用数组表示法( arrayName[index] )实现的目标也可以通过指针实现,但通常更快。

2.一维阵列 (2. 1-D Arrays)

Let us look at what happens when we write int myArray[5];.

让我们看一下在编写int myArray[5];时会发生什么int myArray[5];

Five consecutive blocks of memory starting from myArray[0] to myArray[4] are created with garbage values in them. Each of the blocks is of size 4 bytes.

myArray[0]myArray[4]创建了五个连续的内存块,其中有垃圾值。 每个块的大小为4个字节。

Thus, if the address of myArray[0] is 100 (say), the address of the rest of the blocks would be 104, 108, 112, and 116.

因此,如果myArray的[0]的地址为100 (比方说),块的其余部分的地址将是104108112 ,和116

Have a look at the following code:

看下面的代码:

int prime[5] = {2,3,5,7,11};
printf("Result using &prime = %d\n",&prime);
printf("Result using prime = %d\n",prime);
printf("Result using &prime[0] = %d\n",&prime[0]);/* Output */
Result using &prime = 6422016
Result using prime = 6422016
Result using &prime[0] = 6422016

So, &prime, prime, and &prime[0] all give the same address, right? Well, wait and read because you are in for a surprise (and maybe some confusion).

因此, &primeprime&prime[0]都给出相同的地址,对吗? 好吧,请稍候,因为您可能会感到意外(可能有些困惑)。

Let's try to increment each of &prime, prime, and &prime[0] by 1.

让我们尝试将&primeprime&prime[0]的每个值增加1。

printf("Result using &prime = %d\n",&prime + 1);
printf("Result using prime = %d\n",prime + 1);
printf("Result using &prime[0] = %d\n",&prime[0] + 1);/* Output */
Result using &prime = 6422036
Result using prime = 6422020
Result using &prime[0] = 6422020

Wait! How come &prime + 1 results in something different than the other two? And why are prime + 1 and &prime[0] + 1 still equal? Let's answer these questions.

等待! &prime + 1与其他两个结果有何不同? 为什么prime + 1&prime[0] + 1仍然相等? 让我们回答这些问题。

prime and &prime[0] both point to the 0th element of the array prime. Thus, the name of an array is itself a pointer to the 0th element of the array.

prime&prime[0]都指向数组prime的第0个元素。 因此, 数组名称本身就是指向该数组第0个元素的指针

Here, both point to the first element of size 4 bytes. When you add 1 to them, they now point to the 1st element in the array. Therefore this results in an increase in the address by 4.

这里,两个都指向大小为4个字节的第一个元素。 当您向它们添加1时,它们现在指向数组中的第一个元素。 因此,这导致地址增加4。

&prime, on the other hand, is a pointer to an int array of size 5. It stores the base address of the array prime[5], which is equal to the address of the first element. However, an increase by 1 to it results in an address with an increase of 5 x 4 = 20 bytes.

另一方面, &prime指向大小为5的int数组的指针 。 它存储数组prime[5]的基地址,该地址等于第一个元素的地址。 但是,增加1会导致地址增加5 x 4 = 20字节。

In short, arrayName and &arrayName[0] point to the 0th element whereas &arrayName points to the whole array.

简而言之, arrayName&arrayName[0]指向第0个元素,而&arrayName指向整个数组。

We can access the array elements using subscripted variables like this:

我们可以使用下标变量访问数组元素,如下所示:

int prime[5] = {2,3,5,7,11};
for( int i = 0; i < 5; i++)
{printf("index = %d, address = %d, value = %d\n", i, &prime[i], prime[i]);
}

We can do the same using pointers which are always faster than using subscripts.

使用指针总是可以比使用下标更快。

int prime[5] = {2,3,5,7,11};
for( int i = 0; i < 5; i++)
{printf("index = %d, address = %d, value = %d\n", i, prime + i, *(prime + i));
}

Both methods give the output:

两种方法都给出输出:

index = 0, address = 6422016, value = 2
index = 1, address = 6422020, value = 3
index = 2, address = 6422024, value = 5
index = 3, address = 6422028, value = 7
index = 4, address = 6422032, value = 11

Thus, &arrayName[i] and arrayName[i] are the same as arrayName + i and  *(arrayName + i), respectively.

因此, &arrayName[i]arrayName[i]分别与arrayName + i*(arrayName + i)相同。

3.二维阵列 (3. 2-D Arrays)

Two-dimensional arrays are an array of arrays.

二维数组是数组的数组。

int marks[5][3] = { { 98, 76, 89},{ 81, 96, 79},{ 88, 86, 89},{ 97, 94, 99},{ 92, 81, 59}};

Here, marks can be thought of as an array of 5 elements, each of which is a one-dimensional array containing 3 integers. Let us work through a series of programs to understand different subscripted expressions.

在这里,可以将marks视为由5个元素组成的数组,每个元素都是一个包含3个整数的一维数组。 让我们通过一系列程序来理解不同的下标表达式。

printf("Address of whole 2-D array = %d\n", &marks);
printf("Addition of 1 results in %d\n", &marks +1);/* Output */
Address of whole 2-D array = 6421984
Addition of 1 results in 6422044

Like 1-D arrays, &marks points to the whole 2-D array, marks[5][3]. Thus, incrementing to it by 1 ( = 5 arrays X 3 integers each X 4 bytes = 60) results in an increment by 60 bytes.

像一维数组一样, &marks指向整个二维数组,即marks[5][3] 。 因此,将其递增1(= 5个数组X 3个整数,每个X 4个字节= 60)将导致递增60个字节。

printf("Address of 0th array = %d\n", marks);
printf("Addition of 1 results in %d\n", marks +1);
printf("Address of 0th array =%d\n", &marks[0]);
printf("Addition of 1 results in %d\n", &marks[0] + 1);/* Output */
Address of 0th array = 6421984
Addition of 1 results in 6421996
Address of 0th array = 6421984
Addition of 1 results in 6421996

If marks was a 1-D array, marks and &marks[0] would have pointed to the 0th element. For a 2-D array, elements are now 1-D arrays. Hence, marks and &marks[0] point to the 0th array (element), and the addition of 1 point to the 1st array.

如果marks是一维数组,则marks&marks[0]将指向0th元素。 对于二维数组,元素现在为一维数组 。 因此, marks&marks[0]指向0th数组(元素),加1则指向第1st数组。

printf("Address of 0th element of 0th array = %d\n", marks[0]);
printf("Addition of 1 results in %d\n", marks[0] + 1);
printf("Address of 0th element of 1st array = %d\n", marks[1]);
printf("Addition of 1 results in %d\n", marks[1] + 1);/* Output */
Address of 0th element of 0th array = 6421984
Addition of 1 results in 6421988
Address of 0th element of 1st array = 6421996
Addition of 1 results in 6422000

And now comes the difference. For a 1-D array, marks[0] would give the value of the 0th element. An increment by 1 would increase the value by 1.

现在来了区别。 对于一维数组, marks[0]将给出第0个元素的值。 递增1将使值增加1。

But, in a 2-D array, marks[0] points to the 0th element of the 0th array. Similarly, marks[1] points to the 0th element of the 1st array. An increment by 1 would point to the 1st element in the 1st array.

但是,在二维数组中, marks[0]指向0th 0th数组的0th元素。 同样, marks[1]指向0th 1st数组的0th元素。 递增1将指向1st 1st数组中的1st 1st元素。

printf("Value of 0th element of 0th array = %d\n", marks[0][0]);
printf("Addition of 1 results in %d", marks[0][0] + 1);/* Output */
Value of 0th element of 0th array = 98
Addition of 1 results in 99

This is the new part. marks[i][j] gives the value of the jth element of the ith array. An increment to it changes the value stored at marks[i][j]. Now, let us try to write marks[i][j] in terms of pointers.

这是新的部分。 marks[i][j]给出jth ith数组的jth元素的值。 对其进行增量更改将存储在marks[i][j]处的值。 现在,让我们尝试用指针写marks[i][j]

We know marks[i] + j would point to the ith element of the jth array from our previous discussion. Dereferencing it would mean the value at that address. Thus, marks[i][j] is the same as  *(marks[i] + j).

我们知道在先前的讨论中, marks[i] + j将指向ith jth数组的ith元素。 取消引用将意味着该地址的值。 因此, marks[i][j]*(marks[i] + j)

From our discussion on 1-D arrays, marks[i] is the same as *(marks + i). Thus, marks[i][j] can be written as *(*(marks + i) + j) in terms of pointers.

从我们对一维数组的讨论中, marks[i]*(marks + i) 。 因此,在指针方面, marks[i][j]可以写为*(*(marks + i) + j)

Here is a summary of notations comparing 1-D and 2-D arrays.

这是比较一维和二维数组的符号的摘要。

table{ width: 10%;}th{text-align:center;}

Expression 1-D Array 2-D Array
&arrayName points to the address of whole array
adding 1 increases the address by 1 x sizeof(arrayName)
points to the address of whole array
adding 1 increases the address by 1 x sizeof(arrayName)
arrayName points to the 0th element
adding 1 increases the address to 1st element
points to the 0th element (array)
adding 1 increases the address to 1st element (array)
&arrayName[i] points to the the ith element
adding 1 increases the address to (i+1)th element
points to the ith element (array)
adding 1 increases the address to the (i+1)th element (array)
arrayName[i] gives the value of the ith element
adding 1 increases the value of the ith element
points to the 0th element of the ith array
adding 1 increases the address to 1st element of the ith array
arrayName[i][j] Nothing gives the value of the jth element of the ith array
adding 1 increases the value of the jth element of the ith array
Pointer Expression To Access The Elements *( arrayName + i) *( *( arrayName + i) + j)

table{ width: 10%;}th{text-align:center;}

表达 一维阵列 二维阵列
&arrayName 指向整个数组的地址
加1将地址增加1 x sizeof(arrayName)
指向整个数组的地址
加1将地址增加1 x sizeof(arrayName)
arrayName 指向第0个元素
加1将地址增加到第1个元素
指向第0个元素(数组)
加1将地址增加到第1个元素(数组)
&arrayName [i] 指向第ith个元素
加1将地址增加到第(i + 1)个元素
指向第ith个元素(数组)
加1将地址增加到第(i + 1)个元素(数组)
arrayName [i] 给出第ith个元素的值
加1将增加ith元素的值
指向第ith个数组的第0个元素
加1将地址增加到第i个数组的第一个元素
arrayName [i] [j] 没有 给出第ith个数组的第j个元素的值
加1将增加ith数组的第j个元素的值
指针表达式以访问元素 *(arrayName + i) *(*(arrayName + i)+ j)

4.琴弦 (4. Strings)

A string is a one-dimensional array of characters terminated by a null(\0). When we write char name[] = "Srijan";, each character occupies one byte of memory with the last one always being \0.

字符串是由null(\0)终止的一维字符数组。 当我们写char name[] = "Srijan"; ,每个字符占用一个内存字节,最后一个始终为\0

Similar to the arrays we have seen, name and &name[0] points to the 0th character in the string, while &name points to the whole string. Also, name[i] can be written as *(name + i).

与我们看到的数组类似, name&name[0]指向字符串中的0th字符,而&name指向整个字符串。 同样, name[i]可以写为*(name + i)

/* String */
char champions[] = "Liverpool";printf("Pointer to whole string = %d\n", &champions);
printf("Addition of 1 results in %d\n", &champions + 1);/* Output */
Address of whole string = 6421974
Addition of 1 results in 6421984printf("Pointer to 0th character = %d\n", &champions[0]);
printf("Addition of 1 results in %d\n", &champions[0] + 1);/* Output */
Address of 0th character = 6421974
Addition of 1 results in a pointer to 1st character 6421975printf("Pointer to 0th character = %d\n", champions);
printf("Addition of 1 results in a pointer to 1st character %d\n", champions + 1);/* Output */
Address of 0th character = 6421974
Addition of 1 results in 6421975printf("Value of 4th character = %c\n", champions[4]);
printf("Value of 4th character using pointers = %c\n", *(champions + 4));/* Output */
Value of 4th character = r
Value of 4th character using pointers = r

A two-dimensional array of characters or an array of strings can also be accessed and manipulated as discussed before.

如前所述,也可以访问和操纵二维字符数组或字符串数​​组。

/* Array of Strings */
char top[6][15] = {"Liverpool","Man City","Man United","Chelsea","Leicester","Tottenham"};printf("Pointer to 2-D array = %d\n", &top);
printf("Addition of 1 results in %d\n", &top + 1);/* Output */
Pointer to 2-D array = 6421952
Addition of 1 results in 6422042printf("Pointer to 0th string = %d\n", &top[0]);
printf("Addition of 1 results in %d\n", &top[0] + 1);/* Output */
Pointer to 0th string = 6421952
Addition of 1 results in 6421967printf("Pointer to 0th string = %d\n", top);
printf("Addition of 1 results in %d\n", top + 1);/* Output */
Pointer to 0th string = 6421952
Addition of 1 results in 6421967printf("Pointer to 0th element of 4th string = %d\n", top[4]);
printf("Pointer to 1st element of 4th string = %c\n", top[4] + 1);/* Output */
Pointer to 0th element of 4th string = 6422012
Pointer to 1st element of 4th string = 6422013printf("Value of 1st character in 3rd string = %c\n", top[3][1]);
printf("Same using pointers = %c\n", *(*(top + 3) + 1));/* Output */
Value of 1st character in 3rd string = h
Same using pointers = h

5.指针数组 (5. Array of Pointers)

Like an array of ints and an array of chars, there is an array of pointers as well. Such an array would simply be a collection of addresses. Those addresses could point to individual variables or another array as well.

int的数组和char的数组一样,也有一个指针数组。 这样的数组只是地址的集合。 这些地址可以指向单个变量或另一个数组。

The syntax for declaring a pointer array is the following:

声明指针数组的语法如下:

dataType *variableName[size];/* Examples */
int *example1[5];
char *example2[8];

Following the operators precedence, the first example can be read as -  example1 is an array([]) of 5 pointers to int. Similarly, example2 is an array of 8 pointers to char.

遵循运算符的优先级 ,第一个示例可以读取为example1是一个由5个指向int指针组成的array( [] ) 。 同样, example2是8个指向char指针的数组

We can store the two-dimensional array to string top using a pointer array and save memory as well.

我们可以使用指针数组将二维数组存储到字符串top并节省内存。

char *top[] = {"Liverpool","Man City","Man United","Chelsea","Leicester","Tottenham"};

top will contain the base addresses of all the respective names. The base address of "Liverpool" will be stored in top[0], "Man City" in top[1], and so on.

top将包含所有各个名称的基地址。 "Liverpool"的基地址将存储在top[0]"Man City"存储地址将在top[1] ,依此类推。

In the earlier declaration, we required 90 bytes to store the names. Here, we only require ( 58 (sum of bytes of names) + 12 ( bytes required to store the address in the array) ) 70 bytes.

在之前的声明中,我们需要90个字节来存储名称。 在这里,我们只需要(58(名称的字节总和)+ 12(将地址存储在数组中所需的字节))70个字节。

The manipulation of strings or integers becomes a lot easier when using an array of pointers.

使用指针数组时,字符串或整数的操作变得容易得多。

If we try to put "Leicester" ahead of "Chelsea", we just need to switch the values of top[3] and top[4] like below:

如果我们尝试将"Leicester"放到"Leicester" "Chelsea" ,则只需切换top[3]top[4]的值,如下所示:

char *temporary;
temporary = top[3];
top[3] = top[4];
top[4] = temporary;

Without pointers, we would have to exchange every character of the strings, which would have taken more time. That's why strings are generally declared using pointers.

没有指针,我们将不得不交换字符串的每个字符,这将花费更多的时间。 这就是为什么通常使用指针声明字符串的原因。

6.指向数组的指针 (6. Pointer to Array)

Like "pointer to int" or "pointer to char", we have pointer to array as well. This pointer points to whole array rather than its elements.

像“指向int指针”或“指向char指针”一样,我们也有指向数组的指针。 该指针指向整个数组而不是其元素。

Remember we discussed how &arrayName points to the whole array? Well, it is a pointer to array.

还记得我们讨论过&arrayName如何指向整个数组吗? 好吧,它是指向数组的指针。

A pointer to array can be declared like this:

指向数组的指针可以这样声明:

dataType (*variableName)[size];/* Examples */
int (*ptr1)[5];
char (*ptr2)[15];

Notice the parentheses. Without them, these would be an array of pointers. The first example can be read as - ptr1 is a pointer to an array of 5 int(integers).

注意括号。 没有它们,它们将是一个指针数组。 第一个示例可以读为ptr1是指向5个int (integers)数组的指针

int goals[] = { 85,102,66,69,67};
int (*pointerToGoals)[5] = &goals;
printf("Address stored in pointerToGoals %d\n", pointerToGoals);
printf("Dereferncing it, we get %d\n",*pointerToGoals);/* Output */
Address stored in pointerToGoals 6422016
Dereferencing it, we get 6422016

When we dereference a pointer, it gives the value at that address. Similarly, by dereferencing a pointer to array, we get the array and the name of the array points to the base address. We can confirm that *pointerToGoals gives the array goals if we find its size.

当我们取消引用指针时,它给出该地址处的值。 类似地,通过取消引用数组的指针,我们得到了数组,数组的名称指向了基地址。 如果发现数组大小,我们可以确认*pointerToGoals给出了数组goals

printf("Size of goals[5] = %d, *pointerToGoals);/* Output */
Size of goals[5] = 20

If we dereference it again, we will get the value stored in that address. We can print all the elements using pointerToGoals.

如果再次取消引用它,我们将获得存储在该地址中的值。 我们可以使用pointerToGoals打印所有元素。

for(int i = 0; i < 5; i++)
printf("%d ", *(*pointerToGoals + i));/* Output */
85 102 66 69 67

Pointers and pointer to arrays are quite useful when paired up with functions. Coming up in the next section!

与函数配对时,数组的指针和指针非常有用。 即将在下一节中介绍!

C.功能 (C. Functions)

1.按值致电与按引用致电 (1. Call by Value vs Call by Reference)

Have a look at the program below:

看看下面的程序:

#include <stdio.h>int multiply(int x, int y){int z;z = x * y;return z;
}main(){
int x = 3, y = 5;
int product = multiply(x,y);
printf("Product = %d\n", product);/* prints "Product = 15" */
}

The function multiply() takes two int arguments and returns their product as int.

函数multiply()接受两个int参数,并将其乘积返回为int

In the function call multiply(x,y), we passed the value of x and y ( of main()), which are actual arguments, to multiply().

在函数调用multiply(x,y) ,我们将xy ( main() )作为实际参数传递给了multiply()

The values of the actual arguments are passed or copied to the formal arguments x and y ( of multiply()). The x and y of multiply() are different from those of main(). This can be verified by printing their addresses.

实际参数的值将传递或复制到形式参数 xy ( multiply() )。 multiply()xymain()xy不同。 可以通过打印其地址来验证。

#include <stdio.h>int multiply(int x, int y){printf("Address of x in multiply() = %d\n", &x);printf("Address of y in multiply() = %d\n", &y);int z;z = x * y;return z;
}main(){
int x = 3, y = 5;
printf("Address of x in main() = %d\n", &x);
printf("Address of y in main() = %d\n", &y);
int product = multiply(x,y);
printf("Product = %d\n", product);
}/* Output */
Address of x in main() = 6422040
Address of y in main() = 6422036
Address of x in multiply() = 6422000
Address of y in multiply() = 6422008
Product = 15

Since we created stored values in a new location, it costs us memory. Wouldn't it be better if we could perform the same task without wasting space?

由于我们在新的位置创建了存储的值,因此要消耗我们的内存。 如果我们可以在不浪费空间的情况下执行相同的任务,那会更好吗?

Call by reference helps us achieve this. We pass the address or reference of the variables to the function which does not create a copy. Using the dereferencing operator *, we can access the value stored at those addresses.

以引用方式致电有助于我们实现这一目标。 我们将变量的地址或引用传递给不创建副本的函数。 使用解引用运算符* ,我们可以访问存储在这些地址中的值。

We can rewrite the above program using call by reference as well.

我们也可以通过引用调用来重写上述程序。

#include <stdio.h>int multiply(int *x, int *y){int z;z = (*x) * (*y);return z;
}main(){
int x = 3, y = 5;
int product = multiply(&x,&y);
printf("Product = %d\n", product);/* prints "Product = 15" */
}

2.指针作为函数参数 (2. Pointers as Function Arguments)

In this section, we will look at various programs where we give int, char, arrays and strings as arguments using pointers.

在本节中,我们将研究各种程序,在这些程序中,我们使用指针将intchar ,数组和字符串作为参数。

#include <stdio.h>void add(float *a, float *b){float c = *a + *b;printf("Addition gives %.2f\n",c);
}void subtract(float *a, float *b){float c = *a - *b;printf("Subtraction gives %.2f\n",c);
}void multiply(float *a, float *b){float c = *a * *b;printf("Multiplication gives %.2f\n",c);
}void divide(float *a, float *b){float c = *a / *b;printf("Division gives %.2f\n",c);
}main(){printf("Enter two numbers :\n");float a,b;scanf("%f %f",&a,&b);printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n");char operation = '0';scanf(" %c",&operation);printf("\nOperating...\n\n");switch (operation) {case 'a':add(&a,&b);break;case 's':subtract(&a,&b);break;case 'm':multiply(&a,&b);break;case 'd':divide(&a,&b);break;default:printf("Invalid input!!!\n");}}

We created four functions, add(), subtract(), multiply() and divide() to perform arithmetic operations on the two numbers a and b.

我们创建了四个函数add()subtract()multiply()divide()对两个数字ab进行算术运算。

The address of a and b was passed to the functions. Inside the function using * we accessed the values and printed the result.

ab的地址已传递给函数。 在使用*的函数内部,我们访问了值并打印了结果。

Similarly, we can give arrays as arguments using a pointer to its first element.

同样,我们可以使用指向第一个元素的指针将数组作为参数。

#include <stdio.h>void greatestOfAll( int *p){int max = *p;for(int i=0; i < 5; i++){if(*(p+i) > max)max = *(p+i);}printf("The largest element is %d\n",max);}
main(){int myNumbers[5] = { 34, 65, -456, 0, 3455};greatestOfAll(myNumbers);/* Prints :The largest element is 3455" */
}

Since the name of an array itself is a pointer to the first element, we send that as an argument to the function greatestOfAll(). In the function, we traverse through the array using loop and pointer.

由于数组本身是指向第一个元素的指针,因此我们将其作为参数发送给函数greatestOfAll() 。 在函数中,我们使用循环和指针遍历数组。

#include <stdio.h>
#include <string.h>void wish(char *p){printf("Have a nice day, %s",p);
}main(){printf("Enter your name : \n");char name[20];gets(name);wish(name);
}

Here, we pass in the string name to wish() using a pointer and print the message.

在这里,我们使用指针将字符串name传递给wish()并打印消息。

3.指针作为函数返回 (3. Pointers as Function Return)

#include <stdio.h>int* multiply(int *a, int *b){int c = *a * *b;return &c;
}main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}

The function multiply() takes two pointers to int. It returns a pointer to int as well which stores the address where the product is stored.

函数multiply()将两个指针指向int 。 它还返回一个指向int的指针,该指针存储产品存储的地址。

It is very easy to think that the output would be 15. But it is not!

很容易想到输出为15。但是事实并非如此!

When multiply() is called, the execution of main() pauses and memory is now allocated for the execution of multiply(). After its execution is completed, the memory allocated to multiply() is deallocated.

当调用multiply()main()的执行暂停,并且现在为执行multiply()分配了内存。 执行完成后,分配给multiply()的内存被释放。

Therefore, though c ( local to main()) stores the address of the product, the data there is not guaranteed since that memory has been deallocated.

因此,尽管c (对main()本地)存储了产品的地址,但是由于该内存已被释放,因此无法保证其中的数据。

So does that mean pointers cannot be returned by a function? No!

那么,这是否意味着函数不能返回指针? 没有!

We can do two things. Either store the address in the heap or global section or declare the variable to be static so that their values persist.

我们可以做两件事。 将地址存储在堆或全局节中,或者将变量声明为static变量,以便其值持久存在。

Static variables can simply be created by using the keyword static before data type while declaring the variable.

可以通过在声明变量时在数据类型之前使用关键字 static来简单地创建static变量。

To store addresses in heap, we can use library functions malloc() and calloc() which allocate memory dynamically.

要将地址存储在堆中,我们可以使用库函数malloc()calloc()动态分配内存。

The following programs will explain both the methods. Both methods return the output as 15.

以下程序将说明这两种方法。 两种方法都将输出返回为15。

#include <stdio.h>
#include <stdlib.h>/* Using malloc() */int* multiply(int *a, int *b){int *c = malloc(sizeof(int));*c = *a * *b;return c;
}main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}/* Using static keyword */
#include <stdio.h>int* multiply(int *a, int *b){static int c;c = *a * *b;return &c;
}main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}

4.功能指针 (4. Pointer to Function)

Like pointer to different data types, we also have a pointer to function as well.

像指向不同数据类型的指针一样,我们也有一个指向函数的指针。

A pointer to function or function pointer stores the address of the function. Though it doesn't point to any data. It points to the first instruction in the function.

函数的指针函数指针存储函数的地址。 虽然它没有指向任何数据。 它指向函数中的第一条指令。

The syntax for declaring a pointer to function is:

声明函数指针的语法为:

/* Declaring a function */
returnType functionName(parameterType1, pparameterType2, ...);/* Declaring a pointer to function */
returnType (*pointerName)(parameterType1, parameterType2, ...);
pointerName = &functionName; /* or pointerName = functionName; */

The below example will make it clearer.

以下示例将使其更加清晰。

int* multiply(int *a, int *b)
{int *c = malloc(sizeof(int));*c = *a * *b;return c;
}main()
{ int a=3,b=5;int* (*p)(int*, int*) = &multiply; /* or int* (*p)(int*, int*) = multiply; */int *c = (*p)(&a,&b); /* or int *c = p(&a,&b); */printf("Product = %d",*c);
}

The declaration for the pointer p to function multiply() can be read as ( following operator precedence) - p is a pointer to function with two integer pointers ( or two pointers to int) as parameters and returning a pointer to int.

指向函数multiple multiply()的指针p的声明可以理解为(遵循运算符优先级 ) p是具有两个int eger指针(或指向int两个指针)作为参数并返回指向int的指针的函数的指针

Since the name of the function is also a pointer to the function, the use of & is not necessary. Also removing * from the function call doesn't affect the program.

由于函数的名称也是函数的指针,因此不需要使用& 。 同样从函数调用中删除*不会影响程序。

5.函数指针数组 (5. Array of Pointers to Functions)

We have already seen how to create an array of pointers to int, char, and so on. Similarly, we can create an array of pointers to function.

我们已经看到了如何创建一个指向intchar等指针的数组。 同样,我们可以创建一个函数指针数组。

In this array, every element will store an address of a function, where all the functions are of the same type. That is, they have the same type and number of parameters and return types.

在此数组中,每个元素都将存储一个函数的地址,其中所有函数均为同一类型。 也就是说,它们具有相同的类型和数量的参数以及返回类型。

We will modify a program discussed earlier in this section. We will store the addresses of add(), subtract(), multiply() and divide() in an array make a function call through subscript.

我们将修改本节前面讨论的程序。 我们将把add()subtract()multiply()divide()的地址存储在数组中,以通过下标进行函数调用。

#include <stdio.h>void add(float *a, float *b){float c = *a + *b;printf("Addition gives %.2f\n",c);
}void subtract(float *a, float *b){float c = *a - *b;printf("Subtraction gives %.2f\n",c);
}void multiply(float *a, float *b){float c = *a * *b;printf("Multiplication gives %.2f\n",c);
}void divide(float *a, float *b){float c = *a / *b;printf("Division gives %.2f\n",c);
}main(){printf("Enter two numbers :\n");float a,b;scanf("%f %f",&a,&b);printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n");char operation = '0';scanf(" %c",&operation);void (*p[])(float* , float*) = {add,subtract,multiply,divide};printf("\nOperating...\n\n");switch (operation) {case 'a':p[0](&a,&b);break;case 's':p[1](&a,&b);break;case 'm':p[2](&a,&b);break;case 'd':p[3](&a,&b);break;default:printf("Invalid input!!!\n");}}

The declaration here can be read as - p is an array of pointer to functions with two float pointers as parameters and returning void.

此处的声明可以读为p是指向函数的指针数组,其中两个float指针作为参数并返回void

6.指向参数的指针 (6. Pointer to Function as an Argument)

Like any other pointer, function pointers can also be passed to another function, therefore known as a callback function or called function. The function to which it is passed is known as a calling function.

像任何其他指针一样,函数指针也可以传递给另一个函数,因此称为回调函数称为function 。 传递给它的函数称为调用函数

A better way to understand would be to look at qsort(), which is an inbuilt function in C. It is used to sort an array of integers, strings, structures, and so on. The declaration for qsort() is:

更好的理解方法是看一下qsort() ,它是C语言中的内置函数。它用于对整数,字符串,结构等数组进行排序。 qsort()的声明为:

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));

qsort() takes four arguments:

qsort()有四个参数:

  1. a void pointer to the start of an array

    指向数组开头的void指针

  2. number of elements元素数量
  3. size of each element每个元素的大小
  4. a function pointer that takes in two void pointers as arguments and returns an int

    一个函数指针,它接受两个void指针作为参数并返回一个int

The function pointer points to a comparison function that returns an integer that is greater than, equal to, or less than zero if the first argument is respectively greater than, equal to, or less than the second argument.

函数指针指向一个比较函数 ,如果第一个参数分别大于,等于或小于第二个参数,则该比较函数返回一个大于,等于或小于零的整数。

The following program showcases its usage:

以下程序展示了其用法:

#include <stdio.h>
#include <stdlib.h>int compareIntegers(const void *a, const void *b)
{const int *x = a;const int *y = b;return *x - *y;
}main(){int myArray[] = {97,59,2,83,19,97};int numberOfElements = sizeof(myArray) / sizeof(int);printf("Before sorting - \n");for(int i = 0; i < numberOfElements; i++)printf("%d ", *(myArray + i));qsort(myArray, numberOfElements, sizeof(int), compareIntegers);printf("\n\nAfter sorting - \n");for(int i = 0; i < numberOfElements; i++)printf("%d ", *(myArray + i));}/* Output */Before sorting -
97 59 2 83 19 97After sorting -
2 19 59 83 97 97

Since a function name is itself a pointer, we can write compareIntegers as the fourth argument.

由于函数名称本身就是一个指针,因此我们可以将compareIntegers编写为第四个参数。

D.结构 (D. Structure)

1.结构指针 (1. Pointer to Structure)

Like integer pointers, array pointers, and function pointers, we have pointer to structures or structure pointers as well.

像整数指针,数组指针和函数指针一样,我们也有指向结构的指针或结构指针。

struct records {char name[20];int roll;int marks[5];char gender;
};struct records student = {"Alex", 43, {76, 98, 68, 87, 93}, 'M'};struct records *ptrStudent = &student;

Here, we have declared a pointer ptrStudent of type struct records. We have assigned the address of student to ptrStudent.

在这里,我们声明了一个struct records类型的指针ptrStudent 。 我们已经将student的地址分配给了ptrStudent

ptrStudent stores the base address of student, which is the base address of the first member of the structure. Incrementing by 1 would increase the address by sizeof(student) bytes.

ptrStudent存储student的基址,它是该结构的第一个成员的基址。 递增1将使地址增加sizeof(student)字节。

printf("Address of structure = %d\n", ptrStudent);
printf("Adress of member `name` = %d\n", &student.name);
printf("Increment by 1 results in %d\n", ptrStudent + 1);/* Output */
Address of structure = 6421984
Adress of member `name` = 6421984
Increment by 1 results in 6422032

We can access the members of student using ptrStudent in two ways. Using our old friend * or using -> ( infix or arrow operator).

我们可以通过两种方式使用ptrStudent访问student成员。 使用我们的老朋友*或使用-> (中缀或箭头运算符 )。

With *, we will continue to use the .( dot operator) whereas with -> we won't need the dot operator.

使用* ,我们将继续使用. (点运算符),而使用->则不需要点运算符。

printf("Name w.o using ptrStudent : %s\n", student.name);
printf("Name using ptrStudent and * : %s\n", ( *ptrStudent).name);
printf("Name using ptrStudent and -> : %s\n", ptrStudent->name);/* Output */
Name without using ptrStudent: Alex
Name using ptrStudent and *: Alex
Name using ptrStudent and ->: Alex

Similarly, we can access and modify other members as well. Note that the brackets are necessary while using * since the dot operator(.) has higher precedence over *.

同样,我们也可以访问和修改其他成员。 请注意,使用*必须使用方括号,因为点运算符( . )的优先级高于*

2.结构数组 (2. Array Of Structure)

We can create an array of type struct records and use a pointer to access the elements and their members.

我们可以创建一个类型为struct records的数组,并使用一个指针来访问元素及其成员。

struct records students[10];/* Pointer to the first element ( structure) of the array */
struct records *ptrStudents1 = &students;/* Pointer to an array of 10 struct records */
struct records (*ptrStudents2)[10] = &students;

Note that ptrStudent1 is a pointer to student[0] whereas ptrStudent2 is a pointer to the whole array of  10 struct records. Adding 1 to ptrStudent1 would point to student[1].

注意, ptrStudent1是指向student[0]的指针,而ptrStudent2是指向10个struct records的整个数组的指针。 将1添加到ptrStudent1将指向student[1]

We can use ptrStudent1 with a loop to traverse through the elements and their members.

我们可以将ptrStudent1与循环一起使用,以遍历元素及其成员。

for( int i = 0; i <  10; i++)
printf("%s, %d\n", ( ptrStudents1 + i)->name, ( ptrStudents1 + i)->roll);

3.指向结构的论点 (3. Pointer to Structure as an Argument)

We can also pass the address of a structure variable to a function.

我们还可以将结构变量的地址传递给函数。

#include <stdio.h>struct records {char name[20];int roll;int marks[5];char gender;
};main(){struct records students = {"Alex", 43, {76, 98, 68, 87, 93},
'M'};printRecords(&students);
}void printRecords( struct records *ptr){printf("Name: %s\n", ptr->name);printf("Roll: %d\n", ptr->roll);printf("Gender: %c\n", ptr->gender);for( int i = 0; i < 5; i++)printf("Marks in %dth subject: %d\n", i, ptr->marks[i]);
}/* Output */
Name: Alex
Roll: 43
Gender: M
Marks in 0th subject: 76
Marks in 1th subject: 98
Marks in 2th subject: 68
Marks in 3th subject: 87
Marks in 4th subject: 93

Note that the structure struct records is declared outside main(). This is to ensure that it is available globally and printRecords() can use it.

请注意,结构struct records是在main()外部声明的。 这是为了确保它在全局范围内可用,并且printRecords()可以使用它。

If the structure is defined inside main(), its scope will be limited to main(). Also structure must be declared before the function declaration as well.

如果结构在main()内部定义,则其范围将限于main() 。 同样, 必须在函数声明之前声明结构。

Like structures, we can have pointers to unions and can access members using the arrow operator (->).

像结构一样,我们可以具有指向联合的指针,并且可以使用箭头运算符( -> )访问成员。

E.指针到指针 (E. Pointer to Pointer)

So far we have looked at pointer to various primitive data types, arrays, strings, functions, structures, and unions.

到目前为止,我们已经研究了指向各种原始数据类型,数组,字符串,函数,结构和联合的指针。

The automatic question that comes to the mind is – what about pointer to pointer?

想到的自动问题是-指针指针呢?

Well, good news for you! They too exist.

好吧,对您来说是个好消息! 它们也存在。

int var = 6;
int *ptr_var = &var;printf("Address of var = %d\n", ptr_var);
printf("Address of ptr_var = %d\n", &ptr_var);/* Output */
Address of var = 6422036
Address of ptr_var = 6422024

To store the address of int variable var, we have the pointer to int ptr_var. We would need another pointer to store the address of ptr_var.

为了存储int变量var的地址,我们有一个指向int ptr_var的指针。 我们将需要另一个指针来存储ptr_var的地址。

Since ptr_var is of type int *, to store its address we would have to create a pointer to int *. The code below shows how this can be done.

由于ptr_var的类型为int * ,因此要存储其地址,我们将必须创建一个指向int *的指针。 下面的代码显示了如何完成此操作。

int * *ptr_ptrvar = &ptr_var; /* or int* *ppvar or int **ppvar */

We can use ptr_ptrvar to access the address of ptr_var and use double dereferencing to access var.

我们可以用ptr_ptrvar访问的地址ptr_var和使用双解引用访问变种。

printf("Address of ptr_var = %d\n", ptr_ptrvar);
printf("Address of var = %d\n", *ptr_ptrvar);
printf("Value at var = %d\n", *(*ptr_ptrvar));/* Output */
Address of ptr_var = 6422024
Address of var = 6422036
Value at var = 6

It is not required to use brackets when dereferencing ptr_ptrvar. But it is a good practice to use them. We can create another pointer ptr_ptrptrvar, which will store the address of ptr_ptrvar.

取消引用ptr_ptrvar时,不需要使用方括号。 但是使用它们是一个好习惯。 我们可以创建另一个指针ptr_ptrptrvar ,它将存储ptr_ptrvar的地址。

Since ptr_ptrvar is of type int**, the declaration for ptr_ptrptrvar will be

由于ptr_ptrvar的类型为int** ,因此ptr_ptrptrvar的声明为

int** *ptr_ptrptrvar = &ptr_ptrvar;

We can again access ptr_ptrvar, ptr_var and var using ptr_ptrptrvar.

我们可以再次访问ptr_ptrvarptr_varvar使用ptr_ptrptrvar

printf("Address of ptr_ptrvar = %d\n", ptr_ptrptrvar);
printf("Value at ptr_ptrvar = %d\n",*ptr_ptrptrvar);
printf("Address of ptr_var = %d\n", *ptr_ptrptrvar);
printf("Value at ptr_var = %d\n", *(*ptr_ptrptrvar));
printf("Address of var = %d\n", *(*ptr_ptrptrvar));
printf("Value at var = %d\n", *(*(*ptr_ptrptrvar)));/* Output */
Address of ptr_ptrvar = 6422016
Value at ptr_ptrvar = 6422024
Address of ptr_var = 6422024
Value at ptr_var = 6422036
Address of var = 6422036
Value at var = 6

If we change the value at any of the pointer(s) using ptr_ptrptrvar or ptr_ptrvar, the pointer(s) will stop pointing to the variable.

如果我们使用ptr_ptrptrvarptr_ptrvar更改任何指针的值,则指针将停止指向该变量。

结论 (Conclusion)

Phew! Yeah, we're finished. We started from pointers and ended with pointers (in a way). Don't they say that the curve of learning is a circle!

! 是的,我们完成了。 我们从指针开始,以指针结束(以某种方式)。 他们不是说学习曲线是一个圆!

Try to recap all the sub-topics that you read. If you can recollect them, well done! Read the ones you can't remember again.

尝试重述您阅读的所有子主题。 如果您能记住它们,那就做得好! 再次阅读您不记得的内容。

This article is done, but you shouldn't be done with pointers. Play with them. Next, you can look into Dynamic Memory Allocation to get to know pointers better.

本文已完成,但您不应使用指针。 和他们一起玩。 接下来,您可以研究动态内存分配以更好地了解指针

Stay home, stay safe.

待在家里,保持安全。

翻译自: https://www.freecodecamp.org/news/pointers-in-c-are-not-as-difficult-as-you-think/

c语言指针官方解释

c语言指针官方解释_C语言中的指针解释了–它们并不像您想象的那么难相关推荐

  1. c语言实现函数给主函数中的指针赋值的方法

    //利用二维指针.自从学了c之后,还没怎么用过二维指针,这么算是记住了 /* c语言实现函数给主函数中的指针赋值的方法*/#include<stdio.h>void f (int **p) ...

  2. c语言字符串加减_C语言中指针的介绍

    C语言中指针的介绍 指针是C语言中广泛使用的一种数据类型. 运用指针编程是C语言最主要的风格之一.利用指针变量可以表示各种数据结构:能很方便地使用数组和字符串: 并能象汇编语言一样处理内存地址,从而编 ...

  3. 64位程序怎么判断指针是否有效_C 语言指针怎么理解?

    注: 看完这篇一定会让你理解透彻指针的本质 是的,这一篇的文章主题是「指针与内存模型」 说到指针,就不可能脱离开内存,学会指针的人分为两种,一种是不了解内存模型,另外一种则是了解. 不了解的对指针的理 ...

  4. c++ 形参用指针 还是对象_C语言:聚会上,我发现只有我没有对象!

    再一次程序员聚会上,C语言遇到了不少小伙伴:Java , Python, JavaScript,Ruby...... "指针?你说的是那容易出错的指针吗?现在有谁用指针啊?"  J ...

  5. c++指针详解_c语言详解sizeof

    一.sizeof的概念 sizeof是C语言的一种单目操作符,如C语言的其他操作符++.--等. 它并不是函数. sizeof操作符以字节形式给出了其操作数的存储大小. 操作数可以是一个表达式或括在括 ...

  6. c语言给结构体指针申请空间,结构体中的指针变量申请空间问题

    本人声明了一个结构体包含id,age,name三个属性 struct studentInfo { int id; int age; char *name[20]; }; 本人又创建了一个双向循环链表, ...

  7. c语言高低位拷贝_C语言指针详解

    1为什么使用指针 假如我们定义了 char a='A' ,当需要使用 'A' 时,除了直接调用变量 a ,还可以定义 char *p=&a ,调用 a 的地址,即指向 a 的指针 p ,变量 ...

  8. c语言教程+school,C语言教程方法用法 _C语言-w3school教程

    C语言 的 C语言教程 此C语言教程与编程方法面向C语言初学者和专业人士,帮助他们轻松了解和学习C语言编程.我们的C语言教程中使用程序解释每个主题. C语言被开发用于创建系统应用程序,直接与硬件设备( ...

  9. c语言 swap交换函数_C语言经典100题(14)

    1 上期答案揭晓 首先给大家看看上一篇文章C语言经典100题(13)中第三部分编程题的答案: #includeint main(){ int i,x,y,z; for(i=100;i<1000; ...

最新文章

  1. 微软亚研院CV大佬代季峰跳槽商汤为哪般?
  2. WinCE驱动编写小结(转载)
  3. java xmpp openfire_搭建Xmpp服务器Openfire
  4. 4G江湖大战全面开启 移动4G用户数突破9000万
  5. Mysql :removeAbandonedTimeout:180
  6. 找到7天内要过生日的记录
  7. 你想要的宏基因组-微生物组知识全在这(2020.03)
  8. 单片计算机基础及应用答案,单片机原理及应用课后习题参考答案1~6章(DOC)
  9. Au 音频效果参考(合集)
  10. 在VS中格式化代码 快捷键
  11. 基于YOLOv5的汽车座椅缺陷检测
  12. 问卷与量表数据分析(SPSS+AMOS)学习笔记(六) : SPSS 描述性统计分析
  13. rtc驱动模型及rx8025驱动学习
  14. Go语言 gorutine和channel协同工作经典应用案例 (Golang经典编程案例)
  15. Linux日文教程,Ubuntu日语、法语输入法的设置
  16. Vue之assets图片加载不出来
  17. Linux centOS 7.2 命令行下 静默安装部署oracle11g数据库
  18. JetBrains下载历史版本(IDEA、PyCharm、WebStorm、PhpStorm等)
  19. Git实用技巧36招
  20. RuntimeError: iter() is only supported inside of tf.function or when eager execution is enabled.

热门文章

  1. C++——size_t 和 size_type的区别
  2. 【今日CV 计算机视觉论文速览 第120期】Thu, 23 May 2019
  3. CSDN的一些年度大牛(2018)
  4. 字节缓冲流 BufferedOutputStream java
  5. 实例变量与局部变量的区别 java 1615135277
  6. ado.net的操作查询数据 0127
  7. Mysql 分组后组内排序按字段取最大或最小的数据
  8. 蓝桥杯 第七届 JAVA B组 凑算式
  9. 搭建Android的jenkins持续集成环境
  10. Sqli-labs less 47