c c pointer - yaokun123/php-wiki GitHub Wiki
1、*与符号结合代表是一个指针变量
2、要保存谁的地址,将他的定义放在此处
3、用*p替换掉定义的变量
#include<stdio.h>
int main(){
int a = 10;
//1、*与符号结合代表指针变量(*p)
//2、要保存谁的地址,将他的定义放在此处(int a)
//3、用第一步的表达式替换掉定义的变量(int *p)
int *p = &a;
return 0;
}
指针变量:与*结合代表这是一个指针变量(p)
指针变量的类型:p是变量,p的类型是将变量p本身拖黑,剩下的类型就是指针变量的类型(int *)
指针变量p用来保存什么类型数据的地址,将指针变量p和指针变量p最近的*一起拖黑,剩下什么类型,就保存什么类型数据的地址(int)
#include<stdio.h>
int main(){
int a = 10;
int *p = &a;
//1、p是指针变量
//2、指针变量p的类型是int *
//3、指针变量p中存放的是什么类型的地址 int
return 0;
}
在使用时,对一个表达式取*,就会对表达式减一级*,如果对表达式取&,就会加一级*
不管什么类型的指针,大小只和系统编译器有关
#include<stdio.h>
int main(){
char *p1;
short *p2;
int *p3;
int **p4;
printf("%u\n",sizeof(p1));
printf("%u\n",sizeof(p2));
printf("%u\n",sizeof(p3));
printf("%u\n",sizeof(p4));
return 0;
/*
8
8
8
8
*/
}
#include<stdio.h>
int main(){
int num = 0x01020304;
char *p1 = (char *)#
short *p2 = (short *)#
int *p3 = #
//通过*取指针变量所指向哪块空间内容时,取的内存的宽度和指针变量本身的类型有关
printf("%x\n",*p1);//04
printf("%x\n",*p2);//0304
printf("%x\n",*p3);//01020304
return 0;
}
指针的宽度 = sizeof(将指针变量与指针变量最近的*拖黑,剩下的类型)
宽度也叫步长:指针+1跨过多少个字节
char *p1;//1
short *p2;//2
int *p3;//4
int **p4;//8
#include<stdio.h>
int main(){
char a = 'A';
char *p1 = &a;
printf("char a address %u\n",p1);
printf("char a address + 1 %u\n",p1+1);
short b = 10;
short *p2 = &b;
printf("short b addredd %u\n",p2);
printf("short b addredd + 1 %u\n",p2+1);
int c = 10;
int *p3 = &c;
printf("int c address %u\n",p3);
printf("int c address + 1 %u\n",p3+1);
int **p4 = &p3;
printf("**p4 address %u\n",p4);
printf("**p4 address + 1 %u\n",p4+1);
return 0;
/*
char a address 3799500779
char a address + 1 3799500780
short b addredd 3799500766
short b addredd + 1 3799500768
int c address 3799500748
int c address + 1 3799500752
**p4 address 3799500736
**p4 address + 1 3799500744
*/
}
在使用时:[] = *()
p[n] = *(p+n)
#include<stdio.h>
int main(){
int a = 10;
int *p = &a;
p[0] = 100;//p[0] = *(p+0) = *p
printf("%d\n",a);
return 0;
}
指针数组是一个数组,数组里面的元素保存的都是地址
#include<stdio.h>
int main(){
int a = 10;
int b = 20;
int c = 30;
//定义指针数组
int *p[10] = {&a,&b,&c};
printf("sizeof int *p[10] = %d\n",sizeof(p));//80
printf("%d \n",*p[0]);//访问数组第一个元素地址指向的值
//定义一个指针用来保存数组p首元素的地址p = &p[0] = &(int *) = int**
int **k = p;
for(int i = 0;i<sizeof(p)/sizeof(p[0]);i++){
printf("%d ",**(k+i));
}
return 0;
}
数组作为函数形参时,会默认转化为指针,数组首元素地址
#include<stdio.h>
void print_arr(int b[10]){
//数组作为函数形参时,会默认转化为指针int *b
printf("%u\n",b);
printf("%u\n",sizeof(b));//8
printf("%u\n",sizeof(b[0]));//4 = b[0] = *b = 1 = int;
}
int main(){
int a[10] = {1,2,3,4,5,6,7,8,9,10};
print_arr(a);
return 0;
}
修改为:
#include<stdio.h>
void print_arr(int b[10],int len){
//数组作为函数形参时,会默认转化为指针int *b
printf("%u\n",b);
printf("%u\n",sizeof(b));//8
printf("%u\n",sizeof(b[0]));//4 = b[0] = *b = 1 = int;
for(int i = 0;i<len;i++){
printf("%d ",b[i]);//b[i] = *(b+i)
}
printf("\n");
}
int main(){
int a[10] = {1,2,3,4,5,6,7,8,9,10};
print_arr(a,sizeof(a)/sizeof(a[0]));
return 0;
}
野指针不同于空指针,空指针是指一个指针的值为null,而野指针的值并不为null,野指针会指向一段实际的内存,只是它指向哪里我们并不知情,或者是它所指向的内存空间已经被释放,所以在实际使用的过程中,我们并不能通过指针判空去识别一个指针是否为野指针。避免野指针只能靠我们自己养成良好的编程习惯,下面说说哪些情况下会产生野指针,以及怎样避免。
1、指针变量的值未被初始化:
声明一个指针的时候,没有显示的对其进行初始化,那么该指针所指向的地址空间是乱指一气的。如果指针声明在全局数据区,那么未初始化的指针缺省为空,如果指针声明在栈区,那么该指针会随意指向一个地址空间。所以良好的编程习惯就是在声明指针的时候就对其进行初始化,如果暂时不知道该初始化成什么值,就先把指针置空。
#include <stdio.h>
int main()
{
int *p;//只定义了指针,未初始化,会导致生成野指针
*p = 100;
printf("%d\n",*p);
return 0;
}
2、指针所指向的地址空间已经被free或delete:
在堆上malloc或者new出来的地址空间,如果已经free或delete,那么此时堆上的内存已经被释放,但是指向该内存的指针如果没有人为的修改过,那么指针还会继续指向这段堆上已经被释放的内存,这时还通过该指针去访问堆上的内存,就会造成不可预知的结果,给程序带来隐患,所以良好的编程习惯是:内存被free或delete后,指向该内存的指针马上置空。
void func()
{
int *ptr = new int[5];
delete []ptr;
// 执行完delete后,ptr野指针
}
3、指针操作超越了作用域:
void func()
{
int *ptr = nullptr;
{
int a = 10;
ptr = &a;
} // a的作用域到此结束
int b = *ptr; // ptr指向a,a已经被回收,ptr野指针
}
指针指向了内存地址为0x00000000,该段地址不可操作