第九章 指针

C程序中的变量都是存储在计算机内存特定的存储单元中的,内存中的每个单元都有唯一的地址

通过取地址运算符&可以获得变量的地址

//L9-1

#include <stdio.h>
int main()
{
int a = 0, b = 1;
char c = 'A';
printf("a is %d, &a is %p\n", a, &a);
printf("b is %d, &b is %p\n", b, &b);
printf("c is %c, &c is %p\n", c, &c);
return 0;
}
//运行结果
a is 0, &a is 0060FF0C
b is 1, &b is 0060FF08
c is A, &c is 0060FF07

%p格式符,表示输出变量a、b、c的地址值

地址值是一个十六进制的无符号整数,其字长一般与主机的字长相同

  • 变量的地址:变量在内存中所占的存储空间的首地址
  • 变量的值:变量在存储空间中存放的数据
  • 变量的名字:对程序中数据存储空间的一种抽象

指针:存放变量的地址的特殊类型的变量

类型关键字 *指针变量名;

类型关键字代表指针变量要指向的变量的数据类型,即指针变量的基类型

指针变量的定义只是声明了指针变量的名字及其所能指向的数据类型,并没有说明指针变量究竟指向了哪里

//L9-2

#include <stdio.h>
int main()
{
int a = 0, b = 1;
char c = 'A';
int *pa, *pb; /* 定义指针变量pa和pb */
char *pc; /* 定义指针变量pc */
pa = &a; /* 初始化指针变量pa使其指向a */
pb = &b; /* 初始化指针变量pb使其指向b */
pc = &c; /* 初始化指针变量pc使其指向c */
printf("a is %d, &a is %p, pa is %p, &pa is %p\n", a, &a, pa, &pa);
printf("b is %d, &b is %p, pb is %p, &pb is %p\n", b, &b, pb, &pb);
printf("c is %c, &c is %p, pc is %p, &pc is %p\n", c, &c, pc, &pc);
return 0;
}
//运行结果
a is 0, &a is 0060FF0C, pa is 0060FF0C, &pa is 0060FF00
b is 1, &b is 0060FF08, pb is 0060FF08, &pb is 0060FEFC
c is A, &c is 0060FF07, pc is 0060FF07, &pc is 0060FEF8

指针变量未被初始化意味着指针变量的值是一个随机值,无法预知它会指向哪里

为避免忘记指针初始化给系统带来潜在危险,习惯上在定义指针变量的同时将其初始化为NULL

如int *pa = NULL;

在定义指针变量的同时可以对指针变量进行初始化

如int *pa = &a;

间接寻址运算符:

  • 直接寻址:直接按变量名或者变量的地址存取变量的内容的访问方式
  • 间接寻址:通过指针变量间接存取它所指向的变量的访问方式

间接寻址运算符*用来访问指针变量指向的变量的值

//L9-3

#include <stdio.h>
int main()
{
int a = 0, b = 1;
char c = 'A';
int *pa = &a, *pb = &b; /* 在定义指针变量pa、pb和pc的同时对其初始化 */
char *pc = &c;//*作为指针类型说明符用于指针变量的定义
printf("a is %d, &a is %p, pa is %p, *pa is %d\n", a, &a, pa, *pa);
//*作为间接引用运算符,用于读取并显示指针变量中存储的内存地址所对应的变量的值
printf("b is %d, &b is %p, pb is %p, *pb is %d\n", b, &b, pb, *pb);
printf("c is %c, &c is %p, pc is %p, *pc is %c\n", c, &c, pc, *pc);
return 0;
}
//运行结果
a is 0, &a is 0060FF00, pa is 0060FF00, *pa is 0
b is 1, &b is 0060FEFC, pb is 0060FEFC, *pb is 1
c is A, &c is 0060FEFB, pc is 0060FEFB, *pc is A

输出*pa的值和输出a的值是等价的,因此修改了*pa的值也就相当于修改了a的值

