C博客作业05--指针

| 这个作业属于哪个班级 | C语言--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | C博客作业05--指针 |
| 这个作业的目标 | 学习指针相关内容 |
| 姓名 | 胡旻轩 |

目录

  • 0.展示PTA总分
  • 1.本章学习总结
  • 1.1 指针定义、指针相关运算、指针做函数参数
  • 1.1.1指针变量的定义
  • 1.1.2指针变量的应用
  • 1.1.3通过指针变量取得数据
  • 1.1.4对星号*的总结
  • 1.1.5指针变量的初始化
  • 1.2字符指针
  • 1.3字符串相关函数及函数代码原型的理解
  • 1.4动态内存分配
  • 1.5二级指针
  • 2.PTA实验作业
  • 2.1 (指针做函数返回值) 查找指定字符
  • 2.1.1 代码截图
  • 2.2 合并两个有序数组
  • 2.2.1 代码截图
  • 2.3 说反话-加强版
  • 2.3.1 代码截图

0.展示PTA总分

1.本章学习总结

1.1指针定义、指针相关运算、指针做函数参数

1.1.1指针变量的定义

数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。

在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量。

现在假设有一个 char 类型的变量 c,它存储了字符 'K'(ASCII码为十进制数 75),并占用了地址为 0X11A 的内存(地址通常用十六进制表示)。另外有一个指针变量 p,它的值为 0X11A,正好等于变量 c 的地址,这种情况我们就称 p 指向了 c,或者说 p 是指向变量 c 的指针。

1.1.2指针变量的应用

定义指针变量与定义普通变量非常类似,不过要在变量名前面加星号*,格式为:

datatype *name;

或者:

datatype *name = value;

*表示这是一个指针变量,datatype表示该指针变量所指向的数据的类型 。例如:

int *p1;

p1 是一个指向 int 类型数据的指针变量,至于 p1 究竟指向哪一份数据,应该由赋予它的值决定。再如:

int a = 100;
int *p_a = &a;

在定义指针变量 p_a 的同时对它进行初始化,并将变量 a 的地址赋予它,此时 p_a 就指向了 a。值得注意的是,p_a 需要的一个地址,a 前面必须要加取地址符&,否则是不对的。
和普通变量一样,指针变量也可以被多次写入,只要你想,随时都能够改变指针变量的值。

是一个特殊符号,表明一个变量是指针变量,定义 p1、p2 时必须带。而给 p1、p2 赋值时,因为已经知道了它是一个指针变量,就没必要多此一举再带上,后边可以像使用普通变量一样来使用指针变量。也就是说,定义指针变量时必须带,给指针变量赋值时不能带*。

假设变量 a、b、c、d 的地址分别为 0X1000、0X1004、0X2000、0X2004,下面的示意图很好地反映了 p1、p2 指向的变化

需要强调的是,p1、p2 的类型分别是float和char,而不是float和char,它们是完全不同的数据类型。

指针变量也可以连续定义,例如:

int *a, *b, *c;  //a、b、c 的类型都是 int*

注意每个变量前面都要带*。如果写成下面的形式,那么只有 a 是指针变量,b、c 都是类型为 int 的普通变量:

int *a, b, c;

1.1.3通过指针变量取得数据

指针变量存储了数据的地址,通过指针变量能够获得该地址上的数据,格式为:

*pointer;

这里的*称为指针运算符,用来取得某个地址上的数据。

CPU 读写数据必须要知道数据在内存中的地址,普通变量和指针变量都是地址的助记符,虽然通过 *p 和 a 获取到的数据一样,但它们的运行过程稍有不同:a 只需要一次运算就能够取得数据,而 *p 要经过两次运算,多了一层“间接”。

假设变量 a、p 的地址分别为 0X1000、0XF0A0,它们的指向关系如下图所示:

程序被编译和链接后,a、p 被替换成相应的地址。使用 *p 的话,要先通过地址 0XF0A0 取得变量 p 本身的值,这个值是变量 a 的地址,然后再通过这个值取得变量 a 的数据,前后共有两次运算;而使用 a 的话,可以通过地址 0X1000 直接取得它的数据,只需要一步运算。

