1
指针类型决定了指针进行了解引用操作的时候,能够访问字节空间的大小。{char* 是访问一个字节,int* 是访问四个字节,double*是访问8个字节,但注意,无论是什么类型的指针,地址都是以四字节进行存放的。指针类型并不决定存放地址的字节大小。}
同时,指针类型决定了指针进行加减运算时向后移动几个字节。例如pa+1是指针加了4(相当于跳过四个字节(int型)),pc+1是指针加了1(char为一个字节)
#include<stdio.h>
int main()
int a = 0x11223344;
int *pa =&a;
char *pc = &a;
printf("%p",pa+1);
printf("%p",pc+1);
野指针 :指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
出现野指针的原因:1.指针未初始化:仿照变量,在变量未初始化时,该变量会分配一个随机的内容,指针也是如此,指针如果未初始化,那么就会分配一个随机的地址。
#include<stdio.h>
int main()
{
int *p;
*p = 20;
return = 0;
}
2.指针越界访问:指针访问地址超过数组的范围。
3指针的动态释放(学到再说)
规避野指针的方法:1.指针初始化 2.小心指针越界 3.指针指向空间释放即使置NULL 4.指针使用之前检查有效性。
当NULL作为一个地址时,是不可以访问NULL这个地址的。
2 指针的运算
指针的运算有1.指针+-整数。2.指针-指针。3.指针的关系运算(比较大小之类的)。 其中要注意的是指针-指针得到的是指针中间的元素个数。指针-指针应该是在同一块空间。
c语言允许指向数组元素的指针与指向数组元素最后一个元素后面的那个内存位置的指针进行比较,但是不允许与第一个数组元素之前的那个内存位置的指针进行比较。
众所周知,数组名其实是数组首元素的地址,假设arr是个数组名,那么arr其实就是&arr[0],但是有两个例外:1.&+数组名(&arr)这个时候数组名不是首元素的地址,这时候数组名表示整个数组,所以&arr其实是取出整个数组的地址。2.sizeof(数组名){例如sizeof(arr)}这时候的数组名也不是首元素的地址,它表示整个数组{sizeof计算的是整个数组的大小}。
在第一个例外中,我们测试发现printf函数输出的三个结果是相同的,
int a[10] = {0};
printf("%p\n",a);
printf("%p\n",&a[0]);
printf("%p\n",&a);
但是在下面三行代码的输出结果就不同了,前两个是跳过一个元素,而第三个是跳过整个数组,这也就是区别所在。
printf("%p\n",a+1);
printf("%p\n",&a[0]+1);
printf("%p\n",&a+1);
数组指针:int (*p)[10] 数组指针:int *p[10],前者是指针,后者是数组。
分析下列代码,&arr应该是取arr这个数组的地址,那么数组的地址我们应该放到数组指针当中去,那么这里的pa的类型应该写成char (*pa)[5],要注意,后面的[5]指的是pa指向的数组有五个元素。
#include<stdio.h>
int main()
{
char *arr[5];
pa =&arr;
return 0;
}
如果我们已经获得了一个数组指针,那么问题来了,我们应该怎么去用这个数组里面的元素呢
#include<stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*pa)[10] = &arr;
int i = 0;
for(i = 0;i < 10;i+=)
{
printf("%d",*(*pa+i)); //pa是arr这整个数组的地址,*pa相当于把这整个数组的地址分成了每个元素的地址
}
}
指针的进阶:
1.字符指针:
#include<stdio.h>
int main()
{
char ch = 'A';
char* pc = &ch; //这是一个字符指针
char ch[] = "abcdfer";
char*pc = ch; //这个同样是一个字符指针。
char*pc ="abcjdrhg"; //这也是一个字符指针
}
对于第二个字符指针:要注意%s是直接解引所以,ch前面不需要+*。
char ch[] = "abcdfer";
char*pc = ch;
printf ("%s\n",ch);
printf("%s\n",pc);
对于第三个字符指针来说:是将'a'的地址赋给了pc{首字符的地址} 注意后面的一串字符串是常量字符串pc是存放的'a'的地址,那么*pc就是字符常量'a',那么我们把*pc重新赋值,例如*pc = 'W';就可以发现程序运行可能有问题。{可能能运行,但是仍然存在问题}
指针p 指向常量字符串(位于常量存储区),常量字符串的内容是不可以被修改的,企图修改常量字符串的内容而导致运行错误。所以这个问题出现的原因是char*s="Hello word",赋值的是字符串常量,存储在常量存储区,而常量存储区的内容是无法修改的。
void testCStr(){
char * s = "Hello word";
s[0] = 'a';
cout<<s<<endl;
}
例题:
#include<stdio.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcdef";
char* p1 = "abcdef";
char* p2 = "abcdef";
if(arr1 == arr2)
{printf("hh");}
else{printf("haha");}
}
正确的答案应该是haha 原因是这两个数组的数组名是首元素的地址,这两个地址是不同位置的,那么肯定是不一样的。
if(p1 == p2)
{printf("hehe");}
else
{printf("haha");}
如果改成这样就应该是hehe了,原因是p1和p2这两个字符串常量是一摸一样的,常量字符串理论上来说是不能修改的,那么没有必要再内存中存两份,那我只要存一份。{为了在内存当中节省空间所以这两个都指向同异空间的起始地址。}
2.指针数组:
主语是数组 其形式为int*p[10];(因为[]的优先级要高于*,所以该形式为指针数组,用来接收多个数组。)
3.数组指针 起形式为int(* p)[10] ,这个是指的指向元素为10的整形数组指针,其接收的是一个数组的地址,p就是整个数组的地址,*p是指的把数组地址解开成每个元素的地址,这里的*p为第一个元素的地址。(*p)+i即为第i个元素的地址。
4.函数指针:指向函数的指针
函数其实是也存在地址的,不相信的话可以printf输出测试一下
函数名所代表的就是函数的地址,当然&函数名和函数名都是函数的地址,二者没有任何区别。
函数指针的形式 int (*pa)(int,int) = Add; 第一个int 是函数的返回类型 括号里面的两个int都是函数参数的类型。
那么我们来看两个例题:void(*signal(int,void(*)(int)))(int);这其实是一个函数的声明,我们拆开看,看void(*)(int)这一看就是一个函数指针的形式,signal的参数,随后再把signal(int,void(*)(int)去掉,我们发现void(*)(int)这同样是一个函数指针的类型,那么这个函数指针的类型在这里就是signal函数的返回形式。
再看第二个例题(*(void(*)())0)拆开看 ( void(*)() )0这是把0进行了强制类型转化,再+*就是解引用随后再看后面的()即为函数调用符;