指针的解引用:引用指针变量所指向的变量的值

指针只有在真正指向了一块有意义的内存后,才能访问它的内容

使用指针需要恪守:

  1. 永远清楚每个指针指向了哪里
  2. 永远清楚每个指针指向的对象的内容是什么
  3. 永远不要使用未初始化的指针变量

按值调用

//L9-4

#include <stdio.h>
void Fun(int par);
int main()
{
int arg = 1 ;
printf("arg = %d\n", arg);
Fun(arg); /* 传递实参值的拷贝给函数 */
printf("arg = %d\n", arg);
return 0;
}
void Fun(int par)
{
printf("par = %d\n", par);
par = 2; /* 改变形参的值 */
}
//运行结果
arg = 1
par = 1
arg = 1

函数形参值的改变并未影响实参值的改变

这是因为传给函数形参的值只是函数调用语句中实参值的副本

因此,按值调用的方法不能在被调函数中改变其调用语句中的实参值

模拟按引用调用

//L9-5

#include <stdio.h>
void Fun(int *par);
int main()
{
int arg = 1 ;
printf("arg = %d\n", arg);
Fun(&arg); /* 传递变量arg的地址值给函数 */
printf("arg = %d\n", arg);
return 0;
}
void Fun(int *par)
{
printf("par = %d\n", *par); /* 输出形参指向的变量的值 */
*par = 2; /* 改变形参指向的变量的值 */
}
//运行结果
arg = 1
par = 1
arg = 2

将函数的形参声明为指针类型,使用指针变量作为函数形参

形参接收到的数据只能是一个地址值

函数Fun()使用间接寻址运算符*改变了形参指向的实参的值

模拟按引用调用是一种常用的从函数中返回修改了的数据值的方法

//L9-6

#include  <stdio.h>
void Swap(int *x, int *y);
int main()
{
int a, b;
printf("Please enter a,b:");
scanf("%d,%d", &a, &b);
printf("Before swap: a = %d, b = %d\n", a, b);/* 打印交换前的a,b */
Swap(&a, &b); /* 按地址调用函数Swap() */
printf("After swap: a = %d, b = %d\n", a, b);/* 验证a,b是否互换 */
return 0;
}
/* 函数功能:交换两个整型数的值 */
void Swap(int *x, int *y)
{
int temp;
temp = *x; /* 执行图7-5(b)中的步骤① */
*x = *y; /* 执行图7-5(b)中的步骤② */
*y = temp; /* 执行图7-5(b)中的步骤③ */
}
//运行结果
Please enter a,b:15,8
Before swap: a = 15, b = 8
After swap: a = 8, b = 15

用指针变量作函数参数

//L9-7

#include  <stdio.h>
#define N 30
void FindMax(int score[],long num[],int n,int *pMaxScore,long *pMaxNum);
int main()
{
int score[N], maxScore;
int n, i;
long num[N], maxNum;
printf("How many students?");
scanf("%d", &n); /* 从键盘输入学生人数n */
printf("Input student's ID and score:\n");
for (i=0; i<n; i++)
{
scanf("%ld%d", &num[i], &score[i]); /* 字母d前为字母l */
}
FindMax(score, num,n, &maxScore, &maxNum);/* 按地址调用函数 */
printf("maxScore = %d, maxNum = %ld\n", maxScore, maxNum);
return 0;
}
/* 函数功能:计算最高分及其相应学生的学号 */
void FindMax(int score[],long num[],int n,int *pMaxScore,long *pMaxNum)
{
int i;
*pMaxScore = score[0]; /* 假设score[0]为当前最高分 */
*pMaxNum = num[0]; /* 记录score[0]的学号num[0] */
for (i=1; i<n; i++) /* 对所有score[i]进行比较 */
{
if (score[i] > *pMaxScore) /* 如果score[i]高于当前最高分 */
{
*pMaxScore = score[i]; /* 用score[i]修改当前最高分 */
*pMaxNum = num[i]; /* 记录当前最高分学生的学号num[i] */
}
}
}
//运行结果
How many students?5
Input student's ID and score:
120310122 84
120310123 83
120310124 88
120310125 87
120310126 61
maxScore = 88, maxNum = 120310124

