我就算只会循环只会hello world 我也不学指针,艾玛,讲的真好
Posted 你帅你先说.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我就算只会循环只会hello world 我也不学指针,艾玛,讲的真好相关的知识,希望对你有一定的参考价值。
●🧑个人主页:你帅你先说.
●📃欢迎点赞👍关注💡收藏💖
●📖既选择了远方,便只顾风雨兼程。
●🤟欢迎大家有问题随时私信我!
●🧐版权:本文由[你帅你先说.]原创,CSDN首发,侵权必究。
文章目录
1.指针是什么?
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
2.指针
指针是个变量,是用来存放内存单元的地址。
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//将a的地址存放在p变量中,p就是一个之指针变量。
return 0;
}
总结:指针就是变量
,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
那这里的问题是:
一个小的单元到底是多大?
如何编址?
在内存中每一个小单元的大小是1个字节
在计算机中:
32位-32根地址线
地址线-1/0 要么是0要么是1
那一共多少种可能?
00000000000000000000000000000000
00000000000000000000000000000001
…
111111111111111111111111111111111111
一共 2 32 2^{32} 232种可能
转换成10进制就是4294967296
除1024算出来是4,194,304Kb
除1024算出来是4096Mb
再除1024算来是4GB
这里我们就明白:
1.在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
2.那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
3.指针类型
我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
准确的说:有的
int* num = 10;
p = #
这里就是个int型的指针,除了这些,变量有的类型指针都有。
char* pc = NULL;
int* pi = NULL;
short* ps = NULL;
long* pl = NULL;
float* pf = NULL;
double*pd = NULL;
我们知道,指针是用来存放地址的,在32位平台下地址是4个字节,那为什么还要创建出那么多指针类型呢?
int a = 0x11223344;
int*pa = &a;
*pa = 0;
刚开始a的内存是这样的
当执行到*pa = 0
后
我们把这段代码改一改
int a = 0x11223344;
char* pc = &a;
*pc = 0;
刚开始依旧是这样的
当执行到*pc = 0
后
这次与上次不同 只有1个字节的大小发生了改变
所以我们就知道指针类型
决定了指针解引用操作的时候,一次访问几个字节。char*指针解引用访问1个字节,int*指针解引用访问4个字节。
通过观察,可以发现不管存在int类型还是char类型,a的地址都是一样的,但当+2时跳过的字节就不一样了,int类型的跳过了8个字节,而char类型的只跳过了两个类型。
所以指针类型
还决定了指针+ -整数的时候的步长(指针±整数的时候,跳过几个字节)。int* 指针 +1 跳过4个字节,char* 指针+1 跳过1个字节。
3.1const修饰的指针
int n = 100;
const int *p=&n
*p = 20;
p = &n;
const 放在*的左边
此时const修饰的是指针指向的内容(*p)
,不能通过指针来改变,但是指针变量(p)
本身可以改变(const int *和int const *两种写法是等价的)
const放在*的右边
const修饰的是指针变量(p)
本身,指针指向的内容(*p)
可以修改,但是指针变量(p)
不能修改。
3.2指针定义的常量字符串
char arr[]="abcdef";
char* arr="abcdef";
两种写法是有区别的
第一种写法,数组里的内容是可以更改的
第二种写法是由指针定义的常量字符串,字符串是存在常量区的,不可更改
对于指针定义的字符串我们还需注意
char* arr = "abcdef";
printf("%s",arr);
printf("%s",*arr);//err
此时第一种写法才是正确的。原因是字符指针保存的是字符串的首字符地址,字符串的处理上比较特殊,是连续从首地址处进行处理,直到遇到\\0字符串结尾标志,你如果对指针进行解引用获取到的是首字符,而不是字符串,所以进行字符串打印就会出错
4.野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
4.1野指针成因
- 指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2.指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
- 指针指向的空间释放
int* test()
{
int a = 10;
return &a;//函数只要调用就会申请空间,出了这个函数申请的空间就会被释放
}
int main()
{
int* p = test();
printf("%d\\n", *p);//此时空间被释放,这块空间已经不属于你了,此时再去访问就是非法访问
return 0;
}
4.2如何规避野指针
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即使置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
int main()
{
int a = 10;
int* p = &a;//明确地初始化,确定指向
int* p2 = NULL;//不知道一个指针当前应该指向哪里时,可以初始化为NULL
//*p2 = 100;//err 空指针不能解引用
//这样写代码就可以避免访问到空指针了
if (p2 != NULL)
{
*p2 = 100;
}
return 0;
}
5.指针运算
5.1指针+ -整数
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES]; )
{
*vp++ = 0; //将数组中的内容都改成0
}
5.2指针-指针
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
char ch[5] = { 0 };
printf("%d\\n", &arr[9] - &arr[0]);
指针-指针 得到的数字的绝对值是指针和指针之间元素的个数
printf("%d\\n", &arr[9] - &ch[0]);//err这种写法就是错误的
指针-指针的前提是两个指针指向同一块区域
//
图解
学完这些我们就可以用指针写一个my_strlen
函数了
int my_strlen(char* s)
{
int count = 0;
while (*s != '\\0')
{
count++;
s++;
}
return count;
}
也可以利用刚刚所学的指针-指针
int my_strlen(char* s)
{
char* start = s;
while (*s != '\\0')
{
s++;
}
return s-start;
}
5.3指针的关系运算
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
这段代码和刚刚上面那段代码类似,只是这次是从后往前
我们可以对这段代码进行简化
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
图解
这段代码最终会造成这种情况,实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
C语言标准规定
允许指向数组元素的指针与
指向数组最后一个元素后面
的那个内存位置的指针比较,但是不允许与指向第一个元素之前
的那个内存位置的指针进行比较。
6.指针和数组
数组名是什么?我们看一个例子:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\\n", arr);
printf("%p\\n", &arr[0]);
return 0;
}
你会发现这两个打印的内容是一样的。
所以数组名是首元素的地址
,但有两个例外
1. sizeof(数组名) - 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节
2. &数组名 - 这里的数组名不是首元素的地址,是表示整个数组的,拿到的是整个数组的地址
7.二级指针
指针变量也是变量,是变量就有地址,二级指针
就是用来存放一级指针的地址。
那要怎么理解int** ppa呢?
我们可以把int** ppa
拆成int* * pa
,第二个 *告诉我们pa是指针变量,int* 告诉我们pa所指向a的类型是int*
。
再举个例子
int a = 10;
int* pa = &a;
int** ppa = &pa;
int*** pppa = &ppa;
同样地,我们可以把int*** pppa
拆成int** * pppa
,第三个*告诉我们pppa是个指针变量,int**告诉我们pppa所指向的ppa的类型是int**
。
我们知道,*pa
是对a进行解引用可以找到a的内容。同理,*ppa
可以找到pa的内容,那么**pa
就可以找到a的内容,通过这些,对于指针的理解又进一步提升了。
虽然指针有二级、三级、四级、五级等,但实际应用中经常使用的
还是二级指针
8.指针数组
指针数组是指针还是数组?
答案:是数组。是存放指针
的数组。
int arr[10];//整型数组---存放整型的数组
char ch[5];//字符数组---存放字符的数组
//指针数组---存放指针的数组
//int* 整型指针数组
//char* 字符指针数组
int* pa[5];//整型指针数组
char* pc[5];//字符指针数组
通过之前的学习,我们可以知道存放指针的数组
相当于是存放地址的数组
。
看完这些
所以你也应该意识到
离开之前
这样的文章你还不赶紧 点赞收藏+关注作者!
以上是关于我就算只会循环只会hello world 我也不学指针,艾玛,讲的真好的主要内容,如果未能解决你的问题,请参考以下文章
学妹说要和我去看《Hello World》我教学妹用各种不同编程语言输出“Hello World”