C 语言指针


2021年8月23日, Learn eTutorial
2134

在本教程中,您将通过一些示例学习有关C语言指针的所有知识——什么是指针、如何在C语言中操作它们、指针的用途以及不同类型的指针。

C语言中的地址

我们知道,编程中的所有东西都存储在内存中,每个内存单元都包含两个实体——一个是地址位置,另一个是存储在其中的

Addresses in C

假设我们声明一个类型为 int 的变量 v,并将其初始化为值 1。


int v = 1;
 

值 1 将被存储在地址位置,比如这里的 2000,而 ‘v’ 将指向或包含值 1。下一个整数类型的值将在声明时被分配在连续的内存位置,如上所示。这表明每个变量都将有一个唯一的地址位置,无论其值如何。要获取一个变量的地址,我们在变量前使用与号(&)。


&v;
 

尝试下面的例子,看看 v 和 &v 有什么不同。


#include 
int main()
{
  int v = 1;
  printf("variable value is : %d\n", v);

  // & to retrieve the address 
  printf("address of variable is : %p", &v);  
  return 0;
}

输出


variable value is : 1
address of variable is : 000000000061FE1C

什么是C语言指针

指针是一种特殊类型的变量,它存储的是其他单元的地址位置而不是值。因此,在指针的情况下,我们既有指针本身的地址位置,也有存储在其中的另一个单元的地址位置。

What are Pointers in C

如何在C语言中声明指针

要使用指针变量,我们必须声明它的数据类型,这个数据类型应该与要存储其地址的值的数据类型相同。指针变量用星号 (*) 符号表示。


data_type * pointer_variable_name;
 

声明指针的有效方法如下所列。


int* p;
int * p;
int *p;

int* p1, v;   // p1 is a pointer variable while v is a normal variable

如何在C语言中初始化指针

指针的初始化就像标准变量的初始化一样,只不过是将地址初始化给指针变量,而不是值。语法如下:


Pointer_variable = &variable;
 

考虑下面给出的代码片段:


int v=1;  //variable initialization
int *p;   //declaration of pointer variable
p=&v;     //telling the compiler to store v's address in p
 

在这个例子中,变量 v 的地址被存储在指针变量 p 中。

要访问指针所指向的变量的值,我们使用星号(*)符号,它在与指针一起工作时被视为解引用运算符。

注意:变量和指针的数据类型是相同的。

声明时初始化指针

到目前为止,我们已经学习了如何在C语言中声明和初始化指针。现在我们可以将这两个步骤合并为一个,这没有任何区别,就像我们对标准变量所做的那样。语法如下:


data_type * pointer_variable = &variable;
 

上面的代码片段可以改为


int v=1;  //variable initialization
int *p = &v;  //declaration and initialization of pointer variable

当您不理解指针的真正含义时,这个语法常常会让初学者感到困惑。当您遇到 int *p = &v 时,您可能会认为 *p 是指针变量,但实际上,p 才是指针变量(* 符号有助于区分指针变量和标准变量)。因此,这里,一个指针变量 p 持有了一个标准变量 v 的地址。

C语言中指针的工作原理

内存中的指针最好可以可视化如下:

What are Pointers in C

从这个可视化图中,我们可以看到变量和指针是如何存储以及它们如何在内存中工作的。在内存中,每个段或分区是 1 字节的内存,每个字节都有一个地址,我们可以说 2000、2001、2002 等是每个字节的地址。每当我们声明一个标准变量,在程序执行时,内存会立即根据其数据类型进行分配。由于我们之前例子中声明的变量 v 是整数类型,它会分配 4 字节的内存,即从 2000 到 2003。

当我们声明一个指针变量时,也会发生同样的事情。由于指针应该声明为与变量相同的类型,这里的指针变量 p 是整数类型,它在不同的位置分配了 4 字节的内存,即从 4048 到 4051。

我们可以观察到指针变量 p 指向标准变量 v,并将其地址作为自己的值存储。指针变量持有地址 2000 作为其值,并拥有自己位于 4048 的地址。

由于我们操作的是地址,指针变量所做的任何更改都会反映在标准变量上。因此,借助指针,我们可以管理或操作地址。

如何在C语言中打印指针

