指针
什么是指针?
- 指针就是地址,那么地址的本质是什么?地址就是数据,那么数据可不可以保存在变量空间里呢?当然可以
- 有没有指针变量的概念?
- 保存指针数据的变量就叫做指针变量。指针是指向一块数据,指针变量是一个变量里面保存至指针地址。
- 概念上叫指针,但是我们经常使用的是指针变量。
int main()
{
int a = 10;
int* p = &a;
// 指针就是地址
// 指针变量本质是变量,然后里面保存的地址(指针)值
// 指针变量:空间(左值)+内容(右值:地址)
p = (int*)0x1234;// p变量的空间:左值
int* q = p;// p变量的内容:右值,就是刚刚的0x1234,指针 == 指针变量
return 0;
}
- 为什么要存在指针?
- 为了cpu寻址的效率
- 取地址时,取出来的永远是地址最小的那个
- 一个整型,4个字节,那么应该有4个地址!那么&a取了哪一个地址?那么如何全部访问这4个字节。
- 取出来的永远是地址最小的那个
- 根据类型连续访问4个字节
指针的解引用
- 在同类型情况下,对指针解引用代表指针所指向的目标。
- 在我们 *p 时访问的是右值
- 通过指针变量访问是一种间接访问.
double * p = NULL;// NULL -> 0
*p = 10.0;
// *p 拿到的是 NULL相当于 0
// 会出错,写入错误,访问的右值
数组
- 数组是具有相同类型的集合。
- 数组是线性连续且递增的!
- 在开辟空间的角度,不应该把数组认为一个个独立的元素,应该整体开辟空间,整体释放。
- 在main函数中定义,那么同样在栈上开辟空间。
- 我们发现&arr[0] < &arr[1] < &arr[2] ... ... &arr[9].
- 对指针 +1,其实是指向其类型的大小。
int main()
{
char* c = NULL;
int* i = NULL;
short* s = NULL;
double* d = NULL;
printf("%d \n", c + 1);// 1
printf("%d \n", i + 1);// 4
printf("%d \n", s + 1);// 2
printf("%d \n", d + 1);// 8
return 0;
}
- 首元素的地址,数组地址为什么取出来的值一样?
- 因为首元素的地址和数组的地址,在地址对应的字节是重叠的!所以,地址数据值相等。
- 取出的地址是众多地址最低位。
int main()
{
char arr[5] = { 0 };
printf("%p \n", &arr[0]);// 数组首元素地址
printf("%p \n", &arr[0] +1);// +1
printf("%p \n", &arr);// 数组的地址
printf("%p \n", &arr +1);// + 整个数组的大小
return 0;
}
左右值
- 数组名在做右值的时候,是首元素的地址。
- 一个能够充当左值的,一定要有对应的空间。
- 数组只能整体初始化,不能整体赋值,不能作为左值。
- 数组能【】号索引的方式一个一个存放。
- 总结:
- 指针和数组没关系。
int main()
{
char arr[5] = { 0 };
char* p = arr;// 数组首元素地址
return 0;
}
- c语言没有字符串类型,有字符串。
int main()
{
// 不可修改 操作系统保护,真正意义上不能被修改
const char* str = "hello bit";
// 在栈上开辟空间,可被访问修改
char arr[] = "hello world";
return 0;
}
- 数组传参,发生降维,降维成指针
- 为什么要将维?如果不降维就要发生数组拷贝,函数调用效率低,降维成指针
- 所有数组,传参都会降维成指针,降维成指向其内部元素类型的指针
- 在c中,任何函数调用,只要有形参实例化,必定形成临时拷贝!!!
void show(int* arrp, const int num)
{
for (int i = 0; i < num; i++)
{
printf("%d \n", *arrp);
arrp += 1;
}
}
int main()
{
int arr[] = { 1,2,3 };
int num = sizeof(arr) / sizeof(arr[0]);
show(arr, num);
return 0;
}
// -----------------------------------------------------------
int main()
{
int a[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
printf("%d %d \n", *(a + 1), *(ptr - 1));// 2 5
// *(ptr - 1) 这里不会改变 *ptr ,这是表达式,不是这样的赋值 *ptr = *ptr -1
return 0;
}
指针数组,数组指针
- [] > * 优先级 int p[10]; 指针数组,数组里面存储的是 int 类型的指针
- int (*p)[10]; 整型数组指针,指针可以指向任何合法的类型变量。
- 数组内部可以放置任何类型(内置,指针,结构体,联合体,数组)的内容!
- 数组的降维,如果传递一个数组,或者多维数组,给一个函数,函数的形参会进行降维成指向成内部元素的指针,可以使用正常的数组访问到,不必使用指针方式。
// 这里的 arr[] 会降维 int*指针
void test(int arr[])
{
for (int i = 0; i < 5; i++)
{
printf("%d \n", arr[i]);
}
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
test(arr);
// 两个地址相同
return 0;
}
地址的强制转化
- 强制类型转化,改变的是对特定内容的看待方式,在c中,就是只改变其类型。
- 强制转化的价值。
- 不让编译器报警
- 给程序员看
二维数组
- 所有维度的数组都是线性,连续,递增
- 遍历数组
int main()
{
int arr[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
// 两边的类型要相同
int* p = (int *)arr;
for (int i = 0; i < 3 * 4; i++)
{
printf("%d \n", (*p+i));
}
return 0;
}
- 指针相减代表两个指针之间所经历的元素个数。
函数指针
-
函数也是有地址的,为什么?。
- 函数是代码的一部分,程序运行的时候,也要加载进内存,以供cpu后续寻址访问,函数也有地址
-
函数只关心起始代码在哪里开始
-
函数的地址是数据,就能通过变量去保存
-
void(*p)();
- 首先确定是否是指针(*p)
- 然后看返回类型void
- 参数空()
void test()
{
printf("hello");
}
int main()
{
printf("%p \n", test);
// 等价的
printf("%p \n", &test);
// (*p)指针 ()参数
void (*p)() = test;// 函数指针
return 0;
}