指针学习篇
指针,是一个用来存放地址的变量,其作用是用来解引用。
例如
int a = 0;
int* pa = &a; --> 表示将a的地址赋值给pa
则“pa” 现在存放的值就是a的地址
所以如果对pa进行解引用就会得到a的值 ---> *p = a;
计算机的编址:按32位计算机来说,就是一块内存中有32根地址线,那么每根地址线上都有两种电信号(正跟负),计算机按运行二进制的原因也是因此,所以32根地址线产生的结果就有2的32次方个地址,而每个地址表示一个字节,那么我们就有2^32 Byte(字节),也就是4GB的空间进行编址。
认识内置类型的各自占空间,地址占用的空间则都是一样的8位
char: 一个字节
short: 两个字节
int: 四个字节
double: 八个字节
指针也有对应的类型,指针的类型决定了其解引用操作的时候能访问的空间大小,如下面代码所编写的内容进行测试与检验此结果:
int main()
{
/*printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(double*));*/
int a = 0x11223344; //内存中为 44 33 22 11
int* pa = &a;
//*pa = 0; //此时a = 00 00 00 00
char* pc = &a;
*pc = 0; //此时只能将第一个字节设置为00 也就是a = 00 33 22 11
//指针类型决定了指针进行解引用操作的时候,能访问的空间大小
// int* p --> 能够访问4个字节
// char* p --> 能够访问1个字节
// double* p --> 能够访问8个字节
//指针类型决定了:指针走一步走多远
// int* p --> 4
// char* p --> 1
// double* p --> 8
//
printf("%p\n", pa);
printf("%p\n", pa + 1);
printf("%p\n", pc);
printf("%p\n", pc + 1);
return 0;
}
代码的解释如图所示:
1.指针的定义
int main()
{
int a = 10;
int* pa = &a; //将a的地址赋给初始化的指针pa
int* pa = NULL; //如果不知道指针的初始赋值的话,就给指针指向“0”的地址
//经过初始化不为空的pa就可以进行进一步的赋值操作
*pa = 20;
//而初始化为NULL的就不能进行赋值操作,因为地址指向“0”地址的话,不能确定里头没有值
*p = 10; //这是错误的赋值方式
//因此在赋值之前,要先判断指针是否指向NULL指针的操作,避免出bug
if(pa != NULL){
}
return 0;
}
注:定义指针的时候要注意不能定义“野指针”,即指针指向的位置是不可知的(随机的,不正确的或者没有明确限制的)
有以下几种情况:
//指针未初始化
int main()
{
int a; //局部变量不初始化,默认是随机值
int* p; //局部的指针变量,就初始化随机值
*p = 20;
printf("%d", a);
return 0;
}
//指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i < 0; i < 12; i++) //操作数组的空间,数组空间只有10个,而指针指到地12个
{
*(p++) = 1;
}
return 0;
}
//指针指向的空间释放
int* test()
{
int a = 10; //局部初始化,一出到函数就释放地址了
return &a; //放回地址后,系统释放地址
}
int main()
{
int *p = test(); //获得释放的地址块
*p = 20;
return 0;
}
2.指针的运算
指针 +- 整数:表示指针往后移多少个位置,根据类型所拥有的空间来移动
比如:int类型的话,加一就会往后移动四个字节,到下一个整型数组元素所在的空间进行读取。
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr; /int类型,所以加一次移动四个字节的空间指向到数组的下一个元素
for (i = 0; i < sz; i++)
{
printf("%d\n", *p);
p++;
}
return 0;
}
指针 - 指针:表示两个地址相减,例如,一个数组为arr[10]则其个数可以是arr[9]-arr[0]这一操作。
3.指针关系运算
指针也可以进行关系的运算,比如: 有一指针p,*(--p)表示的就是对指针p进行--后再解引用。
#define N_VALUES 5
int main()
{
float values[N_VALUES];
float* vp;
int i = 0;
for (vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
for (i = 0;i < N_VALUES;i++)
{
printf("%d", values[i]);
}
return 0;
}
C语言指针标准:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但不允许与指向第一个元素之前的那个内存位置的指针进行比较。
4.数组和指针
数组名:大部分情况下为首元素的地址,也就是 如果有指针p跟数组arr,p = arr 表示数组arr的首地址赋值给指针p,相当于p = &arr[0]。
但有两种情况下数组名不是指首元素的地址:
1.&arr --> 数组名:此时的数组名不是首个地址,是整个数组的地址
2. sizeof(arr) -->数组名:不是首元素地址,是整个数组,sizeof(数组名)计算的是整个数组的大小
//数组的关系如下面代码所示
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
printf("%d ==== %d\n", *(p + i), arr[i]);
}
return 0;
}
5.二级指针
二级指针,就是对指针在解引用的操作,假设有a 则一级指针int* pa = &a;int* ppa = &pa;
则在这段代码中,ppa就是二级指针,而**ppa = a,表示取对pa进行解引用。
6.指针数组与数组指针
指针数组,是一个数组,一个由指针组成的数组,数组的概念为多个类型相同的元素组合而成,而同个类型的指针组合而成的数组就叫做指针数组。
数组指针:是一个指针,一个数组的指针。
可以用一下的代码进行了解,指针数组。
int main()
{
int a = 10;
int b = 20;
int c = 30;
//int* pa = &a;
//int* pb = &b;
//int* pc = &c;
int arr[10];
int* arr2[3]={&a,&b,&c};
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d\n", *(arr2[i]));
}
return 0;
}
调试代码
调试代码是作为接触编程的新技能,一般开始进行敲代码时,会有很多的莫名其妙的错误导致程序运行不了,大部分错误编译器会进行报错的过程,这类问题属于有提示的解决方法。而有一些错误是编译器没能检查出来的,说明语法上是没有问题的,是逻辑上的问题。因此要进行代码的调试与代码的排错过程。
第一类:根据编译器报错定位到具体的一行代码,看懂报错的英语便可进行解决。实在看不懂就根据报错的编号可以到百度进行搜索。
这里推荐:CSDN --> 常见问题都有解决方法
overflow stack --> 世界级的代码社区,有更多解决的方法和资源可查询
github --> 代码仓库,也有问题的集合板块,但多数是作为代码的仓库
第二类问题:编译器没有进行报错就需要自己手动排查问题
1. 以隔离、消除等方式对错误进行定位
2.确定错误产生的原因,提出纠正错误的解决方法
3.对程序错误予以改正,重新测试