函数FindMax()的前3个形参在函数调用前必须确定其值,因此称为函数的入口参数

而指针变量的值在函数调用结束后才能被确定,因此指针形参称为函数的出口参数

函数指针:指向函数的指针

指向函数的指针变量中存储的是一个函数在内存中的入口地址

函数名就是这个函数的源代码在内存中的起始地址

编译器将不带()的函数名解释为函数的入口地址

//L9-8

#include <stdio.h>
#define N 40
int ReadScore(int score[]); /* 成绩输入函数原型 */
void PrintScore(int score[], int n); /* 成绩输出函数原型 */
void AscendingSort(int a[], int n); /* 升序排序函数原型 */
void DescendingSort(int a[], int n); /* 降序排序函数原型 */
void Swap(int *x, int *y); /* 两数交换函数原型 */
int main()
{
int score[N], n;
int order; /* 值为1表示升序排序,值为2表示降序排序 */
n = ReadScore(score); /* 输入成绩,返回学生人数 */
printf("Total students are %d\n",n);
printf("Enter 1 to sort in ascending order,\n");
printf("Enter 2 to sort in descending order:");
scanf("%d", &order);
printf("Data items in original order\n");
PrintScore(score, n); /* 输出排序前的成绩 */
if (order == 1)
{
AscendingSort(score, n); /* 按升序排序 */
printf("Data items in ascending order\n");
}
else
{
DescendingSort(score, n); /* 按降序排序 */
printf("Data items in descending order\n");
}
PrintScore(score, n); /* 输出排序后的成绩 */
return 0;
}
/* 函数功能:输入学生某门课的成绩,当输入负值时,结束输入,返回学生人数 */
int ReadScore(int score[])
{
int i = -1;
do{
i++;
printf("Input score:");
scanf("%d", &score[i]);
} while (score[i] >= 0);
return i;
}
/* 函数功能:输出学生成绩 */
void PrintScore(int score[], int n)
{
int i;
for (i=0; i<n; i++)
{
printf("%4d", score[i]);
}
printf("\n");
}
/* 函数功能: 选择法实现数组a的升序排序 */
void AscendingSort(int a[], int n) /* 升序排序函数定义 */
{
int i, j, k;
for (i=0; i<n-1; i++)
{
k = i;
for (j=i+1; j<n; j++)
{
if (a[j] < a[k]) k = j;
}
if (k != i) Swap(&a[k], &a[i]);
}
}
/* 函数功能:选择法实现数组a的降序排序 */
void DescendingSort(int a[], int n) /* 降序排序函数定义 */
{
int i, j, k;
for (i=0; i<n-1; i++)
{
k = i;
for (j=i+1; j<n; j++)
{
if (a[j] > a[k]) k = j;
}
if (k != i) Swap(&a[k], &a[i]);
}
}
/* 函数功能:两整数值互换 */
void Swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
//运行结果
Input score:90
Input score:91
Input score:92
Input score:86
Input score:97
Input score:-1
Total students are 5
Enter 1 to sort in ascending order,
Enter 2 to sort in descending order:2
Data items in original order
90 91 92 86 97
Data items in descending order
97 92 91 90 86

//L9-9