也就是说,使用指针是间接获取数据,使用变量名是直接获取数据,前者比后者的代价要高。

指针除了可以获取内存上的数据,也可以修改内存上的数据,例如:

#include <stdio.h>
int main()
{
    int a = 15, b = 99, c = 222;
    int *p = &a;  //定义指针变量
    *p = b;  //通过指针变量修改内存上的数据
    c = *p;  //通过指针变量获取内存上的数据
    printf("%d, %d, %d, %d\n", a, b, c, *p);
    return 0;
}

运行结果:
99, 99, 99, 99

*p 代表的是 a 中的数据,它等价于 a,可以将另外的一份数据赋值给它,也可以将它赋值给另外的一个变量。

在不同的场景下有不同的作用:可以用在指针变量的定义中,表明这是一个指针变量,以和普通变量区分开;使用指针变量时在前面加*表示获取指针指向的数据,或者说表示的是指针指向的数据本身。

1.1.4对星号*的总结

在我们目前所学到的语法中,星号*主要有三种用途:

  • 表示乘法,例如int a = 3, b = 5, c; c = a * b;,这是最容易理解的。

  • 表示定义一个指针变量,以和普通变量区分开,例如int a = 100; int *p = &a;

  • 表示获取指针指向的数据,是一种间接操作,例如int a, b, *p = &a; *p = 100; b = *p;

1.1.5指针变量的初始化

1)
指针变量先定义,赋值必须是地址:

int a;
int *p1;
p1=&a;

2)
在定义指针变量时,可以同时对它赋初值:

int a;
int *p1=&a; //*p1=&a是错误的!
int *p2=p1;

3)
不能用数值作为指针变量的初值,但可以将一个指针变量初始化为一个空指针

int p=10000是错误的!
p=0;
p=NULL;
p=(int)1732; (int*)是将数字强转为指针类型

1.2 字符指针

包括指针如何指向字符串、字符串相关函数及函数代码原型的理解、字符串相关函数用法(扩展课堂未介绍内容)
字符数组和字符指针的重要区别

如果要改变数组所代表的字符串,只能改变数组元素的内容
如果要改变指针所代表的字符串,通常直接改变指针的值,让他直接指向新的字符串

char sa[]="this is a string";
const charsp="this is a string";
charsp="this is a string";错误❌
sp="hello";错误❌
sa="hello";错误❌

数组名是常量,不能对它赋值
sp是指针,只能指向,不能直接赋值

**关于const **
const是constant的缩写,意思是恒定不变的!
const定义的是变量,但又相当于常量
不允许给它重新赋值,即使是相同的值也不可以
只读变量,必须在定义的时候就给他赋初值

const char*sp="this is a string";错误❌
sp='a';
char sa[] ="this is a string";正确✔
charsp=sa;

字符串的输出

char sa[]="array";
const char*sp="point";
printf("%s",sa); array 数组
printf("%s",sp); point 指针
printf("%s","string"); string字符串
printf("%s",sa+2); ray 对数组进行操作
printf("%s",sp+3); int对指针进行操作
printf("%s","string"+1); tring 对字符串进行操作

即 数组名sa,指针sp和字符串string 的值都是地址

printf("",地址);
//%s:输出从指定地址开始,‘\0’结束的字符串

字符指针,先赋值,后引用
定义字符指针后,没有对他赋值,指针的值不确定,

chars;
scanf("%s",s);错误❌ 不要引用未赋值的指针
chars ,str[20];正确 ✔
s=str;
scanf("%s",s);
定义指针是,现将他的初值置为空
char*s=NULL;

字符数组和指针字符串的区别是内存中的存储区域不一样,字符数组存储在全局数据区或栈区,指针字符串存储在常量区。

全局区和栈区的字符串(包括其它数据)有读取和写入的权限,而常量区的字符串只有读取权限,没有写入权限。

  内存权限不同导致一个明显的结果就是,字符数组在定义后可以读取和修改每个字符,而对于指针字符串来说,一旦被定义

后就只能读取而不能修改,任何对它的赋值都是错误的。

