C语言(初谈指针)

前言:

       很多人在学习C语言的时候可能多多少少了解过C语言的难度,让很多人不愿意接触C语言。而C语言的难度其实主要来源于他语法中的指针

       其实我在这里要说的是,指针其实并不难,造成让人们以为他很难的原因大多是可以是理解上的复杂化和人们在口头上把它恐怖化了!!!

今天我就来简单的举几个例子让大家可以简单的理解一下 

1.指针是什么: 

1.指针其实就是内存中类型单元的地址

2.而我们平常说的指针,可以理解为存储变量地址的指针变量。

上面说的可能不太好理解,我们画一个图来看看

int main()
{
	int a = 10;
	int* p = &a;
	return 0;
}

注: & 是取地址操作符,把a的地址取出来,交给p变量

这里的p就是指针变量(我们所说的指针就是p)int* 代表 p指针指向的类型是int

是不是非常的简单,其实这就是指针,他就是一个拿着别人家地址的邮递员

当然了我们int char double都是有大小区分的,那么我们的指针大小有区别吗?

int main()
{
	int a = 10;
	int* p = &a;
	printf("%d\n", sizeof(p));
	return 0;
}

我上面同样的程序执行了两次为什么会得出不同的答案呢?

这里我先说一下为什么:  因为在这里我是在32位操作系统和64位操作系统两个不同的操作系统下执行的结果

大家可能都知道我们电脑的操作系统有32位和64位的操作系统,但是为什么影响了指针大小呢?

因为32位操作系统其实就是有32根地址线,每一个地址线上都会通过0/1的高低电信号,而这些电信号生成的01序列,其实就是内存单元格的地址编号,通过生成不同的电信号序列去访问这些地址。

而我们要去计算地址的大小那么答案就很明确了,32/8,就是用32个0/1除8个比特位,就是4个字节。通过这些得知64位就是8字节大小。

2.指针的类型

我们理解了上后,我们可能就会说,既然指针的大小取决于操作系统,那么给指针定义类型的有什么意义呢?

这里我想告诉你,意义区别非常大

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

我们看到很多类型的指针,可能现在不用的时候看不出有什么区别,但是如果我们都给指针加1,那么就能看到他们的不同了,char* 的指针 会跳过1个char类型,int* 的指针 会跳过1个int类型,short* 的指针 会跳过1个short类型.

int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;

	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return  0;
}

 

 

也就是说,指针的类型会决定他的步长。同理,如果解引用这个指针,他的类型也会决定它能操作的大小。

3.野指针

野指针其实就是指向未知空间的指针,这个空间并不是我们开辟的,是随机的,未知的

而一般造成这个原因的是指针没有初始化。

如果我们定义了一个指针并且不知道他将要指向那一块空间的时候那么就让他指向一个null(空);


int main()
{ 
    int *p;
    *p = 20;
 return 0;
}

 这个就是一个未初始化的野指针,再强行改变随机地址空间中的数据。实不可取的!!!

int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=10; i++)
   {
        *(p++) = i;
   }
    return 0;
}

我们可以看到arr数组中只有10个空间,但是我们在循环访问的时候,多访问了一次,超出了arr数组所使用的空间,就会存在隐患,就会造成野指针

int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* pi = test();

	return  0;
}

初学者再看这段代码的时候可能觉得并没有什么问题,但是你仔细想想,我们局部变量一般都是存储在栈区中的,出了作用域他就会自动回收,pi拿到的就是一个被回收过的地址,自然会造成野指针的问题

那么这时就有人问了,我们怎么避免这种错误呢?

其实也非常的简单:

那就是定义的时候记得初始化,不使用的时候要指向null,数组使用的时候不要越界 ,避免返回局部变量的地址。

4.指针的运算

指针+-整数 

int main()
{
	int arr[10] = {0};
	int* p = arr;
	for (int i = 0; i < 10; i++)
	{
		
		(*p)++;

		printf("%d ", p[i]);
		
	}
	return 0;
}

我们通过指针把数组中第一个数组加了10;如果不用for循环也可以指针加整数访问数组中的元素。

指针-指针

int main()
{
	char arr[] = "abcde";
	char* p = arr;
	while (*p != '\0')
	{
		p++;
	}
	int ret = p - arr;
	printf("%d ", ret);
}

指针减指针其实计算的是他们之间的元素个数(之间的距离),但是需要注意的是要用大地址减小地址,如果用小地址减大地址就会得要负个数。

指针的关系运算

int main()
{
	char arr[] = "abcde";
	char* p = NULL;
	for (p = &arr[4]; p >= &arr[0]; p--)
	{
		*p = 'x';
	}

	printf("%s ", arr);
}

 

我们通过比较字符的大小改变了数组中的元素,但是这里需要注意的是看下图

 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较!

5.二级指针

int main()
{
	int a = 10;
	int* p = &a;//p是一级指针,指向的空间是int类型
	int** pp = &p;//pp是二级指针,指向的是存放一级指针p地址的空间
	**pp = 20;
	printf("%d ", a);
}

p是一级指针,指向的空间是int类型
pp是二级指针,指向的是存放一级指针p地址的空间

*pp是一级指针指向int **pp就是a 可以直接修改a中的数据

上就是全部代码,如有错误和不足的地方。欢迎大家指正,完结撒花,下一节我们谈下数组指针和指针数组的区别!!!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值