我就算只会循环只会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 = &num;

这里就是个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野指针成因

  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; 
}
  1. 指针指向的空间释放
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 我也不学指针,艾玛,讲的真好的主要内容,如果未能解决你的问题,请参考以下文章

第21课《为什么不学三大框架》

漫画 | 打死我也不学编译了!

我的第一个网页制作:Hello World!

学妹说要和我去看《Hello World》我教学妹用各种不同编程语言输出“Hello World”

学妹说要和我去看《Hello World》我教学妹用各种不同编程语言输出“Hello World”

✨ Python入门 ✨ 1️⃣ 环境配置 ❤️ Hello World