C语言学习笔记——指针(二)

简介: C语言学习笔记——指针

5.指针数组

5.1.指针数组的基本介绍

要让数组的元素指向 int 或其他数据类型的地址(指针)。可以使用指针数组

5.2.指针数组定义

数据类型 *指针数组名[大小];
  1. 例如:double *arr[7]
  2. 申明一个名为arr的double类型指针数组
  3. 其由七个double类型指针组成。因此,arr中的每个元素都是指向double类型的指针

5.3.指针数组代码示例和内存布局示意图

#include<stdio.h>
void main() {
  int var[3] = { 10, 100, 1000 };
  int i;
  int* ptr[3];
  for (i = 0; i < 3; i++) {
    ptr[i] = &var[i];//将var数组元素的地址逐一赋值给相应下标的ptr指针数组
  }
  for (i = 0; i < 3; i++) {
    printf("var[%d] = %d\n", i, *ptr[i]);
  }
}

6b0f560849dd4ccc9a74ce9470150525.png

d2539341de084b5fb389f44dd4ad9c99.png

  1. 申明一个名为var的int类型数组;申明一个名为ptr的指针数组,其每个元素都是指向int类型的指针
  2. 经过第一次循环遍历后,它的每个元素被逐一赋值卫var数组的地址
  3. 指针数组也具有它的内存空间和地址。如图所示,0x1122、0x1126、0x112A,即是ptr指针数组的元素的地址,它们的内容分别是0x1133、0x1137、0x113B,分别对应的是var整型数组中的每个数组元素的地址

5.4.指针数组练习

请编写程序,定义一个指向字符的指针数组来存储字符串列表(四大名著书名), 并通过遍历该指针数组,显示字符串信息  (即:定义一个指针数组,该数组的每个元素,指向的是一个字符串)

#include <stdio.h>
void main() {
  char* books[4] = { "三国演义", "西游记", "红楼梦", "水浒传" };
  int i;
  for (i = 0; i < 4; i++) {
    printf("\nbooks[%d] 指向字符串是=%s %p", i, books[i], &books[i]);
  }
}

59883857c583461b8546cc50af31d7d8.png

6.指向指针的指针

6.1 基本介绍

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

d3f96fe0d3024b8d888590483a4bb616.png

设图中的Variable的Value为int类型,图中pointer1指向的是Variable,应该申明为 int* pointer1,则pointer2应该申明为int** pointer2

6.2.多重指针(二级,三级)代码示例

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

2.当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符, 比如 **ptr(当一个目标值被一个指针间接指向时,访问这个值的指针前面需要加两个*)

#include <stdio.h>
int main() {
  int var;
  int* ptr; //一级指针 
  int** pptr; //二级指针 
  int*** ppptr; // 三级指针 
  var = 3000;
  ptr = &var; // var 变量的地址赋给 ptr 
  pptr = &ptr;// 表示将 ptr 存放的地址,赋给 pptr
  ppptr = &pptr; // 表示将 pptr 存放的地址,赋给 ppptr
  printf("var 的地址 = %p\nvar = %d \n", &var, var);// 0x1133 3000
  printf("\nptr 的本身的地址 = %p\nptr 存放的地址 = %p\n*ptr = %d \n", &ptr, ptr, *ptr);
  printf("\npptr 本身地址 = %p\npptr 存放的地址 = %p\n**pptr = %d\n", &pptr, pptr, **pptr);
  printf("\nppptr 本身地址 = %p\nppptr 存放的地址 = %p\n***pptr = %d\n", &ppptr, ppptr, ***ppptr);
  return 0;
}

dfabdc24aeab4e2eb63858413ee05dbd.png

3.d6ce2ab79d66422bacffe8ed228c29ac.png

7.传递指针(地址)给函数

当函数的形参类型是指针类型时,是使用该函数时,需要传递指针,或者地址,或者数组给该形参

7.1.传地址或指针给指针变量

#include<stdio.h>
void test(int* p) {
  *p += 1;
}
void main() {
  int num = 90;
  int* p = &num;
  test(&num);
  printf("num = %d\n", num);
  test(p);
  printf("num = %d", num);
}

0b339e3e12544d8ea3b6183b62336d29.png


11b5e7dee78045a58f74f00f1d513c92.png

  1. test函数体需要传入一个int类型的指针,因此,不管是传入变量num的地址,还是指针p,都可以;变量num的地址为0x1122,指针p的内容为0x1122,因此,它们其实传入的是同一个地址
  2. 因为,test函数是对传入地址的内容进行改变,所以,它在执行后,虽然test栈被销毁,但是,它所改变num的值被保留了下来

7.2.传数组给指针变量

#include<stdio.h>
double getAverage(int* arr, int size) {
  int i, sum = 0;
  double avg; 
  for (i = 0; i < size; ++i) {
    // arr[0] = arr + 0 
    // arr[1] = arr + 1 个 int 字节(4)
    // arr[2] = arr + 2 个 int 字节(8) //...
    sum += arr[i];// arr[0] =>数组第一个元素的地址 arr[1] 
    printf("\narr 存放的地址=%p ", arr);
    avg = (double)sum / size; 
    return avg;
  }
}
double getAverage2(int* arr, int size) {
  int i, sum = 0;
  double avg;
  for (i = 0; i < size; ++i) {
    sum += *arr;
    printf("\narr 存放的地址=%p ", arr);
    arr++;// 指针的++运算, 会对 arr 存放的地址做修改
  }
  avg = (double)sum / size;
  return avg;
}
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;
}

60927534e4bc4369a5ad12016446bc0d.png

fab7ec7130a04cc38e6fde01643e5a0a.png

