前言:
前面已经详解过初级指针内容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}。