欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 旅游 > C语言指针2

C语言指针2

2025/4/2 8:19:53 来源:https://blog.csdn.net/2302_82164115/article/details/146770478  浏览:    关键词:C语言指针2

文章目录

    • 指针与数组
    • 指针访问数组
    • 一维数组传参的本质
    • 冒泡排序
    • 二级指针
    • 指针数组
    • 指针数组模拟二维数组
    • 字符指针
    • 数组指针
    • 二维数组传参的本质
    • 函数指针
    • 函数指针数组
    • qsort函数

指针与数组

数组的名字就是数组首元素地址。

特殊情况

  1. sizeof(数组名),这里的数组名是一整个数组
  2. &数组名,取出的是整个数组的地址
int main()
{int arr[4] = { 0 };printf("&arr[0]       = %p\n",&arr[0]);//取首元素地址printf("&arr[0] + 1   = %p\n", &arr[0] + 1);//首元素地址加1printf("arr           = %p\n", arr);//数组名等价于数组首元素地址printf("arr+1         = %p\n", arr + 1);printf("&arr          = %p\n", &arr);//取整个数组地址printf("&arr + 1      = %p\n", &arr + 1);return 0;
}

在这里插入图片描述

指针访问数组

指针指向数组后,指针加一个i就是指向了这个数组的第i+1个元素(与索引相同),也可以写作P[i];

  • ∗ ( P + i ) = ∗ ( a r r + i ) = a r r [ i ] = P [ i ] *(P + i) = *(arr + i) = arr[i] = P[i] (P+i)=(arr+i)=arr[i]=P[i]

一维数组传参的本质

void test(int arr[])
{int sz2 = sizeof(arr) / sizeof(int);printf("sz2 = %d\n",sz2);
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };int sz1 = sizeof(arr) / sizeof(int);printf("sz1 = %d\n",sz1);test(arr);return 0;
}

在这里插入图片描述

数组传参的本质是传递数组首元素地址,数组形参的部分是不会真正创建数组的,所以不需要创建数组大小,所以一般传递数组的函数都需要传递数组元素。

冒泡排序

void buble_sort(int* p,int sz)
{int i = 0,j = 0;for (i = 0; i < sz - 1; i++){int flag = 1;//有序数组假设for (j = 0; j < sz - i - 1; j++){if (p[j] > p[j + 1]){int temp= 0;temp = p[j];p[j] = p[j + 1];p[j + 1] = temp;flag = 0;}}if(flag)//如果有序,就结束排序break;}
}void print(int* p, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ",*(p+i));}printf("\n");
}int main()
{int arr[10] = { 9,1,2,3,4,5,6,7,8,0 };int sz = sizeof(arr) / sizeof(int);print(arr,sz);buble_sort(arr, sz);print(arr,sz);return 0;
}

二级指针

指针变量也是变量,他自己也有地址,所以也可以用指针指向指针变量,指向指针变量的指针叫做二级指针

int a = 10;
int* pa = &a;
int** ppa = &pa;

这里的pa存放了a的地址,而ppa存放了pa的地址

再迁移知识,如果我们要访问pa的地址

&pa;//法一
ppa;//法二

如果我们要访问pa存放的值(a的地址)

pa;//pa本身的值
*ppa;

如果我们要访问a的值

a;
*pa;
**ppa;//外层解引用,得到pa这个指针,再解引用得到a的值

指针数组

存放指针的数组,(存放地址的数组)

int* arr[2] = {addr1,addr2};
int main()
{int a = 10;int b = 11;int c = 12;int* arr[3] = {NULL};arr[0] = &a;arr[1] = &b;arr[2] = &c;for (int i = 0; i < 3; i++){printf("arr[%d] = %p\n",i,arr[i]);printf("*arr[%d] = %d\n",i,*arr[i]);}return 0;
}

在这里插入图片描述

指针数组模拟二维数组

int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* arr[] = { arr1,arr2,arr3 };return 0;
}//这并非真正的二维数组,因为空间不连续

