初学C语言之指针

指针是什么

1、指针是内存中一个最小单元的编号,也就是地址。

地址的来源

地址0x000000000x000000010x000000020x000000030x00000004
内存

内存本质上是数据的存储设备,其中被划分为大小相同的空间,为了方便使用,于是给每个内存空间加上了编号,这个编号就是内存的地址也就是指针。

内存空间的大小是多少

计算机中的单位

名称大小
bit1个比特位
byte8个bit
kb1024byte
mb1024kb
gb1024mb
tb1024gb

计算机中最小的单位是bit,也就是二进制的位,表示该位置能存放1或者0,如果把1个bit作为一个内存单元,那么空间太小,一个int类型将占据32个内存单元,同时会导致整个内存的空间都很小,但如果以kb作为一个内存单元,那么会浪费很多空间,所以最合适作为内存单元单位的是byte。

内存的编址

确定了内存空间的大小后,为了方便使用就必须要给内存单元编号。

假设内存有32根地址线,每根线地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是1或者0。

那么32跟地址线产生的地址就会是:

0000 0000 0000 0000 0000 0000 0000 0000

0000 0000 0000 0000 0000 0000 0000 0001

0000 0000 0000 0000 0000 0000 0000 0010

........

1111 1111 1111 1111 1111 1111 1111 1110

1111 1111 1111 1111 1111 1111 1111 1111

也就是有2的32次方个地址,如果是64根地址线,那么就有2的64次方个地址。

2的32次方个字节也就是4gb。

2、平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。

我们可以通过&(取地址操作符)取出变量的内存地址,把地址可以存放到一个变量中,这个变量就是指针变量

2、平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。

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

上面的代码中 ,p就是指针变量也就是口语中的指针,它的作用是用来存储变量a的地址。

那么指针变量p又是怎么存放地址呢?

int main()
{
	printf("%d\n", sizeof(int *));
	system("pause");
	return 0;
}

上面的代码时计算指针变量的大小,结果为4,这说明1个指针变量,只能放的下4个字节的内容。

而一个地址的大小是32位,也就是4个字节,所以一个指针变量,只能存放一个地址。

那么 a变量占据了4个字节是怎么存放进p指针变量呢?

答案是p只存放a变量首字节的地址,变量a在开辟空间时,开辟的是连续的4个字节,只要记录下首字节的地址就可以。

使用&(取地址运算符)就可以把变量a的地址取出。

指针变量的大小

数据类型有大有小,指针变量是否也有不同的大小呢?

int main()
{
	printf("%d\n", sizeof(int *));
	printf("%d\n", sizeof(char *));
	printf("%d\n", sizeof(short *));
	printf("%d\n", sizeof(float *));
	printf("%d\n", sizeof(double *));
	printf("%d\n", sizeof(long *));
	system("pause");
	return 0;
}

上面的代码运行的结果都是4,说明指针变量的大小是相同的,在32位机器下是4个字节,在64位机器下是8个字节,原因是地址的大小都是相同的。

指针和指针类型

与其他的数据类型一样,指针也是有类型的,type *就是指针的类型。

上面代码中的int *,char * short *等等就是指针的类型,分别对应不同的数据类型。

但是其他的数据类型是为了使用大小不同的空间,指针变量的大小都是一样的为什么需要不同的类型呢?

指针类型的意义

那是因为不同的指针类型对内存空间操作的权限不同。

int main()
{
	int a = 0x11223344;
	int *pa = &a;
	*pa = 0x44;
	*pa = 0x11223344;
	char *pc = &a;
	*pc = 0x00;
	system("pause");
	return 0;
}
这段代码运行的步骤如下:
int a = 0x11223344;首先在内存中开辟一块4个字节的空间,并命名为a。
此时a的内存空间中 是44 33 22 11
int *pa = &a;再开辟一块空间,命名为pa,用来存放a的地址。
*pa = 0x44;通过pa解引用改变a中的值。
此时a的内存空间中 是44 00 00 00 
*pa = 0x11223344;通过pa解引用改变a中的值。
此时a的内存空间中 是44 33 22 11
char *pc = &a;再开辟一块空间,命名为pc,用来存放a的地址。(这里的类型是char *表示对内存空间的操作只有char类型的权限)
*pc = 0x00;
此时a的内存空间中 是00 33 22 11

上面的例子已经说明,不同的指针类型具有不同的权限,只能操作对应的内存空间,实际上不同的指针类型读取和写入数据的方式也不同。 

指针+-整数

指针加减整数,指针加减整数,本质是指针的移动,而整数则是指针移动的步长

int main()
{
	int a = 0x11223344;
	int *pa = &a;
	char *pc = &a;
	printf("pa=%p\n", pa);
	printf("pc=%p\n", pc);
	printf("pa+1=%p\n", pa+1);
	printf("pc+1=%p\n", pc+1);
	system("pause");
	return 0;
}

 

