第 2 天
文章目录
- 第 2 天
- 指针强化
- 防止头文件重复包含
- 为什么 %s 能够直接将字符串打印出来,而不能直接将数组全都打印出来?
- 通过指针间接赋值
- 一个变量,应该定义一个怎样类型的指针来保存它的地址?
- 主调函数,被调函数
- 字符串
free():意味着指针变为了野指针,告知 OS 该段内存区域可用。
指针强化
指针也是一种数据类型,即所指向的内存空间的数据类型;
没有变量,就没有内存;
不允许向NULL、未知非法地址拷贝内存;
在指针声明时,* 表示所声明的变量为指针;
在指针使用时,* 表示操作指针所指向的内存空间中的值;
void *p = NULL; // 合法,系统知道分配多少内存,所有指针分配大小固定
// 用的时候需要进行指向;
a[5] = {1,2,3,4,5};
p = a;
*((int*)p + 1) 相当于 int a[1], (int*) 强制类型转换,将void转换为int
int main()
{
int b[3] = {1, 2, 3};
int c[3];
memcpy(c, b, sizeof(b)); // 拷贝内存区域
for (int i = 0; i < 3; i++)
printf("%d ", c[i]); // 1 2 3
char str[] = "abcde";
char dst[100];
memcpy(dst, str, sizeof(str)); // 拷贝内存区域
printf("dst = %s\n", dst); // dst = abcde
char *q = NULL;
char str2[100] = {0};
q = str2; // 为指针赋予空间,赋予意义
strcpy(q, "1234"); // 拷贝字符串,不需要字符串结束符
printf("q = %s\n", q); // q = 1234
}
防止头文件重复包含
将代码封装起来后,可以用头文件提供对外接口。
// 多文件时,防止头文件重复包含
为什么 %s 能够直接将字符串打印出来,而不能直接将数组全都打印出来?
因为字符串有结束符。
char *buf = "asdas";
buf[2] = "2"; // 错误,文字常量区,内存不可修改
char *buf[] = "dads"; // ok, 栈区
buf[2] = "3";
通过指针间接赋值
如果想通过形参改变实参的内存内容,必须通过地址传递
可以达成的目的: 一个函数可以修改(返回)多个值,即对形参进行地址传递
三个条件:
1. 两个变量
2. 建立关系
3. 通过*操作内存
int a = 100;
int *p = NULL;
p = &a;
*p = 22;
printf("%d", *p);
// 如果想通过形参改变实参的内存内容,必须通过地址传递
// 可以达成的目的: 一个函数可以修改(返回)多个值
一个变量,应该定义一个怎样类型的指针来保存它的地址?
在原来基础上加一个*
void fun1(int *p)
{
p = 0xaabb; // 传入的是值
}
void fun2(int **p) // 为了保存 *p 的地址,使用 **p 来保存
{
*p = 0x11ff; // 因为传入的是地址参数,使用 *地址 来改变值;
}
void main()
{
int *p=0x1122;
fun1(p); // 值传递 0x1122
fun2(&p); // 地址传递 0x11ff
}
一、指针强化
1. 指针也是一种数据类型,指针变量也是一种变量,和int a本质是一样的;
1) 指针变量也是一种变量,也有空间,64 位程序大小为 8 个字节
int *p = 0x1122;
2) *操作符,*相当于钥匙,通过*可以找到指针所指向的内存区域
int a = 10;
int *p = NULL;
p = &a; // 指针指向谁,就把谁的地址赋值给指针
*p = 22; // *放=左边,给内存赋值,写内存
int b = *p; // *放=右边,取内存的值,读内存
3) 指针变量,和指向的内存是两个不同的概念
char *p = NULL;
char buf[] = "abcdefg";
// 改变指针变量的值
p = buf;
p = p + 1;
*p = 'm'; // 改变指针指向的内存,并不会影响到指针的值
4) 写内存时,一定要确保内存可写
char *buf = "asddaads"; // 文字常量区,内存不可改
buf[2] = '1'; // err
2. 间接赋值是指针存在的最大意义
1) 间接赋值三大条件
a) 两个变量
b) 建立关系
c) 通过 * 操作符进行间接赋值
1)
int a;
int *p; // 两个变量
p = &a; // 建立关系
*p = 100; // 通过 * 操作进行间接赋值
2)
int b;
fun(&b); // 两个变量之一:实参,给函数传参时,相当于建立关系
void fun(int *p)
{
*p = 100; // 间接赋值
}
2. 如何定义合适类型的指针变量
// 某个变量的地址需要定义一个怎样类型的变量保存
// 在这个类型的基础上加一个*
int b;
int *q = &b;
int **t = &q;
重要:如果想通过函数形参改变实参的值,必须传地址
1. 值传递,形参的任何修改不会影响到实参
2. 地址传递,形参(通过*操作符号)的任何修改会影响到实参
int a = 10;
fun(a); // 值传递
void fun(int b)
{
b = 20;
}
fun2(&a); // 地址传递
void fun2(int *p)
{
*p = 20;
}
int *p = 0x1122;
void fun3(p); // 值传递
void fun3(int *p)
{
p = 0x2233;
}
void fun4(&p); // 地址传递
void fun4(int **p) // **p 保存了 *p 的地址
{
*p = 0xaabb;
}
主调函数,被调函数
- 主调函数可以把堆区、栈区、全局数据内存地址传给被调用函数。
- 被调用函数只能返回堆区、全局数据。
char *p = NULL;
int len = 0;
fun3(&p, &len);
printf("p=%s, len=%d\n", p, len);
void fun3(char **p, int *len)
{
char *tmp = (char*)malloc(100);
if(tmp == NULL)
return;
strcpy(tmp, "adsd");
// 间接赋值
*p = tmp;
*len = str(tmp);
}
字符串
strlen 测字符串长度,不包含数字0,字符’\0’
sizeof 测数组长度,包含数字0,字符’\0’
char buf[] = {'a', 'b', 'c'}; // 确定数组元素个数3个
printf("%s", buf); // 乱码,没有'\0'
char buf[100] = {'a', 'b', 'c'};
printf("%s", buf); // 没有乱码,指定元素个数后自动补零
char buf[50] = {'1', 'a', 'b', '0', '7'};
char buf[50] = {'1', 'a', 'b', 0, '7'}; // 截断
char buf[50] = {'1', 'a', 'b', '\0', '7'};// 截断
// 常用的字符串初始化
char buf[] = "dasdadadsafd";
char buf[] = "abcd";
// strlen 测字符串长度,不包含数字0,字符'\0'
// sizeof 测数组长度,包含数字0,字符'\0'
printf("strlen = %d, sizeof = %d\n", strlen(buf), sizeof(buf)); // 4 5(包含'\0')
char buf[100] = "abcd";
printf("strlen = %d, sizeof = %d\n", strlen(buf), sizeof(buf)); // 4 100