C语言学习笔记 - 指针

C指针

什么是指针?

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;     /* 一个字符型的指针 */

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。

如何使用指针

定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值,这些是通过使用一元运算符 *来返回位于操作数所指定地址的变量的值。

code: 
#include 

int main ()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */

   ip = &var;  /* 在指针变量中存储 var 的地址 */

   printf("Address of var variable: %p\n", &var  );

   /* 在指针变量中存储的地址 */
   printf("Address stored in ip variable: %p\n", ip );

   /* 使用指针访问值 */
   printf("Value of *ip variable: %d\n", *ip );

   return 0;
}

result:
Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20

C 中的 NULL 指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。

code:
#include 

int main ()
{
   int  *p = NULL;

   printf("p 的值是 %p\n", p  );

   return 0;
}

result:
p 的值是 0

如需检查一个空指针,您可以使用 if 语句:

if(p)     /* 如果 p 非空,则完成 */
if(!p)    /* 如果 p 为空,则完成 */

C指针的算术运算

C指针是一个用数值表示的地址,你可以用对指针进行+++- 运算操作。

递增一个指针

code:
#include 

const int LENGTH = 3;

int main (int argc, const char * argv[]) {
    int  var[] = {10, 100, 200};
    int  i, *ptr;

    /* 指针中的数组地址 */
    ptr = var;
    for ( i = 0; i < LENGTH; i++)
    {

        printf("存储地址:var[%d] = %p\n", i, ptr );
        printf("存储值:var[%d] = %d\n", i, *ptr );

        /* 移动到下一个位置 */
        ptr++;
    }
    return 0;
}

result:
存储地址:var[0] = 0x7ffeefbff67c
存储值:var[0] = 10
存储地址:var[1] = 0x7ffeefbff680
存储值:var[1] = 100
存储地址:var[2] = 0x7ffeefbff684
存储值:var[2] = 200

递减一个指针

CODE:
#include 

const int LENGTH = 3;

int main (int argc, const char * argv[]) {
    int  var[] = {10, 100, 200};
    int  i, *ptr;

    /* 指针中的数组地址 */
    ptr = &var[LENGTH - 1];
    for ( i = LENGTH; i > 0; i--)
    {

        printf("存储地址:var[%d] = %p\n", i - 1, ptr );
        printf("存储值:var[%d] = %d\n", i - 1, *ptr );

        /* 移动到下一个位置 */
        ptr--;
    }
    return 0;
}

RESULT:
存储地址:var[2] = 0x7ffeefbff684
存储值:var[2] = 200
存储地址:var[1] = 0x7ffeefbff680
存储值:var[1] = 100
存储地址:var[0] = 0x7ffeefbff67c
存储值:var[0] = 10

指针比较

CODE:
#include 

const int LENGTH = 3;

int main (int argc, const char * argv[]) {
    int  var[] = {10, 100, 200};
    int  i, *ptr;

    /* 指针中第一个元素的地址 */
    ptr = var;
    i = 0;
    while ( ptr <= &var[LENGTH - 1] )
    {

        printf("Address of var[%d] = %p\n", i, ptr );
        printf("Value of var[%d] = %d\n", i, *ptr );

        /* 指向上一个位置 */
        ptr++;
        i++;
    }
    return 0;
}

RESULT:
Address of var[0] = 0x7ffeefbff67c
Value of var[0] = 10
Address of var[1] = 0x7ffeefbff680
Value of var[1] = 100
Address of var[2] = 0x7ffeefbff684
Value of var[2] = 200

C指针数组

int *ptr[LENGTH]

这里将ptr声明为一个数组,由LENGTH个整数指针组成。因此,ptr中的每个元素,都是一个指向int值的指针。

CODE:
#include 

const int LENGTH = 3;

int main ()
{
    int  var[] = {10, 100, 200};
    int i, *ptr[LENGTH];

    for ( i = 0; i < LENGTH; i++)
    {
        ptr[i] = &var[i]; /* 赋值为整数的地址 */
    }
    for ( i = 0; i < LENGTH; i++)
    {
        printf("Value of var[%d] = %d\n", i, *ptr[i] );
    }
    return 0;
}

RESULT:
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

指向指针的指针

指向指针的指针是一种多级间接寻填的形式,或者说是一个指针链。一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。

int **var;

当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符。

CODE:
#include 

int main ()
{
   int  var;
   int  *ptr;
   int  **pptr;

   var = 3000;

   /* 获取 var 的地址 */
   ptr = &var;

   /* 使用运算符 & 获取 ptr 的地址 */
   pptr = &ptr;

   /* 使用 pptr 获取值 */
   printf("Value of var = %d\n", var );
   printf("Value available at *ptr = %d\n", *ptr );
   printf("Value available at **pptr = %d\n", **pptr);

   return 0;
}

