指针的学习

本文详细介绍了C语言中的指针概念,包括内存、地址、指针变量、指针类型、指针运算、指针数组、数组指针、函数指针以及在数组、字符串、函数调用中的应用。还探讨了回调函数、内存管理和排序算法中的指针使用。此外,文章通过实例解释了指针在数组参数传递和二维数组操作中的作用。
摘要由CSDN通过智能技术生成

指针

内存会划分为小的内存单元,每个内存单元都会有一个编号,这个编号被称为地址。这个地址也叫指针。

内存单元 = 地址 = 指针

指针或者地址,要存储,就可以放到:指针变量中。

指针的大小是固定的4/8个字节(32/64位平台)

指针是有类型,指针的类型决定了指针的+整数的步长,指针解引用的时候的权限。

字符指针

int main()
{
  char ch ='w';
  char * pc =&ch;
  *pc = 'a';
  print("%c\n",ch);//a
  return 0;
}
int main()
{
  const char * p ="abcdef";
  //p是指针变量,在x86环境下,是四个字节。
  // 将“abcdef”常量表达式的起始字符的地址放在p里面
  print("%c",*p);//a
  print("%s\n",p);//打印字符串给与首字符地址就可以了
  //"abcdef"是常量字符串不能被修改
  //*p = w;会崩溃
  
}
int main()
{
  const char * p1 = "abcdef";
  const char * p2 = "abcdef";
  char arr1[] = "abcdef";
  char arr2[] = "abcdef";
  if( p1 == p2)
  {
	printf("p1 == p2\n")
  }
  else
  {
    printf("p1 !=p2\n")
  }
  if(arr1 == arr2)
  {
	printf("arr1 == arr2");
  }
  else 
  {
    printf("arr1 != arr2");
  }
  
  return 0;
}

指针数组

指针数组:存放指针的数组

整形数组:存放整形的数组

字符数组:存放字符的数组

int main()
{
  //int * arr[10];//存放整形指针的数组
  //char * ch[10];//存放字符指针的数组
  int a = 10;
  int b = 20;
  int c = 30;
  int * p1 = &a;
  int * p2 = &b;
  int * p3 = &c;
  
  int * arr[3] = {&a,&b,&c}; //arr就是一个指针数组
  for(i = 0;i<3;i++)
  {
     printf("%d",* (arr[i]));
  }
  return 0;
}
int main()
{
	int arr1[5] = {1,2,3,4,5};
  	int arr2[5] = {2,3,4,5,6};
    int arr3[5] = {3,4,5,6,7};
    
  	int * parr[3] ={arr1,arr2,arr3};
  	
  	for(int i = 0;i<3;i++)
    {
      for(int j = 0; j<5; j++)
      {
        printf("%d ",parr[i][j]);
        //printf("%d ",parr[i]+j);
      }
      printf("\n");
    } 
  	//parr 是指针数组
}

数组指针

是指针,是指向数组的指针

int main()
{
  int a =10;
  int * p ==&a;//整形指针 - 指向整形的指针,存放整形地址的指针
  char ch ='w';
  char * pc = &ch;//字符指针 - 指向字符的指针,存放字符地址的指针
  
  //数组指针 - 指向数组的指针
  
  int * p1[10]; //p1是数组,指针数组
  int (*p2)[10]//p2现在是指针;指向一个10个元素的数组,每个元素是int
  
}
int mian()
{
  int a = 10;
  int * p = &a;
  int arr[10] = {0};
  //数组名是首元素地址;
  //&arr 取出的是数组的地址
  printf("%p\n",arr);//int* +1 = +4个字节
  printf("%p\n",&arr[0]);//int* +1 = +4 个字节
  printf("%p\n",&arr);//+1 直接跨越整个数组
  // int (*p)[10] == &arr;
  //p是一个指针,指向了数组
  //所以p叫一个数组指针
}

数组名:

通常情况下,数组名都是数组首元素的地址。

有两个例外:

1.sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小

2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

数组指针的应用

