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)中的调用函数类型
相关文章
|
8月前
|
存储 人工智能 Java
一文轻松拿捏C语言的指针的基础使用
本文介绍了C语言中的指针概念,包括直接访问和间接访问内存的方式、指针变量的定义与使用、取址运算符`&`和取值运算符`*`的应用,帮助读者深入理解指针这一C语言的核心概念。君志所向,一往无前!
153 0
|
10月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
322 7
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
10月前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
1200 9
|
10月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
309 7
|
11月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
11月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
11月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
638 3
|
11月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
11月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
11月前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
222 1