C语言指针初等知识
一.指针的基本常识
指针是个变量,存放内存单元的地址
int main()
{
int a = 10;//在内存中开辟一块空间
int* p = &a;//&a为取出a的地址
//将a的地址存在指针变量p中
return 0;
}
指针的大小仅与cpu位数有关,与指针所存放的变量大小无关。
32根地址线产生的地址(=指针变量大小)就是 32个比特位,4字节 ;有2的32次方种
00000000 00000000 00000000 00000000
…
11111111 11111111 11111111 11111111
1地址对应1字节,每字节的区域都有其对应的地址.
知识点补充:
计算机中的单位
bit——比特位 (1或0占一个比特位)
byte——字节(一个字节 = 八个比特位)
kb——1024字节
mb——1024kb
gb——1024mb
tb——1024gb
pb——1024tb
32位电脑,其地址有2 ^ 32种
假设每个地址对应一个bit(比特位)
已知1024 = 2 ^ 10
(232)bit÷8÷(210)÷(210)÷(210)= 0.5gb
若1地址对应1bit,则电脑只有0.5G,不合适
所以说1地址对应1字节,为4G空间,合适
由此可以看出1地址对应1字节
二.指针类型的意义(重点)
指针类型
为何区分指针变量的类型?
如 char* int* 同为指针变量的数据类型(指针类型),都占相同的字节,表达的地址也相同(都为变量的地址),那么区分类型的意义是什么?
1.(意义)解引用时能够访问的字节数
指针类型决定了指针进行解引用操作时,能够访问空间的大小
int main()
{
int a = 0x11223344;//通过查看内存,内容是11 22 33 44(这里不考虑大小端字节序的问题)
int* pa = &a;
*pa = 0;//(这里*pa认为它指向int)经过此操作,a变为00 00 00 00
a = 0x11223344
char* pc = &a;//通过查看内存,内容是11 22 33 44
*pc = 0;//(这里*pc认为它指向char)经过此操作,a变为11 22 33 00
return 0;
}
指针类型决定指针能够访问空间的大小
int*p ;p能够访问4个字节
charp ;p能够访问1个字节
doublep ;*p能够访问8个字节
2.(意义)指针±整数
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);// 000000CFFF55F894
printf("%p\n", pa + 1);// 000000CFFF55F898 指针跨度为4字节 (一指针对应一字节)
printf("%p\n", pc);// 000000CFFF55F894
printf("%p\n", pc + 1);// 000000CFFF55F895 指针跨度为1字节
}
如上所示,指针类型不同,其向前或者向后走的"字节跨度不同 ".
下面是指针±整数的具体应用
int main()
{
int arr[10] = { 0 };
int* p = &arr;//若int*,解引用时每4字节赋值一个1
char* pc = &arr;//若char*,解引用时每一字节赋值一个1,导致不想要的结果
int i = 0;
for (i = 0; i < 10; i++)
{
*(pc + i) = 1;
printf("%d", arr[0]);
}
return 0;
}
三.野指针
概念:野指针就是指向的位置不可知的(随机的、不可确定都、没有明确限制的)
1.野指针的原因】
①指针未初始化
int main()
{
int* p;//局部变量未初始化,里面默认放一个随机值
*p = 30;
return 0;
}
②指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i;
for (i = 0; i <= 15; i++)
{
*p++ =i;//p越出其管理的arr范围,就是野指针
//上面相当于*p=i;p++(虽然++优先度比较高,但是后置++在赋值表达式算完以后才自增)
}
return 0;
}
③指针指向的空间释放
int* f()
{
int a = 10;
int a[10]={0};
return &a;
}
int main()
{
int* p = f();//a变量出来其所在的函数就被删除,地址会还给系统
printf("%d", *p);//栈上的空间虽然回收了,但是该地址空间上的10值还在。目前还没有其他操作或者语句用了那块地址。所以还是10,但是在实际情况下不可这样
return 0;
}
2.如何避免野指针?
1.指针初始化
2.小心指针越界
3.指针指向空间释放后,将指针赋为NULL
4.指针使用之前检查有效性
四.NULL 空指针
可以把指针变量初始化为空指针NULL来避免野指针出现
空指针可以确保不指向任何对象或函数;而未初始化的指针则可能指向任何地方。
int main()
{
int a = 0;
int* pa = &a;//初始化指针
int* p = NULL;//将暂时用不到的指针赋为空指针
return 0;
}
若是不清楚该初始化为何值,可赋空指针NULL
同理,若之前的地址无效之后,为避免其之后成为野指针,建议及时把它赋为NULL
注意,空指针的类型是void* ,是指针变量,所以赋空指针时不要解引用
编译器中,#define NULL ((void *)0),地址是0x00000000,不能被访问
五.数组名
数组名通常情况下为首元素地址,除了以下两种情况:
1.sizeof(数组名):这是计算整个数组的大小
2.&arr:这时候取的是整个数组的地址;虽然和首元素地址看起来相同,但是地址跨度是整个数组
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]); //可以看出首元素地址 = 数组名
return 0;
}
小练习:通过地址访问数组元素
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%p %p\n", arr + i, &arr[i]);
}
for (i = 0; i < 10; i++)
{
printf("%-2d %-2d\n", *(arr + i), arr[i]);
}
return 0;
}
六.二级指针
指针变量也是变量,创建时会向内存开辟空间,所以也地址;指针变量的地址就是【二级指针】(也有三、四、五极等)
int main()
{
int a = 10;
int* pa = &a;//一级指针
int** ppa = &pa;//二级指针
int*** pppa = &ppa;//三级指针
//. .....
return 0;
}
下面是二级指针的解引用:
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;
**ppa = 20;//这里 **ppa = *(*ppa) = *(pa) = a
printf("%d %d", a, **ppa);
return 0;
}
七.指针数组
指针数组 - 数组 ,存放指针的数组
例如 int arr[10]={1,2,3}:整形数组 ; char arr[10]={‘a’,‘b’,‘c’}:字符数组 ;int* arr[10]={&a,&b,&c}:指针数组
区分: 数组指针 - 指针 ,数组的指针
int main()
{
int a = 10, b = 20, c = 30;
int* arr[3] = { &a,&b,&c };
int i = 0;
for (i = 0; i < 3; i++)
{
printf("arr[%d]=%p , *arr[%d]=%d\n", i + 1, arr[i], i + 1, *arr[i]);
}
printf("%d", **(arr+1));
return 0;
}
八.指针的相关题目与运算
1.利用指针寻找数组元素
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[1]);
int* p = &arr;
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", * (p + i));
}
return 0;
}
数组元素的指针在比较时,允许指针与数组最后一个元素后面的那个位置比较,
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较.
(编译器上跑到过去,但是不满足c语言的语法)
2.利用指针把数组赋为相同的元素
int main()
{
char arr[10] = {0};
char* p = arr;
int i = 1;
int sz = sizeof(arr) / sizeof(arr[0]);
while( p <= &arr[sz-1])//这里意在说明指针也可以比较或者运算
{
*p= '*';
printf("%d%c ",i, *p);
p++;
i++;
}
return 0;
}
3.数组内两元素指针相减
数组内两元素指针相减可得到两元素间【相差的元素个数】而非相差的字节数
int main()
{
int arr[10] = { 0 };
printf("%d", &arr[9] - &arr[0]);//10
return 0;
}
4.模拟实现strlen()函数
int my_strlen1(char* arr)//普通方法
{
int n = 0;
while (*arr++)
{
n++;
}
return n;
}
int my_strlen2(char* arr)//递归
{
return (*arr ? my_strlen2(arr + 1) + 1 : 0);
}
int my_strlen3(char* arr)//指针相减
{
char* p = arr;
while (1)
{
if (!(*p))//当p指向'\0'
{
return (p - arr);
}
p++;
}
}
int main()
{
char arr[] = "qwertyuiop";
printf("%d %d %d %d\n", strlen(arr), my_strlen1(arr), my_strlen2(arr), my_strlen3(arr));
return 0;
}