C语言指针初等知识讲解

本文介绍了C语言中指针的基础知识,包括指针的定义、类型和解引用操作。讨论了指针类型的意义,如解引用时访问的字节数和指针加减整数的含义。此外,还讲解了野指针的概念及避免方法,NULL空指针的使用,以及数组名和指针的关系。文章进一步探讨了二级指针、指针数组以及指针在数组操作中的应用,包括模拟实现strlen()函数。
摘要由CSDN通过智能技术生成

一.指针的基本常识

指针是个变量,存放内存单元的地址

int main()
{
	int a = 10;//在内存中开辟一块空间
	int* p = &a;//&a为取出a的地址
	            //将a的地址存在指针变量p中
	return 0;
}

指针的大小仅与cpu位数有关,与指针所存放的变量大小无关。

32根地址线产生的地址(=指针变量大小)就是 32个比特位,4字节 ;有2的32次方种
00000000 00000000 00000000 00000000

11111111 11111111 11111111 11111111

1地址对应1字节,每字节的区域都有其对应的地址.

知识点补充:
计算机中的单位
bit——比特位 (1或0占一个比特位)
byte——字节(一个字节 = 八个比特位)
kb——1024字节
mb——1024kb
gb——1024mb
tb——1024gb
pb——1024tb
32位电脑,其地址有2 ^ 32种
假设每个地址对应一个bit(比特位)
已知1024 = 2 ^ 10
(232)bit÷8÷(210)÷(210)÷(210)= 0.5gb
若1地址对应1bit,则电脑只有0.5G,不合适
所以说1地址对应1字节,为4G空间,合适
由此可以看出1地址对应1字节

二.指针类型的意义(重点)

指针类型
为何区分指针变量的类型?
如 char* int* 同为指针变量的数据类型(指针类型),都占相同的字节,表达的地址也相同(都为变量的地址),那么区分类型的意义是什么?

1.(意义)解引用时能够访问的字节数

指针类型决定了指针进行解引用操作时,能够访问空间的大小

int main()
{
	int a = 0x11223344;//通过查看内存,内容是11 22 33 44(这里不考虑大小端字节序的问题)
	int* pa = &a;
	*pa = 0;//(这里*pa认为它指向int)经过此操作,a变为00 00 00 00
	
    a = 0x11223344
	char* pc = &a;//通过查看内存,内容是11 22 33 44
	*pc = 0;//(这里*pc认为它指向char)经过此操作,a变为11 22 33 00
	return 0;
}

指针类型决定指针能够访问空间的大小
int*p ;p能够访问4个字节
char
p ;p能够访问1个字节
double
p ;*p能够访问8个字节

2.(意义)指针±整数

int main()
{
	int a = 0x11223344;
	int* pa = &a;
	char* pc = &a;

	printf("%p\n", pa);//       000000CFFF55F894
	printf("%p\n", pa + 1);//   000000CFFF55F898   指针跨度为4字节 (一指针对应一字节)

	printf("%p\n", pc);//       000000CFFF55F894
	printf("%p\n", pc + 1);//   000000CFFF55F895   指针跨度为1字节

}

如上所示,指针类型不同,其向前或者向后走的"字节跨度不同 ".

下面是指针±整数的具体应用

int main()
{
	int arr[10] = { 0 };
	int* p = &arr;//若int*,解引用时每4字节赋值一个1
	char* pc = &arr;//若char*,解引用时每一字节赋值一个1,导致不想要的结果
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(pc + i) = 1;
		printf("%d", arr[0]);
	}
	return 0;
}

三.野指针

概念:野指针就是指向的位置不可知的(随机的、不可确定都、没有明确限制的)

1.野指针的原因】

①指针未初始化

int main()
{
	int* p;//局部变量未初始化,里面默认放一个随机值
	*p = 30;
	return 0;
}

②指针越界访问

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i;
	for (i = 0; i <= 15; i++)
	{
		*p++ =i;//p越出其管理的arr范围,就是野指针
	            //上面相当于*p=i;p++(虽然++优先度比较高,但是后置++在赋值表达式算完以后才自增)
    }
    return 0;
}

③指针指向的空间释放

int* f()
{
	int a = 10;
	int a[10]={0};
	return &a;
}

int main()
{
	int* p = f();//a变量出来其所在的函数就被删除,地址会还给系统
	printf("%d", *p);//栈上的空间虽然回收了,但是该地址空间上的10值还在。目前还没有其他操作或者语句用了那块地址。所以还是10,但是在实际情况下不可这样
	return 0;
}

2.如何避免野指针?

1.指针初始化
2.小心指针越界
3.指针指向空间释放后,将指针赋为NULL
4.指针使用之前检查有效性

四.NULL 空指针

可以把指针变量初始化为空指针NULL来避免野指针出现

空指针可以确保不指向任何对象或函数;而未初始化的指针则可能指向任何地方。

int main()
{
	int a = 0;
	int* pa = &a;//初始化指针
	int* p = NULL;//将暂时用不到的指针赋为空指针
	return 0;
}

若是不清楚该初始化为何值,可赋空指针NULL

同理,若之前的地址无效之后,为避免其之后成为野指针,建议及时把它赋为NULL

注意,空指针的类型是void* ,是指针变量,所以赋空指针时不要解引用

编译器中,#define NULL ((void *)0),地址是0x00000000,不能被访问

五.数组名

数组名通常情况下为首元素地址,除了以下两种情况:

1.sizeof(数组名):这是计算整个数组的大小
2.&arr:这时候取的是整个数组的地址;虽然和首元素地址看起来相同,但是地址跨度是整个数组

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]); //可以看出首元素地址 = 数组名
	return 0;
}

小练习:通过地址访问数组元素

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%p  %p\n", arr + i, &arr[i]);
	}
	for (i = 0; i < 10; i++)
	{
		printf("%-2d  %-2d\n", *(arr + i), arr[i]);
	}
	return 0;
}

六.二级指针

指针变量也是变量,创建时会向内存开辟空间,所以也地址;指针变量的地址就是【二级指针】(也有三、四、五极等)

int main()
{
	int a = 10;
	int* pa = &a;//一级指针
	int** ppa = &pa;//二级指针
	int*** pppa = &ppa;//三级指针
	//. .....
	return 0;
}

下面是二级指针的解引用:

int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	**ppa = 20;//这里 **ppa = *(*ppa) = *(pa) = a
	printf("%d %d", a, **ppa);
	return 0;
}

七.指针数组

指针数组 - 数组 ,存放指针的数组

例如 int arr[10]={1,2,3}:整形数组 ; char arr[10]={‘a’,‘b’,‘c’}:字符数组 ;int* arr[10]={&a,&b,&c}:指针数组

区分: 数组指针 - 指针 ,数组的指针

int main()
{
	int a = 10, b = 20, c = 30;
	int* arr[3] = { &a,&b,&c };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("arr[%d]=%p , *arr[%d]=%d\n", i + 1, arr[i], i + 1, *arr[i]);
	}
	printf("%d", **(arr+1));
	return 0;
}

八.指针的相关题目与运算

1.利用指针寻找数组元素

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[1]);
	int* p = &arr;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", * (p + i));
	}
	return 0;
}

数组元素的指针在比较时,允许指针与数组最后一个元素后面的那个位置比较,
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较.
(编译器上跑到过去,但是不满足c语言的语法)

2.利用指针把数组赋为相同的元素

int main()
{
	char arr[10] = {0};
	char* p = arr;
	int i = 1;
	int sz = sizeof(arr) / sizeof(arr[0]);
	while( p <= &arr[sz-1])//这里意在说明指针也可以比较或者运算
	{
		*p= '*';
		printf("%d%c ",i, *p);
		p++;
		i++;
	}
	return 0;
}

3.数组内两元素指针相减

数组内两元素指针相减可得到两元素间【相差的元素个数】而非相差的字节数

int main()
{
	int arr[10] = { 0 };
	printf("%d", &arr[9] - &arr[0]);//10
	return 0;
}

4.模拟实现strlen()函数

int my_strlen1(char* arr)//普通方法
{
	int n = 0;
	while (*arr++)
	{
		n++;
	}
	return n;
}


int my_strlen2(char* arr)//递归
{
	return (*arr ? my_strlen2(arr + 1) + 1 : 0);
}



int my_strlen3(char* arr)//指针相减
{
	char* p = arr;
	while (1)
	{
		if (!(*p))//当p指向'\0'
		{
			return (p - arr);
	    }
		p++;
	}
}


int main()
{
    char arr[] = "qwertyuiop";
    printf("%d %d %d %d\n", strlen(arr), my_strlen1(arr), my_strlen2(arr), my_strlen3(arr));
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值