指针其实就是一个变量,X86占4个字节,X64占8个字节,它的值是一个内存地址,指向内存的某一个地方,明确了该内存的宽度(通过指针类型确定)。
    指针含义分为3个方面:变量、地址、内存宽度

    int *p = //int型的指针 p

    指针的定义和初始化
    类型名 指针名;例如:int p
    指针的初始化,可以让指针指向某个变量地址,也可以让指针指向一个分配的内存或者字符串常量,当然也可以指向NULL。

    p : 解引用运算符,取值 **要和指针的定义区别开来**
    通过指针(存放的内存地址),找到对应的内存和里面存放的数据。
    解引用后面的指针必须是有效的,不能是野指针或者NULL。(野指针是只定义没有初始化)

    常量指针就是一但赋值就不会再变了,不能对它再进行赋值。

    数组是一个常量指针
    int a[4]={1,2,3,4}; //这里的a是一个常量指针。

    常量指针中的地址是不能再赋值修改的

    1. int a=10;
    2. *&a ==a; //true
    3. &*a; //写法错误,*只能作用于指针
    4. int *p = &a;
    5. *p ==a; //true
    6. *&p ==p; //true
    7. &*p ==p; //true

    *在指针定义的时候,是和类型结合
    *在指针使用的时候,是取内存(解引用)

    1. void func(char *p){ //此处*p是定义的指针
    2. printf(*p); //此处*p是解引用 取值
    3. }

    char 1个字节
    short 2个字节
    int 4个字节
    float 4个字节
    double 8个字节

    1. void *p
    2. //是无类型指针,其他类型指针隐式转换成该类型,不能*p来取值,先转特定类型再取值
    3. //可以接受任何类型的指针
    4. //赋值给其它类型的指针,需要强制转换
    5. //不能进行解引用*运算,必须先转换
    6. int *p = 10;
    7. sizeof(p) //指针的长度 在x86中永远是4个字节,在x64中永远是8个字节。
    8. sizeof(*p) //指针对应类型的长度,此处指针类型是int,所以是4个字节。

    指针支持加减运算,不支持其他算术运算
    对于指针p,指针的加法运算p=p+n (或者p+=n)中,p向前移动的距离不是n个字节,而是n个指针类型单位长度(int、char…),即nsizeof(p)个字节。

    不允许对void指针进行算术运算和(解引用)。

    1. *p++; // = *(p++) -> *p,p+=1; 先解引用取值,然后对p地址+1,也就是移到下一个地址 (先解引用,然后地址+1)
    2. (*p)++; // -> *p,(*p)+=1; 先解引用,然后对解引用的值+1
    3. *++p; // = *(++p) -> p+=1,*p
    4. ++*p; // = ++(*p) -> *p+=1,*p
    5. //举例:
    6. char *p = "hello";
    7. printf("%c\n", *p++); // h
    8. printf("%c\n", *p++); // e
    9. printf("%c\n", *p++); // l
    10. printf("%c\n", *p++); // l
    11. printf("%c\n", *p++); // o
    12. char *p = "lukejian";
    13. printf("%c\n", *++p); // u
    14. printf("%c\n", *++p); // k
    15. //其他两个如果用字符串举例的话,因为字符串是在常量区,而常量区无法更改,所以会报错。
    1. int a[10];
    2. int arr[10][20];
    3. a+1 //此处1代表4个字节
    4. &a+1 //此处1代表4*10=40个字节 &a代表了整个数组
    5. arr+1 //此处1代表4*20=80个字节
    6. &arr+1 //此处1代表4*20*10个字节

    数组作为参数,将退化为指针

    int a[10] //指针数组
    int (
    a)[10] //数组指针

    1. //指针常量和常量指针
    2. const int *p // 指针常量 p指向的内存是常量,不能修改,p本身是变量,可以修改。
    3. int *const p //常量指针 指针不可以修改,但是指针指向的内存是可以修改的

    函数指针:指向函数的指针
    函数名,其实就是函数的地址。
    如果一个指针变量,存放的是函数的地址,那么就把这个指针叫做函数指针
    函数的签名:包含了函数的返回值类型,参数类型,个数。
    函数指针的类型由对应的函数的签名来决定。

    1. //定义函数指针的两种方法:
    2. //1、首先用typedef定义出函数指针的类型,然后通过函数指针类型来定义函数指针。
    3. //2、直接用函数的签名来定义函数指针。
    4. //应用,hook,dll函数动态调用。
    5. int print_int(int x){
    6. printf("hello,%d \n",x);
    7. return 0;
    8. }
    9. typedef int(*F)(int x); //定义一个函数指针类型
    10. int main(){
    11. int a = 100;
    12. int (*f1)(int x) = print_int;
    13. f1(a);
    14. F f2 = print_int; //f1,f2都是函数指针
    15. f2(a);
    16. print_int(a);
    17. return 0;
    18. }

    指针函数:返回值是指针的函数

    1. char *GetMemory(){
    2. char *p = (char *)malloc(100); //在堆上分配100个内存
    3. return p;
    4. }
    5. void Test(){
    6. char *str = NULL;
    7. str = GetMemory();
    8. free(str); //释放内存;
    9. str = NULL;
    10. }

    二级指针:存放一级指针的地址
    一直指针:存放的是普通的内存地址。

    1. int a = 10;
    2. int *p = &a;
    3. int **pp = &p; // **代表的是二级指针
    4. printf("a=%p\n", a);
    5. printf("&a=%p\n", &a);
    6. printf("p=%p\n",p);
    7. printf("&p=%p\n",&p);
    8. printf("*p=%p\n",*p);
    9. printf("pp=%p\n", pp); //=&p
    10. printf("*pp=%p\n", *pp); //=&a
    11. printf("**pp=%p\n", **pp); //=a 解引用两次
    1. //二级指针做参数,被用来接收指针的地址
    2. void get_memory2(char **p){
    3. *p = (char *)malloc(100); // *p 一级解引用,就是指针str
    4. }
    5. int main(){
    6. char * str = NULL;
    7. get_memory2(&str); //传指针(实参)的指针 如果只传str,那其实就是传的普通的地址,&str就是传的指针的地址。
    8. printf("%s\n",str);
    9. free(str);
    10. str = NULL;
    11. return 0;
    12. }
    13. int func1(int x); //传实参的值,不能修改实参
    14. int func2(int *x); //传实参指针,修改实参
    15. int func3(int &x); //传实参引用,修改实参
    16. int func4(int **x); //实参是指针,传指针的指针,修改指针
    17. int func5(int *&x); //实参是指针,传指针的引用,修改指针
    18. //分析下面程序的运行结果
    19. int i=0,j=20,*p1 =&i,*p2=&j;
    20. void f(int **ptr1,int *ptr2){
    21. int *tmp = ptr2;
    22. **ptr1 *=10;
    23. *ptr2 *=10;
    24. ptr2 = *ptr1;
    25. *ptr1 = tmp;
    26. }
    27. f(&p1,p2);
    28. //请问i,j,p1,p2的值各是多少?
    29. //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个字节

    1. int a,b,c,d;
    2. a = b = 10; //a和b得到值10
    3. c = ++a; //a增加至11,c得到a拷贝的值11
    4. d = b++; //d得到b拷贝的值10,然后b增加到11
    5. //前缀和后缀形式的增值操作符都复制一份变量值的拷贝,用于周围表达式的值正式这个拷贝的值。

    逻辑操作符:&& 和 ||
    先对左边的操作进行运算,符合条件则不会再运算右边的操作。这种行为被称为“短路求值

    .->操作符用于访问一个结构的成员。
    如果s是个结构变量,那么s.a就访问s中命为a的成员。
    当你拥有一个指向结构的指针而不是结构本身,且欲访问它的成员时,就需要使用->操作符而不是.操作符

    1. int func(void){
    2. static int counter = 1; //static 改变了存储区域,存储到外部静态区,第二次调用定义初始化则无效
    3. //counter = 2; //但是可以进行赋值,如果在此处赋值,则不管调用几次,输出都是3
    4. return ++counter;
    5. }
    6. int main(){
    7. int answer;
    8. int a = test15_func();
    9. int b = test15_func();
    10. int c = test15_func();
    11. answer = a - b * c;
    12. printf("a=%d\n", a); //2
    13. printf("b=%d\n", b); //3
    14. printf("c=%d\n", c); //4
    15. printf("answer=%d\n",answer); //-10
    16. }