void print1(int arr[],int sz)
{
  for (int i = 0;i<sz;i++)
  {
    printf("%d ",*(arr+i));
  }
}
void print2(int(*p)[10],int sz)
{
  for (int i = 0;i<sz;i++)
  { 
    //*p相当于数组名,数组名又是首元素的地址,所以*p就是&arr[0]
    printf("%d",*(*p+i));
  }
}
int main()
{
  int arr[10] = {1,2,3,4,5,6,7,8,9,10};
  int sz = sizeof(arr)/sizeof(arr[0]);
  print1(arr,sz);
}
void print2(int arr[3][5],int c,int r)
{
  for(int i = 0;i < c;i++)
  {
    for(int j =0;j<r;j++)
    {
      printf("%d",arr[i][j]);
    }
  }
}
int print3(int (*p)[5],int c ,int r)
{
  int i = 0;
  for (i = 0;i<c;i++)
  {
    for(int j =0;j<r;j++)
    {
      //p+i是指向第i行的
      //*(p+i)相当于拿到了第i行,也相当于第i行的数组名
      //数组名表示首元素的地址,*(p+i)相当于第i行的第一个元素的地址
      printf("%d",*(*(p+i)+j));
      //printf("%d",p[i][j]);
    }
  }
}
int main()
{
  int arr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
  //二维数组的首元素是{1,2,3,4,5},五个元素的数组
  //写一个函数,打印arr数组
  print2(arr,3,5);
  return 0;
}
int arr[5]         //arr是一个整形的数组,每个元素是int类型的,有5个元素
int* parr1[10]    //指针数组,数组10个元素,每个元素的类型是int*
int(*parr2)[10]  //数组指针  指向数组的指针,指向的数组有10个元素,每个元素的类型是int
int (*parr3[10])[5];    //parr3 是一个数组,数组有10个元素,每个元素是int(*)[5] 存放数组指针的数组

数组参数、指针参数

一维数组传参

void test(int arr[10])
{
  pass;
}
void test(int arr[]) //一维数组形参部分可以省略
{}
void test(int arr[100])//不建议,但是没错
{}
void test(int * p)
{}
void test2(int* arr2[])
{}
void test2(int** p)
{}
int main(){
  int arr[10] = {0};
  int *arr2[20] = {0};
  test(arr);
  test2(arr2)
}

二维数组传参

形参写成数组的形式可以的

二维元素的数组名是第一行的数组地址

二级指针是用来存放一级指针的地址

数组指针是用来存放数组的地址的

void test(int arr[][5])//行可以省略,列不可以省略
{}
void test(int (*p)[5])
{}
int main()
{
  int arr[3][5] = {0};
  test(arr);
}

一级指针传参

函数的参数为一级指针,可以为:一维数组名,一级指针,一维数组的地址

#include<stdio.h>
void test(int * p,int sz)
{
  for(int i = 0; i <sz; i++ )
  {
    printf("%d",*(p+i))
  }
}
int mian()
{
  int arr[10] ={1,2,3,4,5,6,7,8,9,10};
  int * p =&arr;
  int n =sizeof(arr)/sizeof(arr[0]);
  test(p,sz)
  return 0;
}

二级指针传参

void test(char** ppc)
{
  
}
int main()
{
  char ch = 'w';
  char * pc = &ch;
  char ** pc = &pc;
  char* arr[4];
  test(arr);
  test(&pc);
  test(ppc)
}
//int main()
//{
//  char a = 'w';
//  char * pa =&a;
//  char** ppa =&pa;//ppa就是一个二级指针
//  
//  test(ppa);

//}
void test1(int(*p)[5])
{}
void test2(int(*p)[3][5])
{
  *p;// 第一行的地址
}
int main()
{
  int arr[3][5];
  test1(arr);     //传递的第一行的地址
  test2(&arr);    //传递的是整个二维数组的地址
}

函数指针

函数指针:指向函数的指针

int Add(int x ,int y)
{
  return x+y;
}
int test(char* str)
{
  ;
}
int main()
{
  int arr[10];
  int (*p)[10] =&arr; // p是一个数组指针变量
  printf("%p\n",&Add);//函数的地址
  printf("%p\n",Add);//函数的地址    写法不同,&Add与Add含义一模一样
  int (*pf)(int,int) = Add;//pf就为函数指针变量
  int ret = (*pf)(2,3);
  printf("%d",ret); //5
  int rets = pf(2,3);  //*的含义并不重要
  printf("%d",rets); //5
  int (*pt)(char*) = test;
  
  return 0;
}
(*(void(*)())0)(); 
// void(*)() 是函数类型
// (void(*)())强制类型转换
// (*(void(*)())0)()
//1.首先吧0强制转换成为一个函数指针类型,这意味着0地址处放一个返回类型是void,无参的一个函数
//2.调用0地址处的这个函数
void (* signal(int,void(*)(int)))(int);
//signal是一个函数的声明
//signal函数的参数,第一个是int类型,第二个是为void(*)(int)的函数指针类型
//函数返回类型为函数指针 void(*)(int)
typeof void(* pf_t)(int);
//pf_t 即为 signal(int,pf_t)