RESULT:
Value of var = 3000
Value available at *ptr = 3000
Value available at **pptr = 3000

传递指针给函数

C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

CODE:
#include 

/* 函数声明 */
double getAverage(int *arr, int size);

int main ()
{
    /* 带有 5 个元素的整型数组  */
    int balance[5] = {1000, 2, 3, 17, 50};
    double avg;

    /* 传递一个指向数组的指针作为参数 */
    avg = getAverage( balance, 5 ) ;

    /* 输出返回值  */
    printf("Average value is: %f\n", avg );

    return 0;
}

double getAverage(int *arr, int size)
{
    int    i, sum = 0;
    double avg;

    for (i = 0; i < size; ++i)
    {
        sum += arr[i];
    }

    avg = (double)sum / size;

    return avg;
}

RESULT:
Average value is: 214.400000

从函数返回指针

从函数返回指针,您必须声明一个返回指针的函数。另外,C 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

CODE:
#include 

int * getArray();

int main (int argc, const char * argv[]) {

    int *p = getArray();
    int i;
    for (i = 0; i < 3; i++) {
        printf("*(p + [%d]) : %d\n", i, *(p+i));
    }
    return 0;
}

int * getArray() {
    static int array[] = {1, 3, 5};
    return array;
}

RESULT:
*(p + [0]) : 1
*(p + [1]) : 3
*(p + [2]) : 5

回调函数

函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。

CODE:
#include   
#include 

// 回调函数
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i=0; iarray[i] = getNextValue();
}

// 获取随机值
int getNextRandomValue(void)
{
    return rand();
}

int main(void)
{
    int myarray[10];
    populate_array(myarray, 10, getNextRandomValue);
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}


RESULT:
16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709 

指针的一些复杂说明:

  • int p; – 这是一个普通的整型变量
  • int p; – 首先从 p 处开始,先与结合,所以说明 p 是一个指针, 然后再与 int 结合, 说明指针所指向的内容的类型为 int 型。所以 p 是一个返回整型数据的指针。
  • int p[3] – 首先从 p 处开始,先与[] 结合,说明 p 是一个数组, 然后与 int 结合, 说明数组里的元素是整型的, 所以 p 是一个由整型数据组成的数组。
  • int p[3]; – 首先从 p 处开始, 先与 [] 结合, 因为其优先级比 高,所以 p 是一个数组, 然后再与 * 结合, 说明数组里的元素是指针类型, 然后再与 int 结合, 说明指针所指向的内容的类型是整型的, 所以 p 是一个由返回整型数据的指针所组成的数组。
  • int (p)[3]; – 首先从 p 处开始, 先与 结合,说明 p 是一个指针然后再与 [] 结合(与”()”这步可以忽略,只是为了改变优先级), 说明指针所指向的内容是一个数组, 然后再与int 结合, 说明数组里的元素是整型的。所以 p 是一个指向由整型数据组成的数组的指针。
  • int *p; – 首先从 p 开始, 先与 结合, 说是 p 是一个指针, 然后再与 * 结合, 说明指针所指向的元素是指针, 然后再与 int 结合, 说明该指针所指向的元素是整型数据。由于二级指针以及更高级的指针极少用在复杂的类型中, 所以后面更复杂的类型我们就不考虑多级指针了, 最多只考虑一级指针。
  • int p(int); – 从 p 处起,先与 () 结合, 说明 p 是一个函数, 然后进入 () 里分析, 说明该函数有一个整型变量的参数, 然后再与外面的 int 结合, 说明函数的返回值是一个整型数据。
  • int (*p)(int); – 从 p 处开始, 先与指针结合, 说明 p 是一个指针, 然后与()结合, 说明指针指向的是一个函数, 然后再与()里的 int 结合, 说明函数有一个int 型的参数, 再与最外层的 int 结合, 说明函数的返回类型是整型, 所以 p 是一个指向有一个整型参数且返回类型为整型的函数的指针。
  • int (*p(int))[3]; – 可以先跳过, 不看这个类型, 过于复杂从 p 开始,先与 () 结合, 说明 p 是一个函数, 然后进入 () 里面, 与 int 结合, 说明函数有一个整型变量参数, 然后再与外面的 结合, 说明函数返回的是一个指针, 然后到最外面一层, 先与[]结合, 说明返回的指针指向的是一个数组, 然后再与 * 结合, 说明数组里的元素是指针, 然后再与 int 结合, 说明指针指向的内容是整型数据。所以 p 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数。

你可能感兴趣的:(C)