#include <stdio.h>
#define N 40
int ReadScore(int score[]);
void PrintScore(int score[], int n);
void SelectionSort(int a[], int n, int (*compare)(int a, int b));
int Ascending( int a, int b );
int Descending( int a, int b );
void Swap(int *x, int *y);
int main()
{
int score[N], n;
int order; /* 值为1表示升序排序,值为2表示降序排序 */
n = ReadScore(score); /* 输入成绩,返回学生人数 */
printf("Total students are %d\n",n);
printf("Enter 1 to sort in ascending order,\n");
printf("Enter 2 to sort in descending order:");
scanf("%d", &order);
printf("Data items in original order\n");
PrintScore(score, n); /* 输出排序前的成绩 */
if (order == 1)
{
SelectionSort(score, n, Ascending);/*函数指针指向Ascending()*/
printf("Data items in ascending order\n");
}
else
{
SelectionSort(score, n, Descending);/*函数指针指向Descending()*/
printf("Data items in descending order\n");
}
PrintScore(score, n); /* 输出排序后的成绩 */
return 0;
}
/* 函数功能:输入学生某门课的成绩,当输入负值时,结束输入,返回学生人数 */
int ReadScore(int score[])
{
int i = -1;
do{
i++;
printf("Input score:");
scanf("%d", &score[i]);
} while (score[i] >= 0);
return i;
}
/* 函数功能:输出学生成绩 */
void PrintScore(int score[], int n)
{
int i;
for (i=0; i<n; i++)
{
printf("%4d", score[i]);
}
printf("\n");
}
/* 函数功能:调用函数指针compare指向的函数实现对数组a的交换法排序 */
void SelectionSort(int a[], int n, int (*compare)(int a, int b))
{
int i, j, k;
for (i=0; i<n-1; i++)
{
k = i;
for (j=i+1; j<n; j++)
{
if ((*compare)(a[j], a[k])) k = j;
}
if (k != i) Swap(&a[k], &a[i]);
}
}
/* 使数据按升序排序 */
int Ascending( int a, int b )
{
return a < b; /* 这样比较决定了按升序排序,如果a<b,则交换 */
}
/* 使数据按降序排序 */
int Descending( int a, int b )
{
return a > b; /* 这样比较决定了按降序排序,如果a>b,则交换 */
}
/* 函数功能:两整数值互换 */
void Swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}

void SelectionSort(int a[], int n, int (*compare)(int a, int b))

告诉编译器函数SelectionSort的形参compare是一个指针变量,该指针变量可以指向一个两个整型形参、返回值为整型的函数

该函数指针既可以指向函数Ascending(),也可以指向函数Descending()

因此可以用这样一个通用的排序函数,实现升序或降序排列

虽然定义函数指针时并未指明该指针指向了哪个函数,但在程序中可以通过赋值操作将函数指针分别指向不同的函数,以实现在一点调用不同的函数

利用函数指针编程有助于提高程序的通用性、简洁性,减少重复的代码