除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如:

char *str = "http://c.biancheng.net";

或者:

char *str;
str = "http://c.biancheng.net";

字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第 0 个字符;我们通常将第 0 个字符的地址称为字符串的首地址。字符串中每个字符的类型都是char,所以 str 的类型也必须是char *。

在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量。C语言有两种表示字符串的方法,一种是字符数组,另一种是字符串常量,它们在内存中的存储位置不同,使得字符数组可以读取和修改,而字符串常量只能读取不能修改。

1.3字符串相关函数及函数代码原型的理解

1、求长度

函数:strlen()

格式:strlen(字符串/字符串变量)
函数原型:

//求字符串长度函数strlen
#include <stdio.h>
int str_strlen(char *Str)
{
 int l=0;
 while(*Str!='\0')
 {
  *Str++;
  l++;
 }
 return l;
}

注意:计算字符串的有效长度,不包括结束标志'\0';

2、复制

函数:strcpy()

strcpy(字符串1(目标),字符串2(源))\将字符串2的内容复制给字符串1

注意:字符数组1的长度要能容纳字符串1+2的内容,

函数原型:

拷贝函数strcpy
#include <stdio.h>
char *str_strcpy(char *strDest,char *strSour)
{

 while(*strSour!='\0')
 {
  *strDest=*strSour;
  *strDest++;
  *strSour++;
 }
 *strDest='\0';//直到最后一个给予结束标志符
 return strDest;
}

注意:使用strDest,strSrc这样增强可读性的名字
对于传入参数的strDest,strSrc
进行检查,禁止空指针传入
使用const 来约束strSrc,提高程序的健壮性,如果函数体内的语
句试图改动strSrc的内容。编译器将指出错误

3、比较

函数:strcmp()

格式:strcmp(字符串1,字符串2)

若字符串1与字符串2相等返回--0 字符串1大于2 返回 1 小于返回 -1
注意:strcmp函数实际上是对字符的ASCII码进行比较 , 其中str1和str2可以是字符串常量或者字符串变量,返回值为整形,所以区分大小写

函数原型:

//字符串比较函数strcmp,。
#include <stdio.h>
int str_strcmp(char *str1,char *str2)
{
 while((*str1==*str2)&&(*str1!='\0'))
 {
  *str1++;
  *str2++;
 }
 if(*str1=='\0'&&*str2=='\0')//结束位置相同,说明长度一样长,否则不一样长
  return 1;
 else
  return -1;
}

4、合并

函数:strcat()

格式:strcat(字符串1,字符串2)--将字符串2的内容合并到字符串1

注意:字符数组1的长度要能容纳1+2的内容,且 str1=str1+str2是非法的!

函数原型:

char *str_strcat(char *strDest,const char *strSour)
{
 while(*strDest!='\0')//先将指针指向结尾的位置
  strDest++;//移动到字符串末尾
 while(*strSour!='\0')
 {
  *strDest=*strSour;//从字符串2的起始位置开始赋值到字符串1的结尾位置,再分别移动
  strDest++;
  strSour++;
 }
 *strDest='\0';//给予结束标志
 return strDest;
}

