初级指针
指针概念
指针是内存中一个最小单位元的编号,也就是地址,口语中说的指针通常是指指针变量,指针变量就是一个变量,是用来存放地址的变量,地址是唯一表示一块地址空间的
指针的大小在32位平台是4个字节,64位是8个字节
指针和指针类型
1.指针类型决定指针在被解引用是访问几个字节
如果是int*的指针,解引用访问4个字节
如果是char*的指针,解引用访问1个字节
以此推广
2.指针的类型绝决定了指针+1或-1操作的时候跳过几个字节,决定了指针的步长
例如: int* pc
pc --访问4个字节
pc + 1 --跳过四个字节
const修饰指针
const int* p和 int const *p 都是修饰 *p,使 *p是不能改变的定值
int* const p; 是使p指向的地址是不可以改变的
野指针
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
1.野指针的成因
(1). 指针未初始化(存贮的是随机值)
可以初始化为NULL
int main()
{
int* p; //这里没有对指针进行初始化,可以写成int* p = NULL;
*p = 10;
return 0;
}
(2).指针的越界访问
int mian()
{
int arr[10] = { 0 };
int* p = arr;
int i;
for (i = 0; i <= 10; i++) //指针指向的范围超出arr范围就会形成野指针
{
*p = i;
p++;
}
return 0;
}
代码中,当i取到10时是第十一个元素,但是p只有10个空间,就会形成越界
3).指针指向的空间被释放
int* test()
{
int a = 10;
return &a;
}
int mian()
{
int* p = test(); //如果下一步对p进行使用,就会形成野指针,因为test所创建的地址在函数被调用后就不属于当前程序了(内存还在)但是p还保存的是被清除的地址
return 0;
指针运算
1.指针加减整数
define NUM 5
int main()
{
int arr[NUM];
int* pa;
for (pa = &arr[0]; pa < &arr[NUM];)
*pa++ = 0; //将arr数组值全赋值为0
return 0;
}
2.指针减去指针(不是所有的指针都能相减)
指向同一块空间的指针才能相减
指针减去指针得到的绝对值是指针和指针之间元素的个数
define NUM 10
int main()
{
int arr[NUM];
printf("%d", &arr[9] - &arr[0]); //输出结果为9
return 0;
}
strlen手写
方法一
int my_strlen1(char* str) {
int num = 0;
for (; *str != '\0'; str++)
num++;
return num;
}
方法二
int my_strlen2(char* str) {
char* strat = str;
while (*str != '\0')
str++;
return str - strat;
}
指针相加没有意义
3.指针的运算关系
允许指向数组元素的指针与指向数组最后一个元素后面的Negev内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较.
正确写法:
#define N_VALUES 5
int main()
{
float values[N_VALUES];
float* va;
for (va = &values[N_VALUES]; va > &values[0];) {
*--va = 0;
}
for (int i = 0; i < 5; i++)
printf("%d ", values[i]);
return 0;
}
错误写法:
define N_VALUES 5
int main()
{
float values[N_VALUES];
float* va;
for (va = &values[N_VALUES - 1]; va >= &values[0];va--) { //这种写法是错误的
*va = 0;
}
return 0;
}
指针和数组
数组可以通过指针来访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
printf("%d ", *(p + i));
return 0;
}
arr[i] = *(arr + i) = *(p + i)
二级指针
int a = 10; int
int* pa = &a; int * (*说明pa是指针,int说明所指的地址是int类型)
int** ppa = &pa; int * * ( *说明ppa是指针,int *说明所指的地址是int *类型)
指针数组
存放指针的数组就是指针数组
数组传递的是首地址,但是两个例外
1.sizeof(数组名),这里的数组名表示整个数组
2.&数组名,这里的数组名表示的依然是整个数组
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", arr + 1); //跳过四个字节
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0] + 1); //跳过四个字节
printf("%p\n", &arr);
printf("%p\n", &arr + 1); //跳过整个数组
return 0;
}
以下图片是上述代码输出的地址![]()
int main()
{
int a = 10;
int b = 20;
int c = 30;
int arr[10];
int* pa = &a;
int* pb = &b;
int* pc = &c;
int* parr[10] = {&a,&b,&c};
for (int i = 0; i < 3; i++) {
printf("%d ", *parr[i]);
}
return 0;
}
使用指针数组模拟二维数组
//正常二维数组
int main()
{
int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
//模拟实现
int main(){
int arr[4] = { 1,2,3,4 };
int brr[4] = { 2,3,4,5 };
int crr[4] = { 3,4,5,6 };
int* p[3] = { arr,brr,crr };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", p[i][j]); //*(*(p + i) + j),对(p + i)解引用分别找到arr,brr,crr首地址
}
printf("\n");
}
return 0;
}