美文网首页
C语言指针学习笔记

C语言指针学习笔记

作者: leilifengxingmw | 来源:发表于2021-07-11 20:13 被阅读0次

每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。

#include <stdio.h>
 
int main ()
{
    int var_runoob = 10;
    int *p;              // 定义指针变量,整型的指针只能存储整型变量的地址。
    //把变量地址赋值给指针
    p = &var_runoob;
   printf("var_runoob 变量的地址: %p\n", p);
   return 0;
}

输出结果

var_runoob 变量的地址: 0x7ffee83f73a8

图片示意图

c-pointer.png
#include <stdio.h>
 
int main ()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */
 
   ip = &var;  /* 在指针变量中存储 var 的地址 */
 
   printf("var 变量的地址: %p\n", &var  );
 
   /* 在指针变量中存储的地址 */
   printf("ip 变量存储的地址: %p\n", ip );
 
   //注释1处,使用指针访问值
   printf("*ip 变量的值: %d\n", *ip );
 
   return 0;
}

输出结果

var 变量的地址: 0x7ffee4c7e3a8
ip 变量存储的地址: 0x7ffee4c7e3a8
*ip 变量的值: 20

获取指针指向的值

注释1处,使用指针访问值,要获取指针变量指向的值,就是在指针变量前面加一个星号。

printf("*ip 变量的值: %d\n", *ip );

C 指针的算术运算

C 指针的算术运算

C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

  • 指针的每一次递增,它其实会指向下一个元素的存储单元。
  • 指针的每一次递减,它都会指向前一个元素的存储单元。
  • 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。

递增一个指针,使用指针遍历数组

#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   // 注释1处,指针指向数组中第一个元素的地址
   ptr = var;
   for ( i = 0; i < MAX; i++)
   {
 
      printf("存储地址:var[%d] = %p\n", i, ptr );
      //输出存储的值,要在指针变量前面加一个*号。
      printf("存储值:var[%d] = %d\n", i, *ptr );
 
      /* 指向下一个位置 */
      ptr++;
   }
   return 0;
}

注释1处,指针指向数组中第一个元素的地址 ptr = var;,等价于 ptr = &var[0];

输出结果

存储地址:var[0] = 0x7ffee201739c
存储值:var[0] = 10
存储地址:var[1] = 0x7ffee20173a0
存储值:var[1] = 100
存储地址:var[2] = 0x7ffee20173a4
存储值:var[2] = 200

指针的比较

#include <stdio.h>
 
// 指针的比较

const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   //注释1处,指针中第一个元素的地址 
   ptr = var;
   i = 0;
   while ( ptr <= &var[MAX - 1] )
   {
 
      printf("存储地址:var[%d] = %p\n", i, ptr );
      printf("存储值:var[%d] = %d\n", i, *ptr );
 
      /* 指向上一个位置 */
      ptr++;
      i++;
   }
   return 0;
}

注释1处,指针指向数组中第一个元素的地址 ptr = var;,等价于 ptr = &var[0];

输出结果

存储地址:var[0] = 0x7ffee34673ac
存储值:var[0] = 10
存储地址:var[1] = 0x7ffee34673b0
存储值:var[1] = 100
存储地址:var[2] = 0x7ffee34673b4
存储值:var[2] = 200

C 指针数组

整型指针数组

 #include <stdio.h>

const int MAX = 3;

int main()
{
    int var[] = {10, 100, 200};
    int i;
    int *ptr[MAX]; //声明一个整数指针数组

    for (i = 0; i < MAX; i++)
    {
        ptr[i] = &var[i]; /* 赋值为整数的地址 */
    }

    for (i = 0; i < MAX; i++)
    {
        printf("Value of var[%d] = %d\n", i, var[i]);
        //使用指针输出
        printf("Value of var[%d] = %d\n", i, *ptr[i]);
    }

    return 0;
}

字符串指针数组

#include <stdio.h>
 
 //声明一个字符串指针数组
const int MAX = 4;
 
int main ()
{
   const char *names[] = {
                   "Zara Ali",
                   "Hina Ali",
                   "Nuha Ali",
                   "Sara Ali",
   };
   int i = 0;
 
   for ( i = 0; i < MAX; i++)
   {
      printf("Value of names[%d] = %s\n", i, names[i] );
   }
   return 0;
}

指向指针的指针: C 指向指针的指针

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

pointer_to_pointer.jpg

一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:

int **var;

当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下面实例所示:

c-pointerxxxxx.png
#include <stdio.h>
#include <stdlib.h>

//指向指针的指针
int main()
{
    int V;
    int *pt1;
    int **pt2;

    V = 100;

    /* 获取 V 的地址 */
    pt1 = &V;

    /* 使用运算符 & 获取 pt1 的地址,赋值给pt2 */
    pt2 = &pt1;

    printf("var = %d\n", V);//输出v的值
    printf("pt1 = %p\n", pt1);//输出pt1指向的地址
    printf("*pt1 = %d\n", *pt1);//输出pt1的最终指向的值
    printf("pt2 = %p\n", pt2);//输出pt2指向的地址,也就是pt1的地址
    printf("**pt2 = %d\n", **pt2);//输出pt2最终指向的值
    return 0;
}