char*strcat(char*str1,const char*str2)
{
char *tempstr;
tempstr=str1;
if(!str1||!str2)//防止空指针传入,否则程序崩溃
{
return NULL;
}
while(*str1)
{
str1++;
}
while(*str2)
{
*str1=*str2;
str1++,str2++;
}
*str1='\0';
return tempstr;//连接后字符串的首地址
//函数类型为指针,则返回地址,首地址不要改变

注意:将字符串str2连接在str1后,str1最后的结束字符NULL会被覆盖掉,并且连接后的字符串的尾部
会再增加一个NULL.注意:str1和str2所指的内存空间不能重叠,且str1要有足够的空间来容纳要复制
的字符串。返回是str1字符串的首地址。

1.4 动态内存分配

堆区申请的空间,想要多少就申请多少
数组要指定数组长度,空间浪费
栈区空间有限
一般情况下,运行中的很多存储要求在写程序时无法确定,故需要动态内存分配
使用动态内存分配能有效使用内存
全局变量,静态局部变量,自动变量由编译器系统分配

堆区和栈区区别

一、区别

注:首先堆和栈可以分为两种,一种是数据结构,另一种是和内存的分配有关,这两种虽然都有栈和堆,但是两者关系并不大,

1、栈、堆是数据结构里面的叫法,注意:有时候有人喜欢这样说 "堆栈" 其实说的就是栈而不是堆。  

2、堆区、栈区则是内存模型的叫法。

二、内存中的栈区和堆区

我们知道底层是C
而C语言的内存模型分为5个区:栈区、堆区、静态区、常量区、代码区。每个区存储的内容如下:

1、栈区:存放函数的参数值、局部变量等,由编译器自动分配和释放,通常在函数执行完后就释放了,
其操作方式类似于数据结构中的栈。栈内存分配运算内置于CPU的指令集,效率很高,但是分配的内存量
有限,局部变量申请大小是有限制的,由栈的剩余空间决定,栈的大小为2m,也有的说是1m,总之,是一个
编译时就会确定的常数,如果申请的空间超过了剩余空间,将会出现问题。

2、堆区:就是通过new、malloc、realloc分配的内存块,编译器不会负责它们的释放工作,需要用程序
区释放。分配方式类似于数据结构中的链表。“内存泄漏”通常说的就是堆区。

3、静态区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始
化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后,由系统释放。

4、常量区:常量存储在这里,不允许修改。eg:const

5、代码区:顾名思义,存放代码。

1.5二级指针

A(即B的地址)是指向指针的指针,称为二级指针,用于存放二级指针的变量称为二级指针变量.根据B的不同情况,二级指针又分为指向指针变量的指针和指向数组的指针。

首先任何值都有地址 ,一级指针的值虽然是地址,但这个地址做为一个值亦需要空间来存放,是空间就具有地址 ,这就是存放地址这一值的空间所具有的地址,二级指针就是为了获取这个地址。
一级指针所关联的是其值(一个地址)名下空间里的数据,这个数据可以是任意类型并做任意用途,但二级指针所关联的数据只有一个类型一个用途,就是地址,指针就是两个用途提供目标的读取或改写, 那么二级指针就是为了提供对于内存地址的读取或改写。
指针的表现形式是地址,核心是指向关系指针,运算符“”的作用是按照指向关系访问所指向的对象.如果存在A指向B的指向关系,则A是B的地址,“A”表示通过这个指向关系间接访问B.如果B的值也是一个指针,它指向C,则B是C的地址,“*B”表示间接访问C,如果C是整型、实型或者结构体等类型的变量或者是存放这些类型的数据的数组元素,则B(即C的地址)是普通的指针,称为一级指针,用于存放一级指针的变量称为一级指针变量,指向一级指针变量的"A"则是“二级指针”。

指向指针变量的指针

在如上的A指向B、B指向C的指向关系中,如果A、B、C都是变量,即C是普通变量,B是一级指针变量,其中存放着C的地址,A是二级指针变量,其中存放着B的地址,则这3个变量分别在内存中占据各自的存储单元,它们之间的相互关系下图所示,相互之间的前后位置关系并不重要.此时,B是一级指针变量,B的值(即C的地址)是一级指针数据;A是二级指针变量,A的值(即B的地址)是二级指针数据.

指向数组的指针

在C语言中,数组与其它变量在使用上有很大的不同.无论是字符型、整型、实型变量,还是结构体类型或者指针类型的变量,语句中出现变量名都代表对该变量所在内存单元的访问,变量名代表整个变量在内存中的存储单元,可以向该变量赋值,也可以从中取出数据使用.但是定义一个数组之后,数组名并不代表整个数组所占据的内存单元,而是代表数组首元素的地址

2.PTA实验作业

2.1(指针做函数返回值) 查找指定字符

2.1.1代码截图

2.2 合并两个有序数组

2.2.1代码截图

2.3说反话-加强版

2.3.1代码截图

posted @ 2020-12-27 20:13  云中霓裳  阅读(277)  评论(0编辑  收藏  举报