C语言程序设计笔记---013
C语言指针初阶
前言:
什么是指针?
指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。
由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”
即:能通过它能找到以它为地址的内存单元
/知识点汇总/
1、指针
a、指针就是变量,存放内存单元的地址(编号)
b、一个单元就是一个字节
1.1、指针的概念例程
/*例程1.1*/
#include <stdio.h>
int main()
{
int a = 10;//给变量a分配4个字节
int* pa = &a;//拿到的是a的4个字节中第一个字节的地址
//pa为int类型的指针变量
*pa = 20;//解引用
return 0;
}
注意:指针的大小在32位平台是4个字节,在64位平台是8个字节
1.2、指针的大小例程
/*例程1.2*/
#include <stdio.h>
int main()
{
int* pa;
char* pc;
float* pf;
printf("%d\n",sizeof(pa));//4
printf("%d\n",sizeof(pc));//4
printf("%d\n",sizeof(pf));//4
return 0;
}
2、指针的意义
(1)指针类型决定了,指针解引用的权限有多大
(2)指针类型决定了,指针的移动步长,能走多少个字节
2.1、指针的意义例程
/*例程2.1*/
#include <stdio.h>
//int main()
//{
// int a = 0x11223344;
// //int* pa = &a;
// //*pa = 0;
// //指针类型决定了:指针解引用的权限有多大
// char* pc = &a;
// *pc = 0;
// return 0;
//}
int main()
{
int arr[10] = { 0 };
char* p = arr;
int* pc = arr;
//数组名取地址均是元素的首地址
printf("%p\n",p);
printf("%p\n", p+1);//char+1
printf("%p\n", pc);
printf("%p\n", pc+1);//int+4
return 0;
}
2.2、指针的意义例程
/*例程2.2*/
#include <stdio.h>
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d",arr[i]);
}
printf("\n");
}
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr,sz);
int* p = arr;//以元素的形式操作
//char* pc = arr;//一个一个字节的操作
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 1;
}
print(arr,sz);
return 0;
}
小结:
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如:char类型指针解引用就只能访问一个字节,而int类型的指针的解引用就能访问四个字节*
3、野指针
野指针:就是指针指向的位置是不可而知的(随机、不确定、没有明确限制的)
野指针成因:
a、指针未初始化
b、指针越界访问
c、指针指向的空间释放,即置NULL
3.1、指针未初始化例程
/*例程3.1*/
#include <stdio.h>
int main()
{
int* p;//p为局部的指针变量,局部变量不初始化的话,默认为随机值
*p = 20;//对随机值解引用就是非法访问内存
//所以这里的p为野指针
return 0;
}
3.2、指针越界访问例程
/*例程3.2*/
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i <= 10; i++)//i<=10会导致p++移动地址范围超出
{
*p = i;
p++;
}
return 0;
}
3.3、指针指向的空间释放例程
/*例程3.3*/
#include <stdio.h>
int* test()
{
int a = 10;//a局部变量
return &a;//a局部变量的地址会改变,临时创建a也是临时存储的内存地址
}
int main()
{
int* p = test();//每次调用test()a的地址会改变
*p = 20;//解引用的地址不是以前的地址了,非法访问
return 0;
}
小结:
如何避免野指针的提高规范性?
a、变量最好都初始化,当不确定初始化的值时,可以初始化为NULL/0
如:int*p = NULL;
b、明确范围避免越界(C语言本身也并不会检测数组是否越界)
c、当指针指向的地址已经被释放那么,也需要及时的将指针变量变为空指针,避免非法访问
d、在使用指针之前检查有效性
3.4、使用指针之前检查有效性例程
/*例程3.4*/
#include <stdio.h>
int main()
{
int* p = NULL;
*p = 10;//报错,空指针的0地址不属于操作系统的
//判断一下指针有效性
if (p != NULL)//不为空指针那么就是有效地址
{
*p = 10;
}
return 0;
}
4、指针的+ - 指针关系运算
4.1、指针的+ - 整数例程
/*例程4.1*/
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int* pend = arr + 9;
while (p<=pend)
{
printf("%d ",*p);//1 2 3 4 5 6 7 8 9 10
p++;
}
printf("\n");
return 0;
}
4.2、指针的+ - 指针例程
/*例程4.2*/
#include <stdio.h>
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("%d\n",&arr[9] - &arr[0]);//9
//指针-指针得到的是两个指针之间的元素个数
return 0;
}
小结:
指针的关系运算
注意区分识别两种写法:
a、省略for判断条件的写法
for (vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
b、正常逻辑的写法
for (vp = &values[N_VALUES - 1]; vp >= &values[0]; vp--)
{
*vp = 0;
}
注意:虽然大部分编译器支持第二种写法,但应该避免这种地址超出首地址的写法,因为标准并不保证它可行
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
5、指针求字符串长度运算
5.1、计数器写法
/*例程5.1*/
#include <stdio.h>
//计数器写法
int my_strlen(char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
//strlen()---求字符串长度
int len = my_strlen("abc");
printf("%d\n",len);
return 0;
}
5.2、递归写法
/*例程5.2*/
#include <stdio.h>
//递归写法
int my_strlen(char* str)
{
char* start = str;//start存储数组首元素地址
while (*str != '\0')
{
str++;
}
return str-start;//用指针减指针获取数组元素的个数
}
int main()
{
//strlen()---求字符串长度
int len = my_strlen("abc");
printf("%d\n", len);
return 0;
}
6、指针与数组
6.1、指针与数组例程
理解:数组名是数组首元素的地址
/*例程6.1*/
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n",arr);//数组名是数组首元素的地址
printf("%p\n",&arr[0]);//与首元素地址相同
return 0;
}
6.2、指针与数组的遍历例程
/*例程6.2*/
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
//printf("%p <==> %p\n",&arr[i],p+i);
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d\n",*(p+i));
}
return 0;
}
6.3、指针与数组间地址的关系运算例程
/*例程6.3*/
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
printf("%d\n", arr[2]);//3
printf("%d\n", 2[arr]);//3
printf("%d\n", p[2]);//3--p[2]-->*(p+2)
//[]是一个操作符 2和arr是两个操作数
//推演
//arr[2] <==> *(arr+2) <==> *(p+2) <==> *(2+p) <==> *(2+arr) == 2[arr]
//2[arr] <==> *(2+arr)
return 0;
}
7、指针数组
用于存放指针的数组就是指针数组
7.1、指针数组例程
/*例程7.1*/
#include <stdio.h>
int main()
{
int arr[10];//整型数组 - 存放整形的数组就是整型数组
char ch[5];//字符数组 - 存放字符的数组就是字符数组
int* parr[5];//指针整型数组 - 存放指针的整型数组就是指针整型数组
char* pch[5];//指针字符型数组 - 存放指针的字符数组就是指针字符数组
return 0;
}
8、二级指针
概念:用来存放一级指针地址的指针 ,也就是指向指针的指针
8.1、二级指针例程
/*例程8.1*/
#include <stdio.h>
int mian()
{
int a = 10;
int* pa = &a;//pa是指针变量,一级指针,指向a--int类型的指针
int** ppa = &pa;//ppa就是一个二级指针变量,指向pa同时pa也是一个指针变量,取出pa所在内存的起始地址
int*** pppa = &ppa;//三级指针....依次类推
return 0;
}