C语言:指针初级

一、指针

1.1概念

在计算机中,所有的数据都是存放在存储器中的,不同的数据类型占有的内存空间的大小各不相同。内存是以字节为单位的连续编址空间,每一个字节单元对应着一个独一的编号,这个编号被称为内存单元的地址。比如:int 类型占 4 个字节,char 类型占 1 个字节等。系统在内存中,为变量分配存储空间的首个字节单元的地址,称之为该变量的地址。地址用来标识每一个存储单元,方便用户对存储单元中的数据进行正确的访问。在高级语言中地址形象地称为指针。

1.1.1指针就是个变量,用来存放地址,地址唯一表示一块内存空间。

内存编号 = 地址 = 指针

1.1.2指针的大小是固定的4/8个字节(32位平台/64位平台)

1.2指针变量及其定义

指针变量是存放一个内存地址的变量,不同于其他类型变量,它是专门用来存放内存地址的,也称为地址变量。定义指针变量的一般形式为:类型说明符*变量名。 

类型说明符表示指针变量所指向变量的数据类型;*表示这是一个指针变量;变量名表示定义的指针变量名,其值是一个地址,例如:char*p1;表示 p1 是一个指针变量,它的值是某个字符变量的地址。

#include <stdio.h>  
//打印的变量(i或者p)的地址可能每次都不一样
int main(int argc,char** argv)
{
    int i = 0x12345678;         
    int* p = &i;              
  
    printf("0x%08x \n", i);      //打印结果: 0x12345678
    printf("0x%08x \n", &i);     //打印结果: 0x004ffed0
    printf("%d \n", sizeof(i));  //打印结果: 4
  
    printf("0x%08x \n", p);      //打印结果: 0x004ffed0
    printf("0x%08x \n", &p);     //打印结果: 0x004ffecc
    printf("%d \n", sizeof(p));  //打印结果: 4
   
    // 指针变量p指向的对象是变量i
 
    // 1.指向的对象变量i的地址是&i(0x004ffed0)
    // 2.指向的对象变量i的类型是int
    // 3.指向的对象变量i的内容是0x12345678
    // 4.指针变量p本身的地址是&p(0x004ffecc)
    // 5.指针变量p本身的类型是int*
    // 6.指针变量p本身的内容是&i(0x004ffed0)
   
    return 0;
}

1.3指针类型

1.3.1、int* p[10]
表示一个包含10个整型指针的数组,强调数组概念,是一个数组变量,数组大小为10,数组内每个元素都是指向int类型的指针变量。

1.3.2、int (*p)[10]
表示一个指向有10个整型数数组的指针,强调是指针,只有一个变量,是指针类型,不过指向的是一个int类型的数组,这个数组大小是10。

1.3.3、int *p(int)
是函数声明,函数名是p,参数是int类型的,返回值是int *类型的。

1.3.4、int (*p)(int)
函数指针,强调是指针,该指针指向的函数具有int类型参数,并且返回值是int类型的。


1.3.5、指针的类型决定了指针+-整数的步长,指针解引用时候的权限。

int main()
{
	int a = 4;
	int* p1 = &a;
	char* p2 = &a;
	printf("%p\n", p1);
	printf("%p\n", p2);
	printf("%p\n", p1 + 1);
	printf("%p\n", p2 + 1);
	return 0;
}

结果:

解释 

最开始p1和p2地址是一样的,但后面让p1和p2分别进行+1,后面的结果就不同了,p1加的1是int类型的1,而p2+1加的是char类型的1。上面我们说到指针的大小是固定的4/8个字节,假设是32位平台,那么一个指针就占4个字节。如果这时我定义一个整型指针和字符指针,那么这个整型指针在解引用时就可以访问4个字节,而字符指针就只能访问1个字节。

1.4野指针

1.4.1概念:指针的位置是不可知的

1.4.2成因:1.指针未被初始化   2.指针的越界访问

1.4.2.1未被初始化:就是我们在创建指针变量的时候没有让它指向任何对象

例如: int* p;   这样p就是一个局部变量,p就是一个随机值 

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

1.4.2.2指针的越界访问

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 6; i++)
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
}

 int* p = arr; 这里arr是数组名,数组名是首元素的地址

那么现在p就是首元素的地址  对p进行解引用就是*p ,*p的值就是 1

p++;  这行代码就是让p的地址++;指针的大小是固定的4/8个字节, int型数据在C语言中也是4/8个字节,我们拿到的指针都是数据第一个字节的地址,而数组在内存中又是连续的,p++就是刚好往后移动一个数据。

但是现在arr数组一共就只有5个元素,但是循环6次必然会导致数组的越界

前面5个数就是arr数组里面的数,第6个值就是一个随机值。因为当循环到第6次时,p已经没有指向的对象了,此时p就是一个野指针了。

1.4.3如何避免野指针

1.善于使用NULL,及时对指针进行初始化

如果你在定义指针变量的时候,就已经想到指针变量指向的对象,那就直接进行初始化。

如果你在定义的时候,还不清楚指针指向的对象,也不清楚后面要不要使用指针,那就对指针变量赋值为NULL   

NULL就是空的意思,如果int *p=NULL; 那么此时p就是一个空指针,后面可以重新赋值,并不影响后面的使用。如果一个指针是空指针,在你还没初始化前不要使用它。

2.避免指针的越界

3.避免返回局部变量的地址

1.5、指针运算

int main()
{
	//指针地址加减整数
	int arr[5] = { 1,2,3,4,5 };
	int* p1 = arr;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *p1);
		p1++;
	}
	printf("\n");
	//解引用后的指针加减整数
	int b = 10;
	int* p2 = &b;
	(*p2)++;
	printf("%d", *p2);
	return 0;
}

结果

p1++是对地址进行加减整数。

而(*p2)++, 是定义了一个b变量,然后赋值给了10,然后把b的地址给了p2,*p2通过解引用得到的就是10,(*p)++  相当于 10++ ,得到的就是11。

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	printf("%d", &arr[4] - &arr[0]);
	return 0;
}

没用指针变量相减,其实是一样的,毕竟指针就是地址。两个指针指向同一块空间时,指针减指针的绝对值得到的就是这两个指针之间数据的个数。

1.6指针和数组

1.6.1.数组是可以通过指针来访问的。

1.6.2.通常情况下 数组名是首元素的地址 

例外:

        1.sizeof(数组名)  得到的是整个数组的大小 

        2.&+数组名 这里取出的是整个数组的地址。

1.6.3.在进行函数传参时,如果形参是数组,可以把形参设计成指针,当然如果形参是数组,也可以传指针作为实参。

1.7二级指针

二级指针就是用来存放一级指针(指针变量)的地址。

int main()
{
	int a = 5;
	int* pa = &a;
	int** ppa = &pa;
	printf("%d\n", **ppa);
	** ppa = 20;
	printf("%d\n", **ppa);
	printf("%d\n", a);
	return 0;
}

结果

 此时pa是一级指针,ppa就是二级指针,ppa是把pa的地址取出来放在ppa里面

**ppa就是*pa找到pa,在对pa进行解引用找到a

1.8、指针数组

指针数组的定义:int* 数组名[大小]  

int main()
{
	int arr1[3] = { 1,2,3 };
	int arr2[3] = { 4,5,6, };
	int* arr3[2] = { arr1,arr2 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 2; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("%d ", *(arr3[i] + j));
		}
		printf("\n");
	}
}

结果

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello xiǎo lěi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值