输出结果

var = 100
pt1 = 0x7ffeee6523a8
*pt1 = 100
pt2 = 0x7ffeee6523a0
**pt2 = 100

C 传递指针给函数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* 函数声明 */

//getAverage方法的第一个参数是一个int类型的指针。
double getAverage(int *arr, int size);
//getSeconds方法的参数是一个unsigned long类型的指针。
void getSeconds(unsigned long *par);

//传递指针给函数
int main()
{
    unsigned long sec;
    //传入的是sec的地址
    getSeconds(&sec);
    /* 输出实际值 */
    printf("Number of seconds: %ld\n", sec);

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

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

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

    return 0;
}

void getSeconds(unsigned long *par)
{
    /* 获取当前的秒数 */
    *par = time(NULL);
    return;
}

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;
}

C 从函数返回指针

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* 要生成和返回随机数的函数 */
int *getRandom()
{
    static int arr[10];
    int i;

    /* 设置种子 */
    srand((unsigned)time(NULL));
    for (i = 0; i < 10; ++i)
    {
        arr[i] = rand();
        printf("%d\n", arr[i]);
    }
   //等价于返回的是数组的首个元素的地址
    return arr;
}

/* 要调用上面定义函数的主函数 */
int main()
{
    /* 一个指向整数的指针 */
    int *p;
    int i;

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

函数指针

函数指针是指向函数的指针变量。

通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。函数指针可以像一般函数一样,用于调用函数、传递参数。函数指针变量的声明:

那么这个指针变量怎么定义呢?虽然同样是指向一个地址,但指向函数的指针变量同我们之前讲的指向变量的指针变量的定义方式是不同的。例如:

int(*p)(int, int);

这个语句就定义了一个指向函数的指针变量 p。首先它是一个指针变量,所以要有一个“*”,即(*p);其次前面的 int 表示这个指针变量可以指向返回值类型为 int 型的函数;后面括号中的两个 int 表示这个指针变量可以指向有两个参数且都是 int 型的函数。所以合起来这个语句的意思就是:定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int(*)(int,int)

所以函数指针的定义方式为:

函数返回值类型 (* 指针变量名) (函数参数列表);

“函数返回值类型”表示该指针变量可以指向具有什么返回值类型的函数;“函数参数列表”表示该指针变量可以指向具有什么参数列表的函数。这个参数列表中只需要写函数的参数类型即可。

我们看到,函数指针的定义就是将“函数声明”中的“函数名”改成(*指针变量名)”。但是这里需要注意的是:(*指针变量名) 两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是定义函数指针而是一个函数声明了,即声明了一个返回值类型为指针型的函数。

例1:输入三个数,输出最大值

#include <stdio.h>
 
int max(int x, int y)
{
    return x > y ? x : y;
}

int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略
    int a, b, c, d;
 
    printf("请输入三个数字:");
    scanf("%d %d %d", & a, & b, & c);
 
    /* 与直接调用函数等价,d = max(max(a, b), c) */
    d = p(p(a, b), c); 
 
    printf("最大的数字是: %d\n", d);
 
    return 0;
}

回调函数

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

实例

实例中 populate_array 函数定义了三个参数,其中第三个参数是函数的指针,通过该函数来设置数组的值。

实例中我们定义了回调函数getNextRandomValue,它返回一个随机值,它作为一个函数指针传递给 populate_array 函数。

populate_array 将调用 10 次回调函数,并将回调函数的返回值赋值给数组。

#include <stdlib.h>
#include <stdio.h>

// 回调函数
//注释1处,int (*getNextValue)(void)  这个函数指针指向的函数类型: 返回值是int,函数参数是void
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i = 0; i < arraySize; i++)
        array[i] = getNextValue();
}

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

int main(void)
{
    //直接调用getNextRandomValue函数
    printf("直接调用getNextRandomValue函数,%d\n", getNextRandomValue());
    int myarray[10];
    /* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
    populate_array(myarray, 10, getNextRandomValue);
    for (int i = 0; i < 10; i++)
    {
        printf("%d\n", myarray[i]);
    }
    printf("\n");
    return 0;
}

注释1处,
int (*getNextValue)(void) 这个函数指针指向的函数类型: 返回值是int,函数参数是void。

输出结果

接调用getNextRandomValue函数,16807
282475249
1622650073
984943658
1144108930
470211272
101027544
1457850878
1458777923
2007237709
823564440

参考链接:

相关文章

网友评论

      本文标题:C语言指针学习笔记

      本文链接:https://www.haomeiwen.com/subject/gfhheltx.html