您的位置:

深入理解C语言指针

一、指针的定义及基本概念

指针在C语言中具有重要的作用,它可以用来访问和操作内存中的数据,是C语言中的一种强大的工具。指针实际上就是相应数据类型的内存地址,指针变量存储的是数据的内存地址。下面我们来看一个简单的示例:

#include 
int main()
{
    int a = 3, b = 4;
    int *pointer = &a;
    printf("%d,%d,%d\n",*pointer,a,b);  //3,3,4
    *pointer = 5;
    printf("%d,%d,%d\n",*pointer,a,b);  //5,5,4
    pointer = &b;
    printf("%d,%d,%d",*pointer,a,b);  //4,5,4
    return 0;
}

  

在上面的代码中,我们定义了一个指向整型变量a的指针,并将其初始化为a的内存地址。之后我们打印了指针所指向的变量a的值(即3),然后我们通过指针修改了a的值,并打印了a和指针指向的值(即5)。然后我们将指针指向了变量b,再次打印指针指向的值,此时输出的是变量b的值,即4。

二、指针的运算

指针的运算包括指针的加减、指针的比较。指针的加减通常用于指向数组中的元素,可以实现数组元素的遍历。指针之间的比较可以用于判断指向的地址大小关系,在排序、查找算法中有很重要的作用。下面我们来看一个示例:

#include 
int main()
{
    int arr[5] = {1,2,3,4,5};
    int *pointer = arr;
    printf("%d,%d,%d\n",*pointer,*(pointer+1),*(pointer+2));  //1,2,3
    if(pointer
   

    

在上面的代码中,我们定义了一个包含5个整型元素的数组,然后我们将指针指向了数组的首地址。之后我们通过指针访问数组中的元素,输出了前三个元素的值。最后我们比较了指针和数组最后一个元素的地址大小关系。由于指针指向的是数组的首地址,而数组名代表的是数组的首地址,因此arr+4实际上表示的是数组最后一个元素的下一个地址,所以我们判断了指针是否小于arr+4,输出了提示信息。

三、指针与函数

指针在函数中也扮演着重要的角色,指针参数可以让函数直接操作传递的数据,同时也能将函数内部的操作结果传递给调用者。下面我们来看一个示例:

#include 
void swap(int *p1, int *p2)
{
    int temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp; 
}
int main()
{
    int a = 3, b = 4;
    printf("%d,%d\n",a,b);  //3,4
    swap(&a,&b);
    printf("%d,%d",a,b);  //4,3
    return 0;
}

     

在上面的代码中,我们定义了一个swap函数,它接受两个整型指针参数,并通过交换两个指针所指向的值,完成了两个整型变量的交换。在主函数中,我们定义了两个变量a和b,并打印了它们的值,随后我们调用了swap函数,并传递了a和b的地址,完成了变量的交换。之后我们再次输出a和b的值,可以看到它们已经交换了。

四、指向指针的指针

指向指针的指针是C语言中最复杂的概念之一,当我们定义一个指向指针的指针时,它实际上就是二级指针。指向指针的指针通常用于动态内存分配以及函数参数传递等场合。下面我们来看一个示例:

#include 
int main()
{
    int a = 3, *p1 = &a, **p2 = &p1;
    printf("%d,%d,%d,%d\n",a,*p1,**p2,***p2);  //3,3,3,3
    ***p2 = 5;
    printf("%d,%d,%d,%d",a,*p1,**p2,***p2);  //5,5,5,5
    return 0;
}

     

在上面的代码中,我们定义了一个整型变量a,并定义了一个指向a的指针p1。随后我们定义了一个指向p1的指针p2,并通过二级指针间接访问了变量a的值。之后我们通过三级指针修改了变量a的值,并打印了变量a、指针p1、指针p2以及三级指针指向的值,可以发现它们都变成了5。

五、指针的常见误区

指针虽然在C语言中具有重要的作用,但也有很多容易出错的地方。下面我们举几个常见误区:

1、未初始化的指针

#include 
int main()
{
    int *p;
    printf("%d",*p);  //可能输出任意值,也可能导致程序崩溃
    return 0;
}

     

在上面的代码中,我们定义了一个指针p,但没有给它初始化。当我们通过指针访问变量时,由于指针未指向有效的内存地址,可能导致程序崩溃。

2、指针类型的不匹配

#include 
int main()
{
    int a = 3;
    float *p = &a;
    return 0;
}

     

在上面的代码中,我们定义了一个整型变量a,然后我们定义了一个浮点型指针p,并将其初始化为a的地址。由于指针类型与变量类型不匹配,编译器会给出警告或错误。

3、指针的非法操作

#include 
int main()
{
    int arr[5] = {1,2,3,4,5};
    int *p = arr;
    p += 6;  //非法
    return 0;
}

     

在上面的代码中,我们定义了一个整型数组arr,然后我们定义了一个指针p,并将其初始化为数组的首地址。当我们对指针进行加减运算时,如果越界访问数组,就会产生非法操作。