函数指针数组

int Add(int x,int y)
{
  return x + y;
}
int Sub(int x,int y)
{
  return x - y;
}
int Mul(int x,int y)
{
  return x * y;
}
int Div(int x,int y)
{
  return x / y;
}
int main()
{
  //指针数组
  //字符指针数组
  char* arr[5];
  //整形指针数组
  int* arr2[4];
  //函数指针数组
  int (*pf1)(int,int) = Add;
  int (*pf2)(int,int) = sub;
  int (*pf3)(int,int) = Mul;
  int (*pf4)(int,int) = Div;
  
  int  (*pf[4])(int,int)={Add,Sub,Mul,Div};//函数指针数组
  
  for(int i =0;i<4;i++)
  {
    int ret = pf[i](8,2);
    printf("%d",ret)
  }
  return 0;
}

指向函数指针数组的指针

int Add(int x,int y)
{
  return x+y;
}
int Sub(int x,int y)
{
  return x - y;
}
int Mul(int x,int y)
{
  return x * y;
}
int Div(int x,int y)
{
  return x / y;
}
int mian()
{
  // int arr[10] = {1,2,3,4,5,6,7,8,9,10}
  // int (*p)[10] = &arr; //p是数组指针
  // int* arr2[5]  指针数组
  // int*(*p2)[5]  指向指针数组的指针
  int (*pf)(int,int) = &Add;   //函数指针
  int (*pfarr[4])(int,int) = {Add,Sub,Mul,Div};    //函数指针数组
  int (*(*p3)[4])(int,int) = &pfarr;  //指针:指向函数指针数组的指针
  //p3是一个指针变量,p3指向函数指针数组的指针
  //*p3 --> pfarr
  //(*p3)[i] -->pfarr[i]
  //32位 4个字节    64位 8个字节
  return 0;
}
int Add(int x,int y)
{
  return x+y;
}
int Sub(int x,int y)
{
  return x - y;
}
int Mul(int x,int y)
{
  return x * y;
}
int Div(int x,int y)
{
  return x / y;
}
int main()
{
  int(*parr[4])(int,int) = {Add,Sub,Mul,Div};
  int(*(*p3)[4])(int,int) =&parr;//p3是一个函数指针数组的指针
  for (int i = 0;i<4;i++)
  {
    //*p3
    //*(p3+0)
    //p3[0]
    //p3[0][i]
    //p3是整个数组的地址,&parr是取出的整个数组的地址
    int ret = (*p3)[i] (3,4);
    printf("%d\n",ret);
  }
  //7,-1,12,0
}

回调函数

回调函数是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特地的事件或条件发生时由另外的一方调用的,用于该事件或条件进行响应。

void test()
{
  printf("hehe");
}
void print_hehe(void(*p)())
{
  if(1)
    p();
}
int main()
{
  print_hehe(test);//hehe
  return 0;
}
int Add(int x,int y)
{
  return x+y;
}
int Sub(int x,int y)
{
  return x - y;
}
int Mul(int x,int y)
{
  return x * y;
}
int Div(int x,int y)
{
  return x / y;
}
void calc(int(*p)(int,int))  // 函数指针的参数必须一样
{
  int x = 0;
  int y = 0;
  scanf("%d %d",&x,&y);
  ret = pf(x,y);
  printf("ret = ",ret);
}
void menu()
{
  printf("0.exit 1.sub 2.Sub 3.Mul 4.Div");
}
//计算器
int mian()
{
  int input = 0;
  do
  {
    menu();
    printf("请选择");
    scanf("%d",&input);
    switch(input)
    {
      case 1:
        calc(Add);
        break;
      case 2:
 		calc(Sub);
      	break;
      case 3:
        calc(Mul);
      	break;
      case 4:
        calc(Div);
        break;
    }
  }while(input);
}

qsort函数的实现

qsort是一个库函数,基于快速排序算法实现的一个排序算法

