指针其实就是一个变量,X86占4个字节,X64占8个字节,它的值是一个内存地址,指向内存的某一个地方,明确了该内存的宽度(通过指针类型确定)。
指针含义分为3个方面:变量、地址、内存宽度
int *p = //int型的指针 p
指针的定义和初始化
类型名 指针名;例如:int p
指针的初始化,可以让指针指向某个变量地址,也可以让指针指向一个分配的内存或者字符串常量,当然也可以指向NULL。
p : 解引用运算符,取值 **要和指针的定义区别开来**
通过指针(存放的内存地址),找到对应的内存和里面存放的数据。
解引用后面的指针必须是有效的,不能是野指针或者NULL。(野指针是只定义没有初始化)
常量指针就是一但赋值就不会再变了,不能对它再进行赋值。
数组是一个常量指针
int a[4]={1,2,3,4}; //这里的a是一个常量指针。
常量指针中的地址是不能再赋值修改的
int a=10;
*&a ==a; //true
&*a; //写法错误,*只能作用于指针
int *p = &a;
*p ==a; //true
*&p ==p; //true
&*p ==p; //true
*在指针定义的时候,是和类型结合
*在指针使用的时候,是取内存(解引用)
void func(char *p){ //此处*p是定义的指针
printf(*p); //此处*p是解引用 取值
}
char 1个字节
short 2个字节
int 4个字节
float 4个字节
double 8个字节
void *p
//是无类型指针,其他类型指针隐式转换成该类型,不能*p来取值,先转特定类型再取值
//可以接受任何类型的指针
//赋值给其它类型的指针,需要强制转换
//不能进行解引用*运算,必须先转换
int *p = 10;
sizeof(p) //指针的长度 在x86中永远是4个字节,在x64中永远是8个字节。
sizeof(*p) //指针对应类型的长度,此处指针类型是int,所以是4个字节。
指针支持加减运算,不支持其他算术运算
对于指针p,指针的加法运算p=p+n (或者p+=n)中,p向前移动的距离不是n个字节,而是n个指针类型单位长度(int、char…),即nsizeof(p)个字节。
不允许对void指针进行算术运算和(解引用)。
*p++; // = *(p++) -> *p,p+=1; 先解引用取值,然后对p地址+1,也就是移到下一个地址 (先解引用,然后地址+1)
(*p)++; // -> *p,(*p)+=1; 先解引用,然后对解引用的值+1
*++p; // = *(++p) -> p+=1,*p
++*p; // = ++(*p) -> *p+=1,*p
//举例:
char *p = "hello";
printf("%c\n", *p++); // h
printf("%c\n", *p++); // e
printf("%c\n", *p++); // l
printf("%c\n", *p++); // l
printf("%c\n", *p++); // o
char *p = "lukejian";
printf("%c\n", *++p); // u
printf("%c\n", *++p); // k
//其他两个如果用字符串举例的话,因为字符串是在常量区,而常量区无法更改,所以会报错。
int a[10];
int arr[10][20];
a+1 //此处1代表4个字节
&a+1 //此处1代表4*10=40个字节 &a代表了整个数组
arr+1 //此处1代表4*20=80个字节
&arr+1 //此处1代表4*20*10个字节
数组作为参数,将退化为指针
int a[10] //指针数组
int (a)[10] //数组指针
//指针常量和常量指针
const int *p // 指针常量 p指向的内存是常量,不能修改,p本身是变量,可以修改。
int *const p //常量指针 指针不可以修改,但是指针指向的内存是可以修改的
函数指针:指向函数的指针
函数名,其实就是函数的地址。
如果一个指针变量,存放的是函数的地址,那么就把这个指针叫做函数指针。
函数的签名:包含了函数的返回值类型,参数类型,个数。
函数指针的类型由对应的函数的签名来决定。
//定义函数指针的两种方法:
//1、首先用typedef定义出函数指针的类型,然后通过函数指针类型来定义函数指针。
//2、直接用函数的签名来定义函数指针。
//应用,hook,dll函数动态调用。
int print_int(int x){
printf("hello,%d \n",x);
return 0;
}
typedef int(*F)(int x); //定义一个函数指针类型
int main(){
int a = 100;
int (*f1)(int x) = print_int;
f1(a);
F f2 = print_int; //f1,f2都是函数指针
f2(a);
print_int(a);
return 0;
}
指针函数:返回值是指针的函数
char *GetMemory(){
char *p = (char *)malloc(100); //在堆上分配100个内存
return p;
}
void Test(){
char *str = NULL;
str = GetMemory();
free(str); //释放内存;
str = NULL;
}
二级指针:存放一级指针的地址
一直指针:存放的是普通的内存地址。
int a = 10;
int *p = &a;
int **pp = &p; // **代表的是二级指针
printf("a=%p\n", a);
printf("&a=%p\n", &a);
printf("p=%p\n",p);
printf("&p=%p\n",&p);
printf("*p=%p\n",*p);
printf("pp=%p\n", pp); //=&p
printf("*pp=%p\n", *pp); //=&a
printf("**pp=%p\n", **pp); //=a 解引用两次
//二级指针做参数,被用来接收指针的地址
void get_memory2(char **p){
*p = (char *)malloc(100); // *p 一级解引用,就是指针str
}
int main(){
char * str = NULL;
get_memory2(&str); //传指针(实参)的指针 如果只传str,那其实就是传的普通的地址,&str就是传的指针的地址。
printf("%s\n",str);
free(str);
str = NULL;
return 0;
}
int func1(int x); //传实参的值,不能修改实参
int func2(int *x); //传实参指针,修改实参
int func3(int &x); //传实参引用,修改实参
int func4(int **x); //实参是指针,传指针的指针,修改指针
int func5(int *&x); //实参是指针,传指针的引用,修改指针
//分析下面程序的运行结果
int i=0,j=20,*p1 =&i,*p2=&j;
void f(int **ptr1,int *ptr2){
int *tmp = ptr2;
**ptr1 *=10;
*ptr2 *=10;
ptr2 = *ptr1;
*ptr1 = tmp;
}
f(&p1,p2);
//请问i,j,p1,p2的值各是多少?
//i =0;j=200 p1=&j;p2=&j;
函数return语句不可返回指向栈内存(局部变量)的指针或者引用,因为该内存单元在函数体结束时被自动释放。可以返回堆上或者参数指定的内存。
C和C++编译器默认采用浅拷贝,深拷贝由程序猿自己实现。
函数传参
1、传值 func(int x) //拷贝实参的值,无法改变实参
2、传指针 func(int *x) //拷贝实参的地址,可以改变实参
3、传引用(C++)func(int &x)
int a = 1 ;
func(int x); //x是形参
func(a); // a是实参
传值无法改变实参,传指针才能改变实参。
传指针和传引用效率比传值高。因为传指针和传引用都只是把地址传递给函数,这个过程,只涉及到4个或8个字节的传输。传值,会随着实参的类型不同,有时候不止传递或者拷贝4个字节
int a,b,c,d;
a = b = 10; //a和b得到值10
c = ++a; //a增加至11,c得到a拷贝的值11
d = b++; //d得到b拷贝的值10,然后b增加到11
//前缀和后缀形式的增值操作符都复制一份变量值的拷贝,用于周围表达式的值正式这个拷贝的值。
逻辑操作符:&& 和 ||
先对左边的操作进行运算,符合条件则不会再运算右边的操作。这种行为被称为“短路求值”
.和->操作符用于访问一个结构的成员。
如果s是个结构变量,那么s.a就访问s中命为a的成员。
当你拥有一个指向结构的指针而不是结构本身,且欲访问它的成员时,就需要使用->操作符而不是.操作符
int func(void){
static int counter = 1; //static 改变了存储区域,存储到外部静态区,第二次调用定义初始化则无效
//counter = 2; //但是可以进行赋值,如果在此处赋值,则不管调用几次,输出都是3
return ++counter;
}
int main(){
int answer;
int a = test15_func();
int b = test15_func();
int c = test15_func();
answer = a - b * c;
printf("a=%d\n", a); //2
printf("b=%d\n", b); //3
printf("c=%d\n", c); //4
printf("answer=%d\n",answer); //-10
}