1.在main函数中,设balance数组的首地址为(0x1122)。在执行到avg = getAverage(balance, 5)时,因为double getAverage(int* arr, int size)函数需要传入一个int类型指针变量和一个int类型变量,而getAverage(balance, 5)中balance代表的是balance数组的首地址,传入getAverage数组的是一个地址,相当于指针变量

2.在getAverage函数体中,对传入的balance数组的首地址进行一系列操作,而非在函数体内复制一份balance数组并进行操作!

8.返回指针的函数

8.1.返回指针的函数的介绍

C语言允许函数的返回值是一个指针(地址),这样的函数称为指针函数

8.2.代码示例

请编写一个函数 strlong(),返回两个字符串中较长的一个

//请编写一个函数 strlong(),返回两个字符串中较长的一个
#include<stdio.h>
#include<string.h>
char* strlong(char* str1, char* str2) {
  //通过strlen函数计算字符串的长度
  printf("第一个字符串的长度为%d,第二个字符串的长度为%d\n", strlen(str1), strlen(str2));
  if (strlen(str1) >= strlen(str2)) {//如果字符串1的长度大于等于字符串2的长度,则返回指向字符串1的指针
    return str1;
  }
  else {
    return str2;//else,则返回指向字符串2的指针
  }
}
int main() {
  char str1[20],  str2[20], * str;
  printf("请输入一个字符串:\n");
  gets_s(str1, sizeof(str1));
  printf("请输入一个字符串:\n");
  gets_s(str2, sizeof(str2));
  str = strlong(str1, str2);//用str字符指针指向strlong返回的指针
  printf("这两个字符串中,更长的那个字符串是:%s", str);
  return 0;
}

9bc1e55897b64820974b0fe0acff02dc.png

8.3.指针函数注意事项和细节

  1. 用指针作为函数返回值时需要注意,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针不能指向这些数据
  2. 函数运行结束后会销毁该函数所有的局部数据,这里所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存
#include <stdio.h>
int* func() {
  int n = 100;//局部变量, 在 func 返回时,就会销毁
  return &n;
}
int main() {
  int* p = func(); //func 返回指针
  int n;
  n = *p;
  printf("\nvalue = %d\n", n);
  return 0;
}

2912d20a8e8d4efb86b553385fc06650.png

函数运行结束后,仅仅是放弃了对这块内存空间的使用权限,但是,这块内存空间的内容并没有被销毁,输出这块内存空间的内容,仍然有可能是原来的内容,但是,如果在执行多条语句后,这块内存空间就有可能被别的数据给覆盖,因此,返回的指针不能指向这些数据

3.C 语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为 static 变量

#include <stdio.h>
int* func() {
  static int n = 100; // 如果这个局部变量是 static 性质的,那么 n 存放数据的空间在静态数据区
  return &n;
}
int main() {
  int* p = func(); //func 返回指针
  int n;
  n = *p;
  printf("\nvalue = %d\n", n);
  return 0;
}

3338c3b92d494d0681e87aa8a8b1f410.png

015c17b76c58403bbaee1935844af74e.png

通过static int n =100;将100存入了静态存储区,返回的是静态存储区的地址,因此,在之后的main函数的使用中,仍然可以使用静态存储区中存储的100,而静态存储区不会被其他语句所占用

9.函数指针(指向函数的指针)

9.1 基本介绍

1.一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址(可以理解为把函数名当做函数的首地址,赋给函数指针)

2.把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

9.2.函数指针定义

returnType (*pointerName)(param list);
  1. returnType为函数指针指向的函数返回值类型
  2. pointerName为函数指针名称
  3. param list为函数指针指向的函数的参数列表
  4. 参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称
  5. 注意( )的优先级高于*,第一个括号不能省略,如果写作 returnType *pointerName(param list);就成了函数原型, 它表明函数的返回值类型为 returnType *(变成返回指针的函数)

9.3.函数指针代码示例

用函数指针来实现对函数的调用, 返回两个整数中的最大值

#include <stdio.h>
//说明 //1. max 函数 //2. 接收两个 int ,返回较大数 
int max(int a, int b){ 
  return a>b ? a : b;
}
int main() {
  int x, y, maxVal;
  //说明 函数指针 
  //1. 函数指针的名字 pmax 
  //2. int 表示 该函数指针指向的函数是返回 int 类型
  //3. (int, int) 表示 该函数指针指向的函数形参是接收两个 int 
  //4. 在定义函数指针时,也可以写上形参名 int (*pmax)(int x, int y) = max;
  int (*pmax)(int, int) = max;
  printf("Input two numbers:\n");
  scanf_s("%d %d", &x, &y);
  // (*pmax)(x, y) 通过函数指针去调用 函数max
  maxVal = (*pmax)(x, y);//可以把这个语句中的*去掉
  printf("Max value: %d pmax = %p 本身的地址=%p\n", maxVal, pmax , &pmax);
  return 0;
}

500282ffab4d4afe97d4606605c58865.png

5dd97cc8a21c4d5692c6067223ac0ef0.png

  1. 申明一个名为*pmax函数返回值类型为int的函数指针,它需要指向的函数需要传入两个int类型的变量,它指向max函数
  2. pmax函数指针的地址为0x1133,max函数在内存空间(代码区)的首地址为0x1122,因此,它所指向的内存空间(代码区)中max函数的首地址为0x1122
  3. 调用函数的方式可以是(*pmax)(int)(int),也可以使(pmax)(int)(int),前提是前面必须申明过(1)中的调用函数类型
相关文章
|
2月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
52 0
|
20天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
81 12
|
13天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
2月前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
20 2
|
2月前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
2月前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
2月前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
2月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
2月前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
2月前
|
C语言
C语言指针(3)
C语言指针(3)
14 1