#include<stdio.h>
void bubble_sort(int arr[],int sz)
{
  for(int i =0 ; i<sz;i++)
  {
    for(int j = 0;j<sz-1-i;j++)
    {
      if arr[j]>arr[j+1]
      {
        int temp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = temp;
      }
    }
  }
}
void print_arr(int arr[],int sz)
{
  for(int i = 0;i<sz;i++)
  {
    printf("%d",arr[i]);
  }
}
int main()
{
  int arr[] = {9,8,7,6,5,4,3,2,1,0};
  int sz = sizeof(arr) / sizeof(arr[0])//排序为升序
  bubble_sort(arr,sz);
  print_arr(arr,sz);
}
int main()
{
	int a =10;
	void* pf =&a;  //void类型的指针可以接受任何类型的指针
	
	return 0;
}
void qsort(void * base,size_t num,size_t width,int(*cmp)(const void*e1,const void * e2))
{
  //void * base 待排序数据的起始位置
  //size_t:无符号整形
  // num:数组的个数
  // width:一个元素的字节大小
  //cmp :比较函数,要求qsort的使用者自定义一个比较函数
  //排序的整形数据:用>,<
  //排序的结构体数据:不方便直接使用>,<比较了
  //使用者根据实际情况,提供一个函数,实现2个数据的比较 
  //e1,e2:待比较的两个元素的地址
  //返回值:int 0,>0,<0
  void test1()
  {
    int arr[] = {9,8,7,6,5,4,3,2,1,0};
  	int sz = sizeof(arr) / sizeof(arr[0]);
  	//排序为升序
  	bubble_sort(arr,sz);
  	print_arr(arr,sz);
  }
  int cmp_int(const void *e1,const void * e2)
  {
    //void是无确切类型的指针,不可以直接解引用
    if (*(int*)e1 > *(int*e2))
      return 1;
    if (*(int*)e1 = *(int*e2))
      return 0;
     if (*(int*)e1 < *(int*e2))
      return -1;
    //return (*(*int)e1-*(*int)e2)
  }
  void test2()
  {
    int arr[] = {9,8,7,6,5,4,3,2,1,0};
  	int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr,sz,sizeof(arr[0]),cmp_int)
  }
  struct Stu
  {
    char name[20];
    int age;
    double score;
  };
  int cmp_stu_by_age(const void *e1,const void *e2)
  {
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
  }
  int cmp_stu_by_name(const void * e1,const void* e2)
  {
    return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
  }
  void Swap(char* buf1,char* buf2,int width)
  {
    for(int i = 0 ;i<width;i++)
    {
      char temp = *buf1;
      *buf1 = *buf2;
      *buf2 =temp;
      *buf1++;
      *buf2++;
    }
  }
  void bubble_sort(void* base,int num,int width,(*cmp)(const void *e1,const void * e2))
  {
    for(int i = 0 ,i<num-1;i++)
    {
      for(int j = 0;j < sum-1-i;j++)
      {
        if(cmp((char*)base+width*j,(char*)base+width*(j+1))>0)
        {
          //交换
          swap((char*)base+width*j,(char*)base+width*(j+1),width);
        }
      }
    }
    
  }
  
  //使用qsort排序结构体
  void test3()
  {
    struct Stu arr[3] = {{"zhangsan"20,55.5}{"lisi",30,88.0},{"wangwu",10,90.0}};
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_age);
    //qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);
    bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);
  }
}

面试题

sizeof:操作符,计算的是对象所占内存的大小-单位是字节,size_t。不在乎内存中存放的是什么只在乎内存的大小

strlen:库函数,求字符串长度,从给定的地址向后访问字符,统计\0之前出现的字符个数

1.数组名通常来说是数组首元素的地址

但是有两个例外:

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小

2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