字符指针

当字符串赋给字符指针时,其实是把字符串的首地址给了指针

因为字符串的每一个字符地址连续,而指针只能同时维护一个同类型的空间大小的栈。

#include <stdio.h>int main(){char str1[] = "hello bit.";char str2[] = "hello bit.";const char *str3 = "hello bit.";const char *str4 = "hello bit.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;}
运行结果为
str1 and str2 are not same
str3 and str4 are same

原因:str1与str2都是数组首地址,虽然接受同样的内容,但是同样的内容存放在不同的栈空间。

​ str3 与str4是指向常量的指针,也就是说str3与str4都指向相同的空间

数组指针

数组指针顾名思义就是一个指针,只是这个指针维护的空间大小是整个数组,而不是数组元素的类型,也就是说,数组指针加减1就直接跳过一个数组的长度。

int main()
{int arr[10] = { 0 };int (*p)[10]  = &arr;return 0;
}

这里这里的指针类型怎么理解。

这里的p依然指向数组首地址,但是维护的空间变为了int [10],这是一个数组类型,也就是说p维护了一个数组长度的空间,还是和前面讲指针类型是一样,int [10]是指向的元素类型,*表示它是一个指针。

二维数组传参的本质

二维数组传参的本质其实是将一个数组指针传递了过去,这样指针加1就会跳过一维数组的长度

arr[i][j]

我们知道 [ i ] [i] [i]这个操作符是将指针改为 ∗ ( p t r + i ) *(ptr + i) (ptr+i)的形式,那这里的arr就是一个数组指针指向有[j]个元素宽度的数组,arr[i]就是*(arr + i)用来选行,而解引用后得到了第i行的数组,[j]再将器转换为 *( *(arr + i) + j)用来选列,最外层就是对单个元素的解引用。再和数组指针联系,上一个例子中,p就相当于这里的arr,而p+i就相当于 arr + i,余下结尾相同类比

函数指针

函数也是用地址的,所以函数也能用指针指向

同样函数的名称就是一个地址,与数组一样,

int (*p)(参数列表)

与数组指针类比记忆,这里的p指向的元素类型是 int(参数列表)类型

那么我们只需要对指针进行解引用,结合普通函数使用无异。

int add_fun(int x,int y)
{return x + y;
}int main()
{int (*p)(int ,int) = add_fun;printf("%d",(*p)(20,30));return 0;   
}
(*(void (*)())0)();
void (*signal(int , void(*)(int)))(int);
//解释
(void (*)())是一种强制类型转换
(void (*)())0是将0作为指针(地址),也就是假设0x00000...00有一个函数,这里的地址我作为访问它的指针
*(void (*)())0是解引用
(*(void (*)())0)();调用函数void (*signal(int , void(*)(int)))(int);是一个函数指针,它的返回类型是void ,参数为int
signal(int , void(*)(int))这个函数指针的名字,而这个名字是一个函数调用,说明次函数返回的是一个地址或指针变量

函数指针数组

函数指针数组顾名思义就是存放函数指针的数组

他的用途一般是用来实现转移表

转移表是在需要频繁实现函数跳转时的使用时,可以用函数指针数组的索引将函数的调用转换为索引值的改变。

qsort函数

qsort函数,可以实现对任意类型的数组进行排序,它需要包含头文件<stdlib.h>

qsort(void* base,int num,int size,int(*cmp)(void *e1,void *e2))
  • 第一个参数是数组,无论什么类型的数组都能用void型的指针指向
  • 第二个参数是 数组元素个数
  • 第三个参数是数组元素的长度
  • 第四个参数是一个函数指针,这个指针指向的函数需要我们自己实现,也很简单,只要第一个指针e1指向的值大于第二个指针e2指向的值就返回大于1的数,等于则返回0,小于则返回小于0的数,则qsort会帮助我们自动排序,而qsort的底层实现其实也可以自己写,比如用冒泡排序,选择排序等等,但是库函数中已经为我们定义好了这个函数,直接用就行。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词