我们已经学习了指针的所有基础知识,包括声明和初始化语法。现在我们将通过这个程序来理解C语言中指针的打印格式。


#include 
int main()
{
   int v=1;    //variable declaration
   int *p;      //pointer variable declaration
   p=&v;        // p stores the address of v

   //Standard variable
   printf("---Standard Variable Details---\n");
   printf("Address of variable v is :%x\n",&v);  
   printf("Value stored in v:%d\n",v);   

   //Pointer Variable
   printf("\n---Pointer Variable Details---\n");
   printf("Own Address of Pointer Variable p is: %x\n",&p);
   printf("Value stored in P :%x\n",p);  //accessing the address of v
   printf("Value of v accessing with p is:%d\n",*p);//accessing the value of v


   // Changing value in variable using pointer
     *p = 50;
     printf("\n Value stored in v changed to :%d\n",v);


   return 0;
}
 

输出


---Standard Variable Details---
Address of variable v is :61fe1c
Value stored in v:1

---Pointer Variable Details---
Own Address of Pointer Variable p  is: 61fe10

Value stored in P :61fe1c

Value of v accessing with p is:1

在这个程序中,v 的地址和存储在 p 中的值是相同的,这意味着指针变量总是持有它所指向的单元的地址。

注意:'*' 意味着 '值在','&' 意味着 '地址是'

C语言中指针的用途

指针不是程序的必要部分,但它是C语言支持的与硬件紧密交互的强大功能。由于C是一种低级语言,它大多依赖于机器并大量与内存打交道。因此,指针是处理内存的有效方式,因为它提供了以下特性。

  1. 使用指针的首要优势是,通过不创建传递的变量或函数的局部副本,指针可以极大地节省内存空间。
  2. 指针允许动态分配内存。具体来说,在进程的任何时候,您都可以根据需求在内存中创建和销毁空间。
  3. 通过使用指针,可以从多个位置引用内存中的同一空间。对一个位置所做的任何更改也将反映在所有其他位置。
  4. 由于指针可以直接访问内存(地址),使用指针的执行时间更快。
  5. 指针允许按引用调用,这使得一个函数能够改变另一个函数中变量的值。

这些是使指针成为C语言不可或缺部分的主要特性。现在让我们看看指针通常用在哪些地方。

  • 数组使用指针进行有效导航,也用于表示二维和多维数组。
  • 像链表和树这样的数据结构使用指针进行内存分配和释放。
  • 指针使文件处理变得轻松。
  • 由于指针与硬件交互良好,它是嵌入式系统的重要组成部分。
  • 指针在内存管理方面效率很高。
     

指针不仅限于C或C++,几乎所有高级语言都在后台交互中隐式使用它们。在C和C++中,指针是显式定义的。

什么是指向指针的指针?

现在我们非常清楚,指针存储了它所指向的变量的地址。但它也有自己的地址。在C语言中,指针也可以用来存储另一个指针的地址。这种类型的指针在C语言中被称为指向指针的指针(双重指针)。

这可以看作如下:

pointer to pointer

在这里,第一个指针 ptr1 存储了变量 var 的地址,而第二个指针 ptr2 持有了第一个指针 (ptr1) 的地址。ptr2 就是双重指针。双重指针的声明语法与指针的声明类似,只是需要一个额外的星号符号。


datatype **pointer_variable;

以下是说明双重指针含义的示例。


//double pointer
#include
void main ()
{
    int var = 1;
    int *ptr1;
    int **ptr2;
    ptr1 = &var; // pointer ptr1 is pointing to the address of var
    ptr2 = &ptr1; // pointer ptr2 is a double pointer pointing to the address of pointer ptr1
    printf("Address of var: %x\n",ptr1); // Address of variable will be printed
    printf("Address of ptr1: %x\n",ptr2); // Address of ptr1 will be printed
    printf("value stored at ptr1: %d\n",*ptr1); // value stored at the address contained by ptr
    printf("value stored at ptr2: %d\n",**ptr2); // value stored at the address contained by the pointer stored at ptr2

    printf("address of ptr2: %x\n",&ptr2); // Address of ptr2 will be printed
}

输出