C语言程序设计(九) 指针的更多相关文章

  1. 《C语言程序设计》指针篇<一>

    指针 指针是C语言的精华,同时也是其中的难点和重点,我在近日对这一部分内容进行了重新的研读,把其中的一些例子自己重新编写和理解了一遍.此篇博客的内容即是我自己对此书例子的一些理解和总结. 一.大问题: ...

  2. 《C语言程序设计》指针篇<二>

    通过指针引用多维数组 如何理解二维数组元素的地址? 要知道,这本书用了整整两页的内容来讲解这方面的知识,从这里足以看出来理解通过指针来引用二维数组是一件比较麻烦的事情,但是我认为理解并不难. 什么是二 ...

  3. 【MOOC课程学习记录】程序设计与算法(一)C语言程序设计

    课程结课了,把做的习题都记录一下,告诉自己多少学了点东西,也能给自己一点鼓励. ps:题目都在cxsjsxmooc.openjudge.cn上能看到,参考答案在差不多结课的时候也会在mooc上放出来. ...

  4. C语言程序设计课程设计自查表格

    课程设计自查表格 序号 项目 完成与否(完成打勾) 1 格式是否符合标准(缩进是否规范) 2 是否模块化设计(使用函数分解系统功能) 3 函数名否易懂(不得使用f1(int a1,int a2)这样的 ...

  5. 2018上C语言程序设计(高级)作业- 初步计划

    C语言程序设计(高级)36学时,每周4学时,共9周.主要学习指针.结构和文件三部分内容.整个课程作业计划如下: PTA和博客的使用指南 若第一次使用PTA和博客,请务必先把PTA的使用简介和教师如何在 ...

  6. C语言程序设计课程总结

    第一次教授C语言程序设计课程,相比计算机组成原理.arm体系结构等偏向硬件的课程,C的教学方式要灵活一些.计算机组成原理课程偏向理论,哈尔滨工业大学的计算机组成原理是国家精品课,增加了mooc+spo ...

  7. C语言程序设计II—第二周教学

    第二周教学总结(4/3-10/3) 教学内容 根据邹欣老师的建议,临时修改教学计划,将最后一周的内容:第十二章 文件,提前讲授. 课前准备 在博客园发布作业:2019春第二周作业 作业根据本周讲授的& ...

  8. C语言程序设计实习报告

    C语言程序设计实习报告 简介 语言实践心得体会范文在科技高度发展的今天,计算机在人们之中的作用越来越突出.而c语言作为一种计算机的语言,我们学习它,有助于我们更好的了解计算机,与计算机进行交流,因此, ...

  9. 《C语言程序设计(第四版)》阅读心得(一)

    本篇开始写我个人觉得谭浩强老师的<C语言程序设计(第四版)>中之前没有认识到,或者忘了的知识.因为本科学过,所以有些简单的东西就没有放进来了,所以可能并不是太全面. 第一章程序设计与语言 ...

  10. 2019年春季学期《C语言程序设计II》课程总结

    2019年春季学期<C语言程序设计II>课程总结 1.课程情况 教学内容 课堂小结 作业安排 优秀作业 备注 1.开学谈心 2.测验数据类型.运算符与表达式的自学情况,并讲解测验题目3.第 ...

随机推荐

  1. Office 365 的安装方法

    一.在线安装 进入网址 https://www.office.com/ 使用office账号登陆 1.点击右上角安装office应用,选择第二项 其他安装选项 2.选择安装语言 点击高级,选择安装版本 ...

  2. @Value默认值填null

    @Value("${topology.position.spout.maxpending:#{null}}") private Integer spoutMaxPending; @ ...

  3. NIO详解

    目录 NIO 前言 IO与NIO的区别 Buffer(缓冲区) Channel(通道) Charset(字符集) NIO遍历文件 NIO 前言 NIO即New IO,这个库是在JDK1.4中才引入的. ...

  4. Qt HWND转QWidget

    HWND m_hWnd; QWidget *newWidget; newWidget = QWidget::find((WId)m_hWnd): //需要用(WID)

  5. openssl nodejs https+客户端证书+usbkey

    mac sslconfig 文件路径 /System/Library/OpenSSL/openssl.cnf 一生成CA openssl req -new -x509 -keyout ca.key - ...

  6. nevertheless|magnificent |prosperous|

    ADV 然而;不过You use nevertheless when saying something that contrasts with what has just been said. Mos ...

  7. win10安装navisworks失败,怎么强力卸载删除注册表并重新安装

    一些搞设计的朋友在win10系统下安装navisworks失败或提示已安装,也有时候想重新安装navisworks的时候会出现本电脑windows系统已安装navisworks,你要是不留意直接安装n ...

  8. hibernate主键(generator)生成方式

    1) assigned 主键由外部程序负责生成,无需Hibernate参与. 2) hilo 通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态. 3) seqhilo 与 ...

  9. 吴裕雄--天生自然HTML学习笔记:HTML 框架

    通过使用框架,你可以在同一个浏览器窗口中显示不止一个页面. iframe语法: <iframe src="URL"></iframe> 该URL指向不同的网 ...

  10. 关于Git的版本控制

    1.关于版本控制? 版本控制是一种记录文件或文件集随时间变化的系统,以便您以后可以调用特定版本,如果您是图形或Web设计人员并希望保留图像或布局的每个版本(您肯定希望这样),则使用版本控制系统(VCS ...