C-指针与数组
- 指针要点
- 本质是一个变量, 需要占用一定内存空间, 他被用来保存内存的地址值
- 占用4字节内存空间(32位机), ->即指针占用的字节数, 应能够访问计算机的所有内存地址
- 指针是有类型的, 之所以有类型是为了让CPU在根据指针访问内存时知道该如何取用内存(取多大)
- 在指针声明的地方,表示所声明的变量为指针
- 在指针使用时, 表示取指针所指向内存的值
- 在根据指针取值时, 会依据指针的类型来取值
- 指针存在的一个重大意义就是我们可以更加灵活的访问内存, 就像硬件编程中,寄存器的定义
- 向函数传递指针参数, 可以节省时间, 并且防止栈溢出 (比如大的结构体)
代码演示:
//你可以随便利用指针来修改内存, 例如:
*((int*)(0X22ffc4)) = 10; //只要是可访问的内存, 当然可以
//我们可以利用指针, 来进行传址调用(在函数中,改变实参的值),最典型的当然是swap
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
数组要点
- 数组元素的个数可以显示或者隐士指定。 -> int arr[] = {1, 2};
- 当数组作为局部变量(在栈上)声明时。 -> int arr[5]; 与 int arr[5] = {0}; 是不同的, 第一个数组中的值为任意, 第二个全部初始化为0.
- 数组地址与数组名
- 数组名代表数组首元素的地址
- 数组的地址需要使用&才能得到 -> &a
- 数组首元素的地址值与数组的地址值相同, 但是他们两个是不同的概念!
- 数组名
- 数组名可以看做一个常量指针
- 数组名指向的是内存中数组首元素的起始位置
- 在表达式中数组名只能作为右值来使用 -> int a[5]; intb[5]; b = a; !!!!错
- 在下列场合中,数组名不能看做常量指针
- 数组名作为sizeof操作符的参数 ; 即 sizeof(arr)->这是整个数组的大小
- 数组名作为&运算符的参数; &a-> 数组的地址
- a 与 &a的区别
- a为数组是数组首元素的地址, &a为整个数组的地址
- 指针运算可以看出:
- a + 1 : (unsigned int)a + sizeof(*a);
- &a + 1:(unsigned int)&a + sizeof(*&a);
指针运算
指针与整数之间的运算规则为:
- p + n -> (unsigned int)p + n*sizeof(*p) // 即, 当指针指向同类型数组元素时, +1 与 -1 分别对应指向下一个与上一个元素
指针之间只支持减法运算,并且参与运算的指针类型必须相同, 规则为
- p1 -p2 -> ((unsigned int)p1 - (unsigned int)p2) / sizeof(type)
- 对于数组来说, 数组元素的指针相减, 即为元素角标之间的差
指针的比较
- 指针之间可以进行关系运算, < <= …
- 指针关系运算的前提是指向同一个数组中的元素
- 任意两个指针之间的比较运算(== != )无限制
- 指针与数组
- 要防止定义为指针, 声明为数组 这种错误的出现:
#include <stdio.h>
char* p = "hello world!"; // file1中, 定义为指针
extern char p[]; //file2中, 声明为数组
int main()
{
//file2 中使用
printf("%s\n", p); //你在这里又把它当做指针使用了 , 肯定是错误
return 0;
}
- 这个例子可以很明显的看出,编译器在处理指针与数组时的区别: 编译器处理指针会寻址,而数组则不会寻址
- 编译器在处理指针时,首先找到p的内存,取出内存中的地址,然后去访问这个地址
- 而在处理数组时, 以上面例子, 编译器直接找到p的内存,访问其代表的内存空间!!! 没寻址
- 那么怎么样才能在file2, 即使声明成了数组,也一样访问那个字符串呢?
- 解决办法, 编译器没有去寻址, 我们帮他去寻址!!!!! -> printf("%s\n", (char*)(*((unsigned int*)p)));
- 即可以看出, 数组名和指针有很大的区别!!!,
- Motorala面试题:
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int* p1 = (int*)(&a + 1);
int* p2 = (int*)((int)a + 1);
int* p3 = (int*)(a + 1);
printf("%d, %d, %d \n", p1[-1], p2[0], p3[1]); // 结果: 5, 33554432, 3
return 0;
}
- 如果上面的都看懂了, 这题也就不再话下了
- 从这题也可以看出, 数组与指针易混淆的地方就是, 指针可以像数组一样使用, 而数组的访问有时也可以像指针一样
- 记住这个 a[i] -> *(a + i)
- 数组参数的退化
- C语言中, 数组作为函数参数时, 编译器会将其编译为指针
- void f(int a[]) -> void f(int* a) ; void f(a[500]) -> void f(int* a);
- 即会丢失长度信息,所以一般我们想在函数中处理数组时, 需要传递数组的长度信息 -> void f(int a[], int len);
- 上面的说法何以体现呢?看下面这个例子
- C语言中, 数组作为函数参数时, 编译器会将其编译为指针
void f(int a[1000])
{
printf("%d \n", sizeof(a));
}
int main()
{
int a[1000] = {0};
f(a[1000]); //32位机上是 4
printf("%d \n", sizeof(a)); //4000
return 0;
}