c语言集训-刘佳雨-第五次作业
学习笔记
指针:
- 指针的重要性:
- 表示一些复杂的数据结构
- 快递的传递数据
- 使函数返回一个以上的值
- 能直接访问硬件
- 能够方便的处理字符串
- 是理解面对对象语言中引用的基础 总结:指针是c语言的灵魂
- 指针的定义:
#include <stdio.h>
int main()
{
int *p; //p是变量的名字int *表示p变量存放的是int类型变量的地址
int i = 3;
int j;
p = &i; //p保存了i的地址,因此p指向i
//p不是i,i也不是p,准确来说:修改p和i的值并不会影响两者的值
//如果一个指针变量指向了某个普通变量,则*指针变量就完全等同于普通变量
即:*p = i,在任何地方*p与i都可互相替换
//*p就是以p的内容为地址的变量
j = *p;//即j = i;
printf("i = %d, j = %d\n", i, j);
return 0;
}
- 地址:
- 内存单元的编号
- 从零开始的非负整数
- 范围:4G (0--4G-1)
- 指针:
- 指针就是地址,地址就是指针
- 指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量
- 指针和指针变量是两个不同的概念,虽然我们叙述时通常会将指针变量简称为指针,但实际两者含义并不相同
- 指针的本质就是一个操作受限的非负整数
- 指针的分类:
*
基本类型指针- 指针常见错误:
#include <stdio.h>
int main(void)
{
int i = 5;
int *p;
int *q;
p = &i;
p = q; //q是垃圾值,赋值给p,p也变成垃圾值
printf("%d\n", *p);/*q的空间属于本程序所以本程序可以读写q的内容,但是如果q内部是垃圾值,则本程序就不能读写*q的内容
因为此时*q所代表的内存单元的控制权限并没有分配给本程序,所以程序运行到11行就会出错*/
return 0;
}
运行结果:
互换两个数字:
#include <stdio.h>
void f(int *p, int *q)
{
int t; //如果要互换*p和*q的值,则t必须定义成int,不能定义成int *,否则语法将会出错
t = *p; //p是int *,*p是int类型
*p = *q;
*q = t;
}
int main(void)
{
int a = 3;
int b = 5;
f(&a, &b);
printf("a = %d, b = %d\n", a, b); //f(*p, *q)是错误的,f(a, b)也是错误的
return 0;
}
运行结果:
附注:
* 的含义:
1. 乘法
2. 定义指针变量:
int *p; //定义了一个名字叫p的变量,int 表示p只能存放int类型的变量
3. 指针运算符:
该运算符放在已经定义好的指针变量的前面,如果p是一个已经定义好的指针变量,则p表示以p的内容为地址的变量。
如何通过被调函数修改主函数普通变量的值:
1. 实参必须为该普通变量的地址
2. 形参必须为指针变量
3. 在被调函数中通过: *形参名 = ...... 的方式就可以修改主调函数相关变量的值
- 指针和数组
数组元素的地址
数组定义: int a[8]={1,2,3,4,5,6,7,8};
指针定义: int *p; p= &a[0];//p指向数组首元素
指针定义: int* p; p= a;//p仍指向数组首元素
指针定义: int * q; q= &a[2]; // q指向a[2]
希望表达p、q之间的联系
它们都指向同一数组中的元素
指针与整数加减运算
设p为指向整数数组中某元素的指针,i为整数,则p+i表示指针向后滑动i个整数,p -i表示指针向前滑动i个整数
- 指针和一维数组:
-
数组名: 一维数组名是个指针常量,它存放的是一维数组第一个元素的地址
-
下标和指针的关系: 如果p是个指针变量,则p[i]永远等价于*(p+i)
-
指针变量的运算: 指针变量不能相加,不能相乘,也不能相除; 如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量才可以相减
-
一个指针变量到底占几个字节:
sizeof(数据类型)
功能:返回值就是该数据类型所占的字节数 例如:sizeof(int) = 4 ,sizeof(char) = 1 , sizeof(double) = 8.sizeof(变量名)
功能:返回值是该变量所占的字节数
-
一个指针变量,无论它指向的是什么变量,占几个字节,该指针变量本身都只占四个字节 一个变量的地址使用该变量首字节的地址来表示
- 指针和二维数组
- 指针和函数
*
指针和结构体- 多级指针: int i = 10; int *p = &i; int **q = &p; int k = &q; 则k = i
专题:动态内存分配
1. 传统数组(静态数组)的缺点:
- 数组长度必须事先制定,且只能是常整数,不能是变量 例如:int a[5]; //ok int len = 5; int a[len]; //error
- 传统形式定义的数组,该数组的内存程序员无法手动释放 在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放
- 数组的长度一旦定义,就无法再被改变 数组的长度不能再函数运行的过程中动态的扩充或缩小
- A函数定义的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕后,A函数中的数组将无法再被其它函数使用 传统方式定义的数组不能跨函数使用 代码示例:
#include <stdio.h>
void g(int * pArr, int len)
{
pArr[2] = 88;
}
void f(void)
{
int a[5] = {1, 2, 3, 4, 5}; //20个字节的存储空间程序员无法手动编程释放它,它只能在本函数运行完毕时由系统自动释放
g(a, 5);
printf("%d\n", a[2]);
}
int main(void)
{
f();
return 0;
}
2. 为什么需要动态分配内存: 动态数组很好的解决了传统数组(静态数组)的这4个缺陷
- malloc函数的用法1:
#include <stdio.h>
#include <malloc.h> //malloc是memory(内存)allocate(分配)的缩写
int main(void)
{
int i = 5; //分配了4个字节(静态分配)
int*p = (int *)malloc(4);//动态分配
*p = 5; //*p代表的是一个int类型的变量,只不过*p这个整形变量的内存分配方式和int i = 5的分配方式不同
free(p); //free(P)表示把p所指向的内容释放掉,但p变量本身的内存是静态分配的,不能由程序员手动释放
printf("释放\n");
return 0;
}
/*1. 要使用malloc函数,必须添加malloc.h头文件
2. malloc函数只有一个形参,并且是整形形参
3. 4表示请求系统为本程序分配4个字节
4. malloc函数只能返回第一个字节的地址
5. int *p一行分配了8个字节,其中p变量占4个字节,p所指向的内存也占4个字节
6. p变量本身所占的内存是静态分配的,p所指向的内容是动态分配的
*/
- malloc函数的用法2:
#include <stdio.h>
#include <malloc.h>
void f(int *q)
{
*q = 100;
free(q); //把q所指向的内存释放掉, 本句必须注释掉,否则会导致第17行的代码出错
}
int main()
{
int *p = (int *)malloc(sizeof(int)); //sizeof(int)返回值是int所占的字节数
*p = 10;
printf("%d\n", *p);
f(p); //p是int *类型
printf("%d\n", *p);
return 0;
}
3. 动态分配内存举例_动态一维数组的构造:
#include <stdio.h>
#include <malloc.h>
int main(void)
{
int a[5]; //如果int占4个字节的话,则本数组总共包含有20个字节,每4个字节被当做了一个int变量来使用
int len;
int *pArr;
int i;
//动态的构造一维数组:
printf("请输入你要存放的元素的个数:");
scanf ("%d", &len);
pArr = (int *)malloc(4 * len); //本行动态的构造了一个一维数组,该一维数组的长度是len,数组名是pArr,每个元素的类型是int类型 类似于int pArr[len]
//对一维数组进行操作:(对动态一维数组进行赋值)
for (i = 0; i < len; ++i)
scanf("%d", &pArr[i]);
//对一维数组进行输出:
printf("一维数组的内容是:\n");
for (i = 0; i < len; ++i)
printf("%d\n", pArr[i]);
//free(pArr); //可通过该行手动的释放掉动态分配的数组
return 0;
}
运行结果:
4. 静态内存和动态内存的比较:
静态内存是由系统自动分配,自动释放
且静态内存是在栈分配的
而动态内存是由程序员手动分配,手动释放 动态内存是在堆分配的 5. 跨函数使用内存的问题: 静态内存无法跨函数使用
#include <stdio.h>
void f(int **q) //q是个指针变量,无论q是什么类型的指针变量,都只占4个字节
{
int i = 5;
*q = &i; //等价于p = &i;
}
int main(void)
{
int *p;
f(&p);
printf("%d\n", *p); //本句语法没有问题,但逻辑上有问题
return 0;
}
运行结果:
动态内存可以跨函数使用
#include <stdio.h>
#include <malloc.h>
void f(int **q)
{
int i = 5;
*q = (int *)malloc(sizeof(int)); //sizeof(数据类型)返回值是该数据类型所占的字节
**q = 5; //等价于p = (int *)malloc(sizeof(int))
}
int main(void)
{
int *p;
f(&p);
printf("%d\n", *p);
return 0;
}
运行结果:
代码实操
指针使函数返回一个以上的值:
#include <stdio.h>
void g(int *p, int *q)
{
*p = 1;
*q = 2;
}
int main(void)
{
int a = 3, b = 5;
g(&a, &b);
printf("%d %d\n", a, b);
return 0;
}
运行结果:
确定一个一维数组需要几个参数:
#include <stdio.h>
void f(int * pArr, int len)
{
pArr[3] = 88;
}
int main(void)
{
int a[6] = {1, 2, 3, 4, 5, 6};
printf("%d\n", a[3]); //第五行的pArr[3]与12、14行的a[3]是同一个变量
f(a, 6);
return 0;
}
运行结果:
#include <stdio.h>
void f(int *pArr, int len) //f函数可以输出任何一个一维数组的内容
{
int i;
for (i = 0; i < len; i++)
printf("%d ", *(pArr + i));
printf("\n");
}
int main(void)
{
int a[5] = {1, 2, 3, 4, 5};
int b[6] = {-1, -2, -3, -4, -5, -6};
int c[100] = {1, 99, 22, 33};
f(a, 5); //a是int *类型
f(b, 6);
f(c, 100);
return 0;
}
运行结果:
结论: 确定一个一维数组需要两个参数:
数组第一个元素的地址; 数组的长度
课后习题:
1. 输出结果如图:
2.
ref有4个元素
3.
ref的地址为0X61FE00
ref是数组名,指向数组的首元素,ref + 1表示指针向后滑动一个整数即指向第二个元素
++ref不能表示什么,因为ref是常量而不是变量
4.
a. *ptr的值是12 , *(ptr + 2)的值是16
b. *ptr的值是12 , *(ptr + 2)的值是14
5.
a. **ptr的值是12 , **(ptr + 1)的值是16
b. **ptr的值是12 , **(ptr + 1)的值是14
转载自:https://juejin.cn/post/7037493943709204510