《计算机程式设计》Week5 课堂笔记
本笔记记录自 Coursera课程 《计算机程式设计》 台湾大学 刘邦锋老师
Week5 Pointer
5-1 Pointer Definition and Declaration
指针和一般变量的区别。一般变量的值就是代表数据,而指针变量的值则代表另一个变量的记忆体位址。一般变量有数据类别,比如说整数,浮点数等。指针变量也有数据类型,比如说指向整数的指针,指向浮点数的指针等。
申明指标变量的方法
int *iptr;
float *fptr;
double *dptr;
在变量名称前加一个*号代表这是一个指标变量。而*之前的数据类型就是这个指标变量所能指到的变量数据类型。
例子:(size.c)指针变量所占的位元组数
#include <stdio.h>
int main(void)
{
int *iptr;
float *fptr;
double *dptr;
printf("sizeof(iptr) = %d\n", sizeof(iptr));
printf("sizeof(fptr) = %d\n", sizeof(fptr));
printf("sizeof(dptr) = %d\n", sizeof(dptr));
return 0;
}
输出结果
sizeof(iptr) = 8
sizeof(fptr) = 8
sizeof(dptr) = 8
所以说,不管是指向4个位元组的整数,还是8个位元组的倍准浮点数,指针变量都是占8个位元组,因为他们都是存8个位元组(64 bit)的记忆体位址。
指定整数指标变量的值
int i;
int *iptr1;
int *iptr2;
iptr1 = &i;
iptr2 = iptr2;
- 当一个整数指针变量iptr的值是另一个整数变量i的记忆体位址时,称iptr指向i。
- 一个整数变量的记忆体位址可以指定给一个整数指针变量当作值,也可以将一个整数指针变量的值指定给另一个整数指针变量当作值。
使用指标变量所指到的变量
i = *iptr;
*iptr = i;
- 当一个指针变量前加上*时,就代表从这个记忆体位址取值(dereference)。
- *iptr可以出现在=的右边或左边。
NULL绝不指向任何有效的记忆体位址
#include <stdio.h>
...
ptr = NULL;
- 有时我们希望程序能够让一个指针变量不指向任何有效的记忆体位址,方法就是将它初始化成一个“特殊值”。
- 为此C语言定义了NULL作为这样的用途。一般这个值是定义成0,因为0这个记忆体位址通常是保留给系统使用,一般程序是不能使用的。
- NULL是在<stdio.h>标头档定义的,所以必须引入。
5-2 Pointer Usage
使用更加复杂一点的例子来讲解指针与普通变量的关系。可以使用指针以及指针的取值进行一些操作。
5-3 Pointer Reference and Dereference
使用更加复杂一点的例子来讲解指针与指针位址与普通变量位址的关系。
总之只要了解*是取值,&是取位址。
5-4 Pointer Parameter Passing
例子:(pointer-parameter.c)指针参数传递
#include <stdio.h>
void pointer_inc(int *p1, int *p2)
{
printf("The address of p1 is %p\n", &p1);
printf("The value of p1 is %p\n", p1);
printf("The address of p2 is %p\n", &p2);
printf("The value of p2 is %p\n", p2);
*p1 += 1;
p1 = p2;
*p1 += 2;
}
int main(void)
{
int i, j;
int *iptr = &i;
scanf("%d", &i);
scanf("%d", &j);
printf("The address of i is %p\n", &i);
printf("The address of j is %p\n", &j);
printf("The address of iptr is %p\n", &iptr);
printf("i = %d, j = %d\n", i, j);
pointer_inc(iptr, &j);
printf("i = %d, j = %d\n", i, j);
*iptr += 5;
printf("i = %d, j = %d\n", i, j);
return 0;
}
输入
10 20
输出
The address of i is 0x7fff1592a9c0
The address of j is 0x7fff1592a9c4
The address of iptr is 0x7fff1592a9c8
i = 10, j = 20
The address of p1 is 0x7fff1592a9a8
The value of p1 is 0x7fff1592a9c0
The address of p2 is 0x7fff1592a9a0
The value of p2 is 0x7fff1592a9c4
i = 11, j = 22
i = 16, j =22
所以总结一下:
- 实际参数与形式参数使用不同的记忆体,所以实际参数iptr和对应的形式参数p1位于不同的记忆体位址。
- 虽然p1已经改指向j,但是iptr还是指向i,因为p1和iptr是位于不同记忆体位址的不同变量,改变一个并不会改变另一个。
5-5 Pointer and Array
指针指向数组的起始记忆体位址
int array[100];
int *iptr;
...
iptr = array;
...
use *iptr as array[0]
...
- iptr = array;就是使iptr指向数组的起始记忆体位址。
- 因为数组的起始记忆体位址是array[0]的位址,所以*iptr就如同array[0]。有如用指针的语法从数组内取元素。
将指针加1
iptr++;
- C语言中有一套指针的算术语法,让指针可以加减一个整数,最常用的就是将指针加1。
- 这里的加1是指加一个元素的大小。所以如果是整数,指针变量就是加sizeof(int) = 4,而如果是倍准浮点数,指针变量就是加sizeof(double) = 8。可以想象成指向下一个元素。
例子:(inc3-with-pointer.c)利用指针将数组元素加3
#include <stdio.h>
int main(void)
{
int a[5];
int i;
int *ptr;
for (i = 0; i < 5; i++)
scanf("%d", &(a[i]));
for (i = 0, ptr = a; i < 5; i++, ptr++){
printf("%p\n", ptr);
*ptr += 3;
}
for (i = 0; i < 5; i++)
printf("a[%d] = %d\n", i, a[i]);
return 0;
}
输入
1 2 3 4 5
输出
0x7fff4b931d90
0x7fff4b931d94
0x7fff4b931d98
0x7fff4b931d9c
0x7fff4b931da0
a[0] = 4
a[1] = 5
a[2] = 6
a[3] = 7
a[4] = 8
5-6 Pointer and Relative Index
指针也可以使用数组的语法
*(iptr + i)
iptr[i]
- 指针变量加i就是指向下i个的意思。
- 指针变量iptr指向数组a的起始位置,再加i,再用星号取值,那*(iptr + i)就正是a[i]。
但是注意:数组的是绝对坐标,而指针变量的是相对坐标。
5-7 Pointer Arithmetic
例子:(arith.c)指针算术
#include <stdio.h>
int main(void)
{
int array[10];
int *iptr1 = &(array[3]);
int *iptr2 = iptr1 + 4;
printf("iptr1 = %p\n", iptr1);
printf("iptr2 = %p\n", iptr2);
printf("iptr2 - iptr1 = %d\n", iptr2 - iptr1);
return 0;
}
输出
iptr1 = 0x7fffa00d9f8c
iptr2 - 0x7fffa00d9f9c
iptr2 - iptr1 = 4
- 指针算术可以将一个指针加一个常数得到一个指针,自然也可以减去一个常数得到一个指针,而且两个指针也可以相减得到一个常数。
- 这里的常数都不是指一般以位元组为单位的记忆体位址,而是以指针所指到元素大小为单位。
5-8 Pointer as Return Value
例子:(first-positive.c)将iptr所指到的记忆体中的第一个正整数的记忆体位址返回
#include <stdio.h>
int *first_positive(int *ptr)
{
while (*ptr <= 0)
ptr++;
return ptr;
}
int main(void)
{
int i;
int array[10];
int *iptr;
for (i = 0; i < 10; i++)
scanf("%d", &(array[i]));
iptr = first_positive(array);
printf("*iptr = %d\n", *iptr);
printf("iptr -array = %d\n", iptr - array);
iptr = first_positive(array[5]);
printf("*iptr = %d\n", *iptr);
printf("iptr -array = %d\n", iptr - array);
return 0;
}
输入
0 0 0 5 9 0 0 6 0 2
输出
*iptr = 5
iptr - array = 3
*iptr = 6
iptr - array = 7
5-9 Caution in Using Pointer
指针的用途
- 在动态分配记忆体时,我们可以直接向作业系统要求一块记忆体使用,所以我们需要一个机制,让程序有办法能记住要来的记忆体位址,这个机制就是指针变量。
- 动态数据结构会将数据串联起来形成结构。此时数据结构的大小与形状都是依动态要求而调整,此时我们就需要指针来描述数据之间的连结关系。
- 在处理字串时,程序常常需要以记忆体位址来沟通。此时沟通的双方未必能够使用数组的标注语法,因为其中一方未必能够知道数组的起始记忆体位址。此时使用指针直接指到记忆体是最有效的沟通方法。
注意事项
- 除非是上述的动态配置记忆体,动态数据结构及字串处理,否则尽量避免使用指针。
- C语言中对指针的使用没有安全机制,初学很容易弄错,而且难以除错。
- 很多指针的使用是可以用数组语法代替的,这样不但容易月度,也容易除错。
- 有人认为使用指针可增加效率,但是现在的编译器已经能产生非常好的执行档。为了效率牺牲可读性也许并不值得。
测验代码
老师的
#include <stdio.h>
void shuffle ( int *deck[] )
{
int i = 0;
int buf[10000];
while ( deck[i] ) {
buf[i] = *deck[i];
i++;
}
int sec_s = i / 2 + i % 2;
int fir = 0, sec = sec_s, deck_p = 0;
while ( deck[deck_p] ) {
*deck[deck_p++] = buf[fir++];
if ( buf[sec] )
*deck[deck_p++] = buf[sec++];
}
}
void print ( int *deck[] )
{
int i = 0;
while ( deck[i] ) {
if ( i ) printf ( " " );
printf ( "%d", *deck[i++] );
}
}
int main()
{
int card[10000];
int *deck[10000];
int index = 0;
while (scanf("%d", &(card[index])) != EOF) {
deck[index] = &(card[index]);
index++;
}
deck[index] = NULL;
shuffle(deck);
print(deck);
return 0;
}
《计算机程式设计》Week5 课堂笔记的更多相关文章
- 【C语言】Coursera课程《计算机程式设计》台湾大学刘邦锋——Week6 String课堂笔记
Coursera课程 <计算机程式设计>台湾大学 刘邦锋 Week6 String 6-1 Character and ASCII 字符变量的声明 char c; C语言使用一个位元组来储 ...
- 《计算机程式设计》Week4 课堂笔记
本笔记记录自 Coursera课程 <计算机程式设计> 台湾大学 刘邦锋老师 Week4 Functions 4-1 System Function 函数主要分为两大类系统定义函数与使用者 ...
- 《计算机程式设计》Week3 课堂笔记
本笔记记录自 Coursera课程 <计算机程式设计> 台湾大学 刘邦锋老师 Week3 Array 3-1 Array Usage 例子:使用数组一次申明10个整数变量 int a[10 ...
- 《计算机程式设计》Week2 课堂笔记
本笔记记录自 Coursera课程 <计算机程式设计> 台湾大学 刘邦锋老师 Week2 Control Structure 2-1 If-then-else if then 判断 if ...
- 九章算法系列(#3 Binary Tree & Divide Conquer)-课堂笔记
前言 第一天的算法都还没有缓过来,直接就进入了第二天的算法学习.前一天一直在整理Binary Search的笔记,也没有提前预习一下,好在Binary Tree算是自己最熟的地方了吧(LeetCode ...
- 九章算法系列(#5 Linked List)-课堂笔记
前言 又是很长时间才回来发一篇博客,前一个月确实因为杂七杂八的事情影响了很多,现在还是到了大火燃眉毛的时候了,也应该开始继续整理一下算法的思路了.Linked List大家应该是特别熟悉不过的了,因为 ...
- 九章算法系列(#4 Dynamic Programming)-课堂笔记
前言 时隔这么久才发了这篇早在三周前就应该发出来的课堂笔记,由于懒癌犯了,加上各种原因,实在是应该反思.好多课堂上老师说的重要的东西可能细节上有一些急记不住了,但是幸好做了一些笔记,还能够让自己回想起 ...
- 九章算法系列(#2 Binary Search)-课堂笔记
前言 先说一些题外的东西吧.受到春跃大神的影响和启发,推荐了这个算法公开课给我,晚上睡觉前点开一看发现课还有两天要开始,本着要好好系统地学习一下算法,于是就爬起来拉上两个小伙伴组团报名了.今天听了第一 ...
- ocp11g培训内部教材_052课堂笔记(042)_体系架构
OCP 052 课堂笔记 目录 第一部分: Oracle体系架构... 4 第一章:实例与数据库... 4 1.Oracle 网络架构及应用环境... 4 2.Oracle 体系结构... 4 3. ...
随机推荐
- ukhj
SQL解析顺序: 七种Join图:
- C#基础知识之扩展方法
扩展方法需要满足的条件: 1.扩展方法必须定义在静态类里. 2.扩展方法必须是静态方法. 3.扩展方法的第一个参数以this修饰符为前缀. 4.扩展方法必须在使用它的类的扩展方法内,否则必须显示的us ...
- java 分页对象以及数据库分页查询
import java.util.List; public class Pager<T> { private Integer pageSize; private Integer total ...
- Set 的合集 并集 差集
合集 ,,,,,,]; ,,]; function union() { //先将数组去重 let s1 = new Set(arr1); let s2 = new Set(arr2); //[...s ...
- MyEclipse使用教程:使用Workbench和Perspectives
[MyEclipse CI 2019.4.0安装包下载] workbench指的是加载IDE时看到的内容,它通常包含一个perspective,这是相关视图和编辑器的布局.您可以根据正在进行开发的类型 ...
- 洛谷P3768 简单的数学题 莫比乌斯反演+杜教筛
题意简述 求出这个式子 \[ \sum_{i=1}^n\sum_{j=1}^n ij(i,j) \bmod p \] 做法 先用莫比乌斯反演拆一下式子 \[ \begin{split} \sum_{i ...
- 设计模式Design Pattern(3) -- 责任链模式
什么是责任链模式? 责任链模式(Chain of Responsibility Pattern):请求知道公开接口,但不知道那个具体类处理,这些具体处理类对象连接成一条链.请求沿着这条链传递,直到有对 ...
- Ldap 从入门到放弃(一)
OpenLDAP 2.4版本 快速入门 本文内容是自己通过官网文档.网络和相关书籍学习和理解并整理成文档,其中有错误或者疑问请在文章下方留言. 一.Introduction to OpenLDAP D ...
- 最全面的H5的背景音效素材(经过实践),分享给你!!!
个人内心独白: 这两天在为一个H5的页面寻找一些相关音效,茫茫的网络,辣么大,真是想法设法翻遍你,不说废话了,看总结吧哦 方法总结(这才是重点,看这里): 1.如果是部分铃声截取的,我们可以来到铃声之 ...
- CF D. Labyrinth 01BFS
由于上下走不限制,所以按照贪心,我们应该尽可能走上下方向. 我们可以开一个双端队列,并认为每次提取队首的时候得到的是到达该点的最优策略.(这个一定是唯一的,因为不可能向右走几格,然后再退回去. ) 那 ...