??众所周知,指针的用法最常见的无外乎两种,一种是用指针来指向变量的内存地址,通过操控指针进而可以间接的操控变量。另外一种是把指针当成变量来使用,像变量一样可以存储数据。数组也是类似的道理,因为数组实际上也是变量。本文仅讨论第二种情况,即用指针来代替变量和数组进行数据的存储。
??用指针来存储数据主要有三种方法,即:
- 定义指针之后直接使用
- 定义指针之后,分配内存再使用
预科知识
??我们知道,c语言在定义变量的同时,会为变量分配一个对应类型的内存空间,方便后续用该内存空间来存储数据。如果没有初始化的话,这个内存空间里面的值是随机的,不确定的。如果加了static 关键字,并且没有初始化的话,这个值就是0。
int a;
static int b;
int c = 3;
??而在定义指针变量的时候,也会为指针变量分配一个对应类型的内存,里面存储的数据的情况也和上面类似,但是不同的是,指针存储的是内存首地址,而变量存储的是基本初等类型。
??定义了一个指针变量,如果没有初始化的话,它指向一个随机的未知的空间,也称为野指针。所以为了系统的内存安全性,一般在定义的时候将指针初始化为NULL ,表示该指针不指向任何空间。
??因为指针存储的是地址,所以指针占的空间和语言无关,和系统有关。32位系统的话,占4个字节,16位系统的话,占2个字节。
int *a;
int *b = NULL;
int c;
int *d = &c;
一、定义指针之后直接使用
??既然未初始化的指针指向的地址是随机的,那么就可以用这个地址来存储数据。
1.当成变量使用
??我们这里以结构体为例。我们可以看到,未初始化的指针也可以像变量一样用来存储数据。只不过受限于编译器的缘故,有的电脑可能能正常输出结果,而有的会异常终止。
??这种用野指针的行为,我不提倡大家使用!!
struct Student{
int age;
int number;
};
struct Student stu;
struct Student *stuu;
int main()
{
stu.age = 12;
stu.number = 12345;
stuu->age = 13;
stuu->number = 11111;
printf("%d\n",stu.age);
printf("%d\n",stu.number);
printf("%d\n",stuu->age);
printf("%d\n",stuu->number);
return 0;
}
2.当成数组使用
??当成数组使用的时候也和上面类似,有的电脑可能能正常输出结果,而有的会异常终止。
??这种用野指针的行为,我不提倡大家使用!!
int main()
{
int *a;
a[0] = 11;
a[1] = 22;
a[2] = 33;
a[3] = 44;
a[4] = 55;
int b[5] = {1, 2, 3, 4, 5};
for(int i = 0;i < 5; i++)
{
printf("%d ",a[i]);
printf("%d ",b[i]);
}
return 0;
}
二、定义指针之后,分配内存再使用
??我们定义了未初始化的指针之后,可以用malloc函数,为这个指针分配空间。即,指针就会指向该空间的首地址。如果这个空间大小刚好是n个int类型的大小的话,这个指针就相当于一个大小为n的int数组,可以用来存储数据。
??这种方式,就不是野指针的用法了,提倡大家用这种方法。但是所存储的数据大小最好不要超过分配的空间的大小,不然就和野指针一样,可能会发生内存安全问题,导致异常终止。
1、当成变量使用
??和上面的代码类似,只不过多了一步,就是为指针分配了空间。
struct Student{
int age;
int number;
};
struct Student stu;
struct Student *stuu = (struct Student *)malloc(sizeof(struct Student));
int main()
{
stu.age = 12;
stu.number = 12345;
stuu->age = 13;
stuu->number = 11111;
printf("%d\n",stu.age);
printf("%d\n",stu.number);
printf("%d\n",stuu->age);
printf("%d\n",stuu->number);
return 0;
}
??可以看到能正常输出,不会异常终止程序。
2.当成数组使用
??和上面的代码类似,只不过多了一步,就是为指针分配了空间。
int main()
{
int *a = (int *)malloc(sizeof(int) * 5);
a[0] = 11;
a[1] = 22;
a[2] = 33;
a[3] = 44;
a[4] = 55;
int b[5] = {1, 2, 3, 4, 5};
for(int i = 0;i < 5; i++)
{
printf("%d ",a[i]);
}
printf("\n");
for(int i = 0;i < 5; i++)
{
printf("%d ",b[i]);
}
return 0;
}
??看一下运行结果。
三、总结
??1、指针可以作为数组用,数组号表示指针基地址偏移。
??2、第一次看到这种用法的时候,我觉得很诧异(居然可以这样子。。。。)。但是后来想想,这种用法也合情合理,毕竟数组名本身也是数组的首地址,数组是有类型的,数组元素的类型都是相同的,这样数组下标可以根据类型来决定偏移多少空间,找到对应下标元素的地址,从而找到对应的元素。回过头来看指针当成数组使用,指针也是有类型的,int* 、 char*、 unsigned char* 、struct st* 等等。这样的话,把指针当成数组使用,利用下标来寻找偏移地址,也可以根据类型知道每次偏移多少空间。
??3、如果只定义指针,没有初始化系统是不会给它分配内存的,初始化后,只是指向某个内存而已,没有初始化的指针是指向一个未知的内存地址,这样的指针是很危险的。一般我们定义一个指针变量就会给它初始化为空,以免造成的危险无法补救。分配内存之后它再也不指向a变量的内存地址,而是指向一个新的内存地址。
|