int main()
{
  int a[] = {1,2,3,4}
  printf("%d",sizeof(a)); // 16
  printf("%d\n",sizeof(a+0)); //4/8  a+0是数组第一个元素的地址,是地址大小就是4或8个字节
  printf("%d",sizeof(*a));    // 4 a:表示首元素地址 *a:表示第一个元素
  printf("%d",sizeof(a+1));   // 4/8  a+1 数组第二个元素的地址
  printf("%d",sizeof(a[1]));  // 4 计算的是第二个元素的大小
  printf("%d",sizeof(&a));    // &a取出的是数组的地址,数组的地址也是地址,4或者8个字节
  printf("%d",sizeof(*&a));   // 16 &a取出的是整个数组的地址,*&a解引用得到的是整个数组
  printf("%d",sizeof(&a+1));  // 4/8  &a取出的是整个数组,+1跳过整个数组,产生的4后边位置的地址
  printf("%d",sizeof(&a[0])); //4/8 取出的是数组第一个元素的地址
  printf("%d",sizeof(&a[0]+1));  // 4/8取出的是数组第二个元素的地址
}
// 字符数组
// strlen()  的输入是指针
int main()
{
  char arr[] = {'a','b','c','d','e','f'};
  printf("%d\n",sizeof(arr));             //不要与strlen()搞混。 6
  printf("%d\n",sizeof(arr+0));           //4/8 取出首元素的地址
  printf("%d\n",sizeof(*arr));            //1 首元素
  printf("%d\n",sizeof(arr[1]));          //1 首元素
  printf("%d\n",sizeof(&arr));            //4/8  取出的是整个数组的地址
  printf("%d\n",sizeof(&arr+1));       //4/8  &arr+1是从数组地址开始向后跳过了整个数组产生的一个地址
  printf("%d\n",sizeof(&arr[0]+1));       //4//8 数组第二个元素的地址
  printf("%d\n",strlen(arr));   // 随机值,遇到‘\0’才停止
  printf("%d\n",strlen(arr+0)); // 随机值,arr+0还是数组首元素的地址 
  printf("%d\n",strlen(*arr));// err arr是首元素地址,*arr就是数组的首元素,就为a,'a'-97,野指针
  printf("%d\n",strlen(arr[1])); //err  'b' -98
  printf("%d\n",strlen(&arr));   //随机值 
  printf("%d\n",strlen(&arr+1)); //随机值
  printf("%d\n",strlen(&arr[0]+1)); //随机值
  return 0;
}
int main()
{
  char arr[] = "abcdef"
  printf("%d\n",sizeof(arr));          // 7 计算的是整个数组的大小包括'\0'
  printf("%d\n",sizeof(arr+0));        // 4/8 arr+0 是数组首元素
  printf("%d\n",sizeof(*arr));         // 1   arr数组的首元素
  printf("%d\n",sizeof(arr[1]));       // 1   arr的第二个元素
  printf("%d\n",sizeof(&arr));         // 4/8 &arr数组的地址,地址大小就是4/8
  printf("%d\n",sizeof(&arr+1));       // 4/8 &arr+1 是\0后面的地址 
  printf("%d\n",sizeof(&arr[0]+1));    // 4/8  -&arr[0]  +1 是数组第二个元素的地址
  printf("%d\n",strlen(arr));   // 6 遇到\0停止
  printf("%d\n",strlen(arr+0)); // 6
  printf("%d\n",strlen(*arr));// err arr是首元素地址,*arr就是数组的首元素,就为a,'a'-97,野指针
  printf("%d\n",strlen(arr[1])); //err  'b' -98
  printf("%d\n",strlen(&arr));   //6 
  printf("%d\n",strlen(&arr+1)); //随机值
  printf("%d\n",strlen(&arr[0]+1)); //5
  return 0;
}
int main()
{
  char* p = "abcdef";   //abcdef是常量字符串,p存储的是a的地址
  printf("%d\n",sizeof(p));//4/8
  printf("%d\n",sizeof(p+1));// 4/8   p+1存放b的地址
  printf("%d\n",sizeof(*p)); //  1    - *p就是'a'
  printf("%d\n",sizeof(p[0])); // 1      p[0]  *(p+0) ->*p
  printf("%d\n",sizeof(&p));//   4/8   &p是指针变量p在内存中的地址
  printf("%d\n",sizeof(&p+1));  // 4/8    &p+1 跳过p之后的地址
  printf("%d\n",sizeof(&p[0]+1)); //4/8  取出的是b的地址
  printf("%d\n",strlen(p));  //   6  p就是a的地址
  printf("%d\n",strlen(p+1));  // 5  p+1 从b开始
  printf("%d\n",strlen(*p));  // err 野指针
  printf("%d\n",strlen(p[0]));  //err  野指针
  printf("%d\n",strlen(&p));  // 随机值  
  printf("%d\n",strlen(&p+1));  //随机值
  printf("%d\n",strlen(&p[0]+1));  //5    &p[0]就是a的地址
}
int main()
{
  int a[3][4] = {0};
  printf("%d\n",sizeof(a));  //a是整个数组 12*4=48字节
  printf("%d\n",sizeof(a[0][0]));  //  4字节 第一行第一个元素的大小
  printf("%d\n",sizeof(a[0]));  // a[0]是第一行的数组名,计算的是第一行的元素 16字节
  printf("%d\n",sizeof(a[0]+1));  // a[0]作为第一行的数组名。并没有单独放在sizeof内部或者取地址
  //a[0]就是数组首元素的地址,就是第一行第一个元素的地址,a[0]+1就是第一行第二个元素的地址
  printf("%d\n",sizeof(*(a[0]+1))); //  4个字节 
  printf("%d\n",sizeof(a+1));//a表示首元素的地址,a是二维数组,首元素的地址就是第一行的地址,
  //a+1就是第二行的地址  4/8个字节
  printf("%d\n",sizeof(*(a+1));  //16 *(a+1)->a[1]对第二行解引用
  printf("%d\n",sizeof(&a[0]+1));  // 4/8 -a[0]取出的是第一行的数组名,&a[0]取出的是第一行的地址 
  printf("%d\n",sizeof(*(&a[0]+1)));  //16 - 第二行的地址解引用就是第二行
  printf("%d\n",sizeof(*a));  // 16字节 a就是首元素的地址,就是第一行的地址 *a就是第一行
  printf("%d\n",sizeof(a[3]));  // 16个字节  并没有访问
  //arr[i]  -> * (arr+i)
}
struct Test
{
  int Num;
  char* pc Name;
  short sDate;
  char cha[2];
  short sBa[4];
}* p;
//假设p 的值为0x100000,如下的表达式的值分别是多少?
//已知 ,结构体Test类型的变量大小是20字节
int main()
{
  p = (struct Test*)0x100000;
  
  printf("%p\n",p + 0x1);  //相当于+20,16进制,  00100014  
  printf("%p\n",(unsigned long)p + 0x1); // 强制转换为整形类型,不为指针,+1就加一个字节 00100001
  printf("%p\n",(unsigned int*)p + 0x1); //整形指针,+1,跳过四个字节 00100004
  
}
int main()
{
  int a[4] = {1,2,3,4};
  int* ptr1 = (int*)(&a+1);
  int* ptr2 = (int*)((int)a+1);
  printf("%x,%x",ptr1[-1],*ptr2);//1 02 00 00 00->20000000
  return 0;
}
#include<stdio.h>
int main()
{
 int a[3][2] = {(0,1),(2,3),(4,5)};//逗号表达式,1,3,5  
 int * p;
 p =a[0];
 printf("%d",p[0]);
 return 0;
}
int main()
{
  int a[5][3];
  int(*p)[4];
  p = a;
  printf("%p %d\n",&p[4][2]-&p[4][2],&p[4][2]-&a[4][2]);//指针减指针是元素的个数 -4
  //FFFFFFFC  -4
  
  return 0;
}
int main()
{
  int aa = {1,2,3,4,5,6,7,8,9,10 };
  int * ptr1 = (int*)(&aa+1);
  int * ptr2 = (int*)(*(aa+1));
  printf("%d %d",*(ptr1-1),*(ptr2-1)); //10  5
  return 0;
}
#include <stdio.h>
int main()
{
  char* a[] = {"work",'at','alibaba'}; 
  // work\0 at\0 alibaba\0
  //a数组有三个元素 a[0]是work\0的w的地址,a[1]是at\0的a的地址,a[2]是alibaba\0的a的地址
  //pa指向a[0]
  char** pa = a;
  pa++;
  printf("%s\n",*pa);//at
  return 0;
}
int main()
{
  
  
  return 0;
}
int main()
{
  char* c[] = {"ENTER","MEW","POINT","FIRST"};
  char** cp[] = { c + 3,c + 2,c + 1,c};
  char*** cpp = cp;
  // c : E N P F
  // cp :F P N E
  // cpp:F
  printf("%s\n",**++cpp); //POINT
  printf("%s\n",*-- * ++cpp + 3);   //ER
  printf("%s\n",*cpp[-2]+3);  //ST
  printf("%s\n",cpp[-1][-1]+1); //EW
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

love2study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值