Address of var: 61fe1c
Address of ptr1: 61fe10
value stored at ptr1: 1
value stored at ptr2: 1
address of ptr2: 61fe08

何时在C语言中使用双重指针

要理解何时在C语言中使用双重指针,需要一些解释。我们可以用一句话来定义双重指针的用途:

在您想要更改作为函数参数传递的指针的情况下,会使用双重指针。

假设你有一个指针 p1,其值为变量 v1 的地址,同时你还有另一个指针 p2,其值为 v2 的地址。现在你希望将 p1 的地址更改为 p2 的地址。是否可以更改存储在两个指针中的地址?当然可以,你可以通过赋值 p1 = p2 来实现,这意味着 p2 的地址现在存储在了 p1 中。你可以在下面的例子中看到这一点。


#include 

int main()
{

    int v1 = 10;
    int v2 = 20;

    int * p1 = &v1;
    int * p2 = &v2;
   
    printf(" p1's value: %x \n", p1);
    printf(" p2's value: %x \n", p2);
   
    printf("\n\n Can we change values of two pointers , say p1 and p2 ?");
    p1 = p2;
    printf("\n Value of p1 changed to: %x, same as 'p2' ", p1);
}
 

输出


p1's value: 61fe04
p2's value: 61fe00

Can we change values of two pointers , say p1 and p2 ?
Value of p1 changed to: 61fe00, same as 'p2'

现在我们将看看是否可以用函数实现同样的操作,以及程序会发生什么变化。是的,这可以通过函数实现,但唯一重要的问题是,对指针所做的更改在退出函数后是否仍然存在。这可以通过检查下面给出的例子来发现。


#include 

int main()
{
    int v1 = 10;
    int v2 = 20;

    int * p1 = &v1;
    int * p2 = &v2;

    printf(" p1's value: %x \n", p1);
    printf(" p2's value: %x \n", p2);

   printf("\n\n Can we change values of two pointers using function? \n");
   change_fun(p1, p2);
   printf("\n Outside Function : Value of 'p1' is : %x, same as 'p1' not 'p2'\n", p1);
  return 0;
}

void change_fun(int * x, int * z){
    x = z;
    printf(" Inside Function : Value of 'p1' is: %x  same as 'p2'", x);
}

 

输出


p1's value: 61fe0c
p2's value: 61fe08

Can we change values of two pointers using function?
Inside Function : Value of 'p1' is: 61fe08  same as 'p2'
Outside Function : Value of 'p1' is : 61fe0c, same as 'p1' not 'p2'

在这个程序中,指针的值在函数内部从 p1 变为 p2,并且这种变化仅存在于函数内部。在函数外部,指针 p1 的值保持不变,无论发生了什么变化。这表明在函数内部对指针所做的任何更改都只在该函数局部有效。

这会导致错误,并且在编程中不是一个好的实践。为了解决这个问题,我们可以使用双重指针。让我们观察程序并理解其中的技巧。


#include 

int main()
{
    int v1 = 10;
    int v2 = 20;

    int * p1 = &v1;
    int * p2 = &v2;
    int ** pp = &p1;  // pointer to pointer 'v1'

    printf(" p1's value: %x \n", p1);
    printf(" p2's value: %x \n", p2);

    printf("\n\n Can we change values of two pointers with the help of double pointers? \n");
    change_doublepointer(pp, p2);
   printf("\n Outside Function : Value of 'p1' is : %x, same as  'p2'\n", p1);
  return 0;
}
void change_doublepointer(int ** x, int * z){
    *x = z;
    printf(" Inside Function: Value of 'p1' is: %x , same as 'p2', \n", *x);

}

 

输出


p1's value: 61fe0c
p2's value: 61fe08

Can we change values of two pointers with the help of double pointers?
Inside Function: Value of 'p1' is: 61fe08 , same as 'p2',
Outside Function : Value of 'p1' is : 61fe08, same as  'p2'

这个程序将双重指针 pp 作为函数的参数传递,并执行相同的操作。我们知道双重指针指向另一个指针的地址,因此在函数内部所做的任何更改也会反映到函数外部。这是因为双重指针操作的是地址而不是值。

在下一篇教程中,您将学习关于C语言中不同类型的指针