上面这段代码的结果可以明确看出,pa与pc加1时移动的距离是不同的,pa+1向后移动了4个字节,但是pc+1只向后移动了1个字节。 

指针的解引用

解引用的操作符是*,它的作用就是通过指针变量中存放的地址找到变量的值。 

*pa等于a。

野指针

野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)。

野指针成因

1、指针未初始化。

指针未初始化时,pa指向的地址时随机的。

int main()
{
	int *pa;
	*pa = 10;
	printf("%d\n", *pa);
	system("pause");
	return 0;
}

2、指针越界。

当指针超过了指定的范围时,指针指向不正确的地址。

int main()
{
	int arr[10] = { 0 }; 
	int *pa = arr;
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		printf("%d ", *(pa + i));
	}
	system("pause");
	return 0;
}

 3、指针指向的空间释放。

这是一种最难以察觉的错误,指针被指向的空间释放后,如果没有放入其他数据,该空间存放的值并不会改变。

int* add()
{
	int c = 10;
	return &c;
}

int main()
{
	int *pa = add();
	printf("%d\n", *pa);
	system("pause");
	return 0;
}

 但如果该空间的值会改变,对该空间的操作就会产生未知的影响。

int* add()
{
	int c = 10;
	return &c;
}

int main()
{
	int *pa = add();
	printf("hehe\n");
	printf("%d\n", *pa);
	system("pause");
	return 0;
}

如何规避野指针

1、对指针初始化。

int main()
{
	int a = 10;
	int *pa = &a;
	system("pause");
	return 0;
}

如果创建指针时,不确定指针具体指向哪里,可以将指针指向NULL,使用时进行判断就可以,注意NULL本质是0,当指针指向NULL时,是不能解引用的。

int main()
{
	int *pa = NULL;
	if (pa == NULL)
	{
		printf("指针指向NULL");
	}
	system("pause");
	return 0;
}

2、小心指针越界,指针越界只能在写代码的时候多注意了。

3、指针指向空间释放时将指针指向NULL。

int* add()
{
	int c = 10;
	return &c;
}

int main()
{
	int *pa = add();
	pa = NULL;
	if (pa == NULL)
	{
		printf("指针指向NULL");
	}
	printf("%d\n", *pa);
	system("pause");
	return 0;
}

4、避免指针返回局部变量的地址。

5、指针使用之前检查有效性。 

指针运算

指针+-整数

指针+-整数的本质是指针的移动,+-代表方向,整数代表步长。

int main()
{
	int a = 0x11223344;
	int *pa = &a;
	char *pc = &a;
	printf("pa=%p\n", pa);
	printf("pc=%p\n", pc);
	printf("pa+1=%p\n", pa+1);
	printf("pc+1=%p\n", pc+1);
	system("pause");
	return 0;
}

指针-指针

指针-指针本质是计算两个指针之间有多少个元素,其结果要看作绝对值。

注意;该运算的前提是同一块空间的指针,(一般是数组,连续空间,类型相同)

int main()
{
	int arr[10];
	int *pa = arr;
	int *pb = &arr[9];
	printf("%d\n", pa - pb);
	printf("%d\n", pb - pa);
	system("pause");
	return 0;
}

注意:这里arr数组最后一个元素的指针减去第一个元素的指针,结果为9而不是10。

运算过程如下:

 指针-指针是两个指针之间元素的个数,所以结果为9。

指针的运算关系

比较指针的大小关系。

注意:标准规定允许指向数组元素的指针与指向数组最后一个元素后面的那个位置的指针比较,但是不允许与指向指针第一个元素前面的那个元素进行比较。

int main()
{
	int arr[10];
	int *pa=arr;
	for (pa; pa < arr+10;)
	{
		*pa++ = 1;
	}
	system("pause");
	return 0;
}

指针和数组

数组名就是数组首元素的地址,也就是指针。

sizeof(arr)时,数组名是整个数组的地址。

&arr时,数组名时整个数组的地址。

二级指针

二级指针就是指向指针变量的指针变量。

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

 int * pa;

*代表pa是指针,int是指pa所指向的数据类型。

int * *ppa;

后面的*说明ppa是指针,int *说明ppa所指向的是整型指针类型。

**ppa的解引用,是先通过*ppa找到pa,然后对pa进行解引用,找到a。

指针数组

指针数组就是保存指针的数组。

类型为type *  [const_n];

举例,利用指针数组模拟二维数组。

 

int main()
{
	int *arr[3];
	int arr1[5] = { 1, 2, 3, 4, 5 };
	int arr2[5] = { 6, 7, 8, 9, 10 };
	int arr3[5] = { 11, 12, 13, 14, 15 };
	arr[0] = arr1;
	arr[1] = arr2;
	arr[2] = arr3;
	int i = 0, j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j< 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	system("pause");
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值