C语言指针详解(2)

前言:

        前面已经详解过初级指针内容C语言指针详解(1)-CSDN博客,不懂的小伙伴可以提前观看之前的内容。

const修饰的变量

        const修饰的变量称为常变量,类似于是一个常量,是不可以被修改的。

例如:

int main()
{
	int a = 0;
	不加const修饰
	a = 10;
	//这里没有报错
	//如果在初始化的时候加上const
	const int b = 10;
	//此时b是不能在被修改的,如果修改会报错
	b = 20;
	return 0;
}

        这是const修饰一个变量的时候,该变量被称之为常变量,这里的常变量不等于常量  。

        为什么说不等于常量,我们看着一组代码。

         我们都知道,初始化数组如果要指定数组里的元素个数的时候,必须是常数。

如果这里的a是直接是一个常量的话,应该不会报错,但是这里报错了!!

int main()
{
	const int a = 10;
	int arr[a] = { 0 };//这指定数组里的元素个数时,必须是常数,此时放const修饰的a进去报错
	return 0;
}

                   

const修饰指针变量

        const如果修是一个指针变量会是什么样的一个结果呢?

        分以下两种情况:

        1、const放在*之前:修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。

也就是如图所示:

int main()
{
	int a = 9;
	int b = 0;
	const int* c = &a;
	c = &b;//可以修改
	*c = 10;//不可以修改
}

              2、const放在*之后:修饰的是指针变量本身,保证了指针变量的内容不能修改。

也就是如图所示:

int main()
{
	int a = 0;
	int b = 9;
	int*const c = &a;
	c = &b;//不可以修改
	*c = 10;//可以修改
}

造成野指针的原因

  1、指针未初始化:

        例如:

int main()
{
	int a = 10;
	int* b;//未初始化,为野指针
	int* c = &a;
}

如果一开始指针不直到指针该怎么初始化,可以初始化NULL.

int main()
{
	int a = 10;
	int* b;//未初始化,为野指针
	int* c = &a;
	int* b = NULL;//可以初始化空指针
}

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;
}

这是两类常见的野指针。

数组名的含义:

一般情况:

        在这是一个重点,一个数组的数组名代表着什么意思。

        大家需要知道,一个数组的数组名可以放在指针变量中,放进去的的是首先元素的地址,例如:

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9};
	int* a = arr;//这里的arr就相当于是首元素的地址,就相当于&arr[0]
    char arr2[] = {'1','2','3'};
    char *b = arr2;//这里的arr2就相当于&arr[0]
}

        两类特殊情况:

1、sizeof(arr)

        如下代码:

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9};
	int* a = arr;//这里的arr就相当于是首元素的地址,就相当于&arr[0]
	printf("%d\n", sizeof(arr));//计算的是整个数组的大小,并不是arr[0]的大小 
}

        大家应该都知道,sizeof是计算大小的,如果sizeof(arr)这里的arr表示的是首元素arr[0]的话,打印的结果应该是4。

        但是,这里打印的结果是36,这里的arr表示的是整个数组的地址

2、&arr        

        如果直接进行&arr的话,此时得到的地址不单单是首元素的地址,而是整个数组的地址。

注:这里不要被这个取整个数组的地址所迷惑,整个数组的地址也是一个地址,我还是可以放入一个整型指针中。

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	int* a = &arr;
	int* b = arr;
	printf("%p\n", &arr);//打印整个数组的地址
	printf("%p\n", arr);//打印数组首元素的地址
	return 0;
}

        那么如果打印一下这两种情况的地址,结果会是则怎么样的呢?

        问题来了,结果是一样的,没有什么区别,为什么说一个是数字首元素的地址,一个是数组的地址,到底区别在哪?

        画图解释:

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	int* a = &arr;
	int* b = arr;
	printf("%p\n", &arr+1);//打印整个数组的地址
	printf("%p\n", arr+1);//打印数组首元素的地址
	return 0;
}

        区别是在加减整数,在初级指针的时候,有提到过指针加减的情况。

        如果取出的的是首元素的地址的时候,我加1只是跳过一个整形的大小。

        如果取出的是整个数组的地址的时候,我加1是跳过整个数组的大小。(如上图所示)

字符指针(进阶)

        前面介绍了字符指针,char*a的相关解读,在这里需要拓展一下char*a的用法等相关的说明。

首先看看如下的代码:

        

int main()
{
    char ch = 'w';
    char *pc = &ch;
    *pc = 'c';
    return 0;
}

这里的pc里面的一开始放的是字符w的地址,之后可以通过修改*pc修改ch的值。

可以这样使用字符指针。

打印字符串的方法(%s的本质)

那么可不可以这样使用字符指针呢?

int main()
{
    const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
    printf("%s\n", pstr);
    return 0;
}

我可不可以用一个字符指针指向一个字符串呢?

如果可以这个指针里面存放的是什么?

        答案是可以的,一个字符指针可以直接指向一个字符串的,里面存放的是这个字符串的地址,还是单单只是首字符的地址,那么如何验证呢?

        还是可以用加一验证,究竟里面存的是一个字符的地址,还是整个字符串的地址?

如果加以打印出来的地址比之前的只是加了1,那么认为就是单单放了首字符的地址,不是整个字符串的地址。

        

int main()
{
const char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%p\n", pstr);//pstr的地址
printf("%p\n", pstr + 1);//pstr+1的地址
return 0;
}

结果如下:

我们发现只是单纯的加了1,所以这里的pstr中存的不是整个字符串的地址!!

        但是通过上述的方法我们也可以打印出一个字符串出来,因为之前我们都是将字符串放在字符数组中,然后再打印,例如:

int main()
{
	char arr[] = {"abcdef"};
	printf("%s\n", arr);
}

        这里我们打印的时候传进去的是arr,arr是什么?arr就是首元素的地址,也就是第一个字符的地址。(想通了没??)

数组指针:

        之前说过一个指针数组,也就是一个数组里面全部都放相同类型的指针(地址),例如:

int main()
{
	int a = 1;
	int b = 10;
	int c = 20;
	int* arr[] = {&a,&b,&c};
}

        想要取哪一个直接通过下标拿出。

        今天我我们来介绍一个容易混淆的概念,数组指针,数组指针是一个指针,指向数组的指针,里面存放的是整个数组的地址!

        例如:

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10};
	int(*a)[10] = &arr;
	return 0;
}

        单独拿出int (*a)[10] = &arr;

        解读:

        int 表示类型,*a表示指针变量名,[10]表示有10个元素。

        它和int *a[10]的区别,int*a[10]表示的是是一个数组,这个数组是int*类型,数组名是a,也就是指针数组!

        此时a里面存的就是整个数组的地址,还是可以通过加1验证地址如何变化的!

大家可以自己验证一下,a的地址和a+1的地址区别在哪?

数组组指针的使用:

        数组指针因为存放的是整个数组的地址,我们这时候可以想到二维数组,我们可以把二维数组看作是一维数组的数组,怎么理解,一个二维数组例如:

int main()
{
int arr[3][3] = {{1,2,3},{2,3,4},{5,6,7}};
return 0;
}
int main()
{
int arr[3][3] = {{1,2,3},{2,3,4},{5,6,7}};
//可以看作如下以为数组的数组
int arr1[3] = {1,2,3};
int arr2[3] = {2,3,4};
int arr3[3] = {5,6,7};
int arr4[3] = {arr1,arr2,arr2};//这个代码不正确,多的是让大家理解
return 0;
}

所以可以利用刚刚的数组指针模仿一个二维数组:

void print_arr(int (*arr)[5], int b, int c)
{
	int i = 0;
	for (i = 0; i < b; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d", arr[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { {1,2,3},{2,3,4},{5,6,7} };
	int(*a)[5] = arr;//里面存的是arr[1]的地址也就是{1,2,3}的地址
	//模拟实现二维数组打印
	print_arr(arr, 3, 5);
	return 0;
}

注:二维数组的arr[3][5]直接写数组名arr的话就相当于是arr[1],arr[1] = {1,2,3}。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值