指针&函数&数组&const

本文深入讲解C语言中的指针、函数、数组等概念,并探讨它们的组合应用,如函数指针、指针数组等,同时介绍了Linux下的cdecl工具。

C语言中指针,函数,数组,const变量,单独来说,大家都不陌生.
但是,几个简单的事物组合起来,往往会生成奇异的变化。

基础介绍
指针是什么?
指针就是一个变量,它存储着指向变量的地址
函数是什么?

函数就是一个有着形参(可选),有着返回值(可选),有着名字(函数名)的代码块,这个代码块根据我们的需求实现了一定的功能,例如:

int max(int a, int b) {
     if(a > b)
          return a;
     return b;
}

一些说明:
 这里max()就是一个函数
 返回值:一个int型的变量
 形参:两个int类型的变量
 函数名:max
 功能:获取两个整形变量中最大值

数组是什么?
数组就是一个变量。这个变量可以存储指定数量,指定类型的值。例如:

int    a[3];//定义了一个名字为a,可以容纳3个整形值的数组(整型数组)
char    c[15];//定义了一个名字为c,可以容纳15个字符的数组(字符数组)

所谓const
const可以看做constant的缩写,就是声明一个常量,也就是只能读,不能改变,例如:

int const a=2;  //a此时是一个整形常量,它的值为2,只能读取,不能改变
a=3;    //Error,编译时,编译器会报错
嗯,貌似看起来很简单,应该可以一气呵成吧
确实是这样的,但是实际应用的时候,我们往往组合起来使用:
  • 函数指针,指针函数
  • 数组指针,指针数组
  • 常量指针,指针常量

函数指针和指针函数

函数指针
首先它是一个指针,然后,它指向一个函数,例如:
   int (*p)(int, int);    
   //这样,我们就定义了一个函数指针
   //它指向返回值为int,参数为两个int的函数

我们可以这样使用它:

   p=max;       //把max函数地址赋给p
   p=&max;    //把max函数地址赋给p
   /*这两种方法都是okay的,有点疑惑是吧,待会解释*/

定义一个整型变量,用来存储最大值:

   int max_value;

正常函数调用:

   max_value = max(2, 5);

好咯,我们开始使用函数指针:

   max_value = p(2, 5);       //就像函数调用[max(2,5))]一样使用它
   max_value = (*p)(2, 5);   //推荐的函数指针使用方法,与函数调用形成区别

好了,开始解释疑惑啦,你可能很纳闷为什么p=max和p=&max效果一样?

max是函数名,当max参与函数调用,或者直接赋值给函数指针时,它会自动转化成>函数指针常量;
而当你使用取地址符时,编译器会把它当作函数名,取出该函数的地址。

指针函数
啊,不要被吓到哦,它就是一个返回值为指针的函数
呵呵,是不是too simple了?
也举个例子吧:
  char *get_my_name();   //这就是一个指针函数

数组指针和指针数组

数组指针
指向数组的指针变量

指针数组
一个数组,数组的元素是指针
来个例子吧:
       int   (*arr1)[5];           //数组指针,指向一个数组容量为5的整形数组
       int    *arr2[5];           //指针数组,一个可以容纳5个整形变量的数组

定义一个二维数组吧:

       int    arr[3][5];           //一个行为3,列为5的整型数组
       //其实,也可以看做有着一个容量为3一维数组,
       //只是这个容量为3的一维数组的每一个元素都是一个容量为5的一位数组

二维数组指针和二维数组:

       arr1=arr[2];

那么,arr1[i] == arr[2][i]
还有一点需要注意的

       arr1 = arr1+1;
       //此时,arr1[i] == arr[3][i],也就是指针向后移动了1*5*sizeof(int)个字节

常量指针和指针常量

常量指针
一个指向常量的指针
也就是说这个指针指向的值不能改变
例如:
       int  zz=5;
       int  const * p=&zz;           .//常量指针
       *p = 6;           //Error:向只读位置‘*p’赋值,编译器会报错

但是

       int   d=4;
       p = &d;           //重新指向另一个指针

总的来说:常量指针指向的变量的值不能通过长里常量指针改变,但是常量指针的值却是可以改变的,即指向的位置可以改变。

指针常量
也是一个指针,不过其指针不能改变,但是指针指向的值可以改变。
你可以与常量指针对比来着
例如:
       int  * const pp=&zz;           //指针常量,指向整型变量zz
       *pp = 1124;           //zz的值被改变,变为1124
       pp=&d;           //Error:向只读变量‘p’赋值, 编译器报错

由此可见,指针常量使指针本身不变,但不影响其所指向的变量值的变化
哦,还有比较重要的事儿,指针常量在定义时就必须初始化哦,你可以试试。


Linux下的cdecl工具

终端下的使用
通过这个工具,我们很容易知道声明为何,
以下在linux下终端下输入,例如:

mwumli@mwumli-K43SA:~$ cdecl
Type help' or?' for help
cdecl>
 explain int zz;
declare zz as pointer to int
cdecl>
 explain int 
(zz)(int);
declare zz as pointer to function (int) returning pointer to int
*cdecl>
 quit

终端下的安装
这个工具在ubuntu中没内置,故痛过以下命令安装:
      sudo apt-get install cdecl
<think>我们详细解释C语言中四个容易混淆的概念:指针函数函数指针指针数组数组指针。 注意:指针函数本质是函数函数指针本质是指针指针数组本质是数组数组指针本质是指针。 1. **指针函数(返回指针函数)** - 定义:返回值为指针类型的函数。 - 语法:`返回类型 *函数名(参数列表)` - 示例: ```c int *create_array(int size) { int *arr = (int*)malloc(size * sizeof(int)); return arr; // 返回一个指向整型的指针 } ``` - 调用:`int *myArray = create_array(10);` 2. **函数指针(指向函数指针)** - 定义:指向函数指针变量,用于调用函数或作为回调函数。 - 语法:`返回类型 (*指针名)(参数类型列表)` - 示例: ```c int add(int a, int b) { return a+b; } int (*pFunc)(int, int) = add; // pFunc指向add函数 ``` - 调用:`int result = pFunc(3, 5); // 相当于调用add(3,5)` 3. **指针数组(元素为指针数组)** - 定义:数组元素均为指针类型。 - 语法:`数据类型 *数组名[数组长度]` - 示例: ```c char *names[] = {"Alice", "Bob", "Charlie"}; // 每个元素是char*类型 ``` - 访问:`printf("%s", names[0]); // 输出"Alice"` 4. **数组指针(指向数组指针)** - 定义:指向整个数组指针,常用于二维数组操作。 - 语法:`数据类型 (*指针名)[数组长度]` - 示例: ```c int matrix[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}; int (*pArr)[4] = matrix; // pArr指向一个包含4个整数的数组 ``` - 访问:`printf("%d", pArr[1][2]); // 输出第1行第2列元素,即7` **总结对比表** | 类型 | 本质 | 声明形式 | 用途 | |------------|--------|------------------------|--------------------------| | 指针函数 | 函数 | `int* func(...)` | 返回指针 | | 函数指针 | 指针 | `int (*pFunc)(...)` | 动态调用函数、回调 | | 指针数组 | 数组 | `int* arr[10]` | 存储多个指针 | | 数组指针 | 指针 | `int (*pArr)[10]` | 操作二维数组的行 | **深入理解函数指针数组** 结合引用[2][^2]和引用[3][^3],函数指针数组是存储函数指针数组,其声明为: ```c // 声明一个包含5个函数指针数组,每个指针指向返回void、参数为const char*的函数 void (*funcArray[5])(const char*); ``` 而指向函数指针数组指针(二级指针): ```c void (*(*pFuncArray)[5])(const char*) = &funcArray; // pFuncArray是指向函数指针数组指针 ``` **易混淆点解析** - `int *p[10]`:指针数组,p是数组,包含10个int*元素。 - `int (*p)[10]`:数组指针,p指向一个包含10个int数组。 - `int *func()`:指针函数,func返回int*。 - `int (*pFunc)()`:函数指针,pFunc指向返回int函数。 **应用场景** - 指针函数:动态内存分配函数(如malloc)、字符串处理函数(如strcpy)。 - 函数指针:回调函数(如qsort)、状态机实现。 - 指针数组:命令行参数argv、字符串数组。 - 数组指针:二维数组传递、矩阵运算。 **代码示例:综合运用** ```c #include <stdio.h> // 指针函数:返回字符串长度 size_t* str_len_ptr(const char* s) { static size_t len; // 静态存储,避免返回局部变量地址 for(len=0; s[len]; len++); return &len; } // 函数指针应用:回调函数 void process(int a, int b, int (*op)(int,int)) { printf("Result: %d\n", op(a,b)); } int add(int a, int b) { return a+b; } int mul(int a, int b) { return a*b; } int main() { // 指针数组:存储字符串 char* fruits[] = {"Apple", "Banana", "Cherry"}; // 数组指针:操作二维数组 int arr[2][3] = {{1,2,3}, {4,5,6}}; int (*pArr)[3] = arr; // 使用指针函数 size_t *len = str_len_ptr(fruits[0]); printf("Length of '%s': %zu\n", fruits[0], *len); // 使用函数指针数组 int (*ops[2])(int,int) = {add, mul}; for(int i=0; i<2; i++) { process(3, 4, ops[i]); } // 使用数组指针访问二维数组 printf("arr[1][2] = %d\n", pArr[1][2]); // 输出6 return 0; } ``` 输出: ``` Length of 'Apple': 5 Result: 7 Result: 12 arr[1][2] = 6 ``` **注意事项** - 指针函数返回的指针不能指向局部变量(除非是静态或动态分配的内存)。 - 函数指针需确保签名(返回类型和参数类型)匹配。 - 指针数组数组指针在内存布局上有本质区别。 §§相关问题§§ 1. 函数指针作为参数传递时有什么注意事项? 2. 如何用函数指针实现状态机? 3. 指针数组和二维字符数组在内存结构上的区别是什么? 4. 数组指针在动态二维数组中的应用? </think> ### 详细解析C语言中的指针函数函数指针指针数组数组指针 --- #### **1. 指针函数(返回指针函数)** **本质**:普通函数,但返回值是指针类型。 **语法**:`返回类型 *函数名(参数列表)` **特点**: - 返回一个内存地址(指针),可指向变量、数组或动态分配的内存。 - **必须确保返回的指针有效**(避免返回局部变量地址)。 **示例**: ```c // 指针函数:返回动态分配的整型数组 int* create_array(int size) { int *arr = (int*)malloc(size * sizeof(int)); // 动态分配内存 for (int i = 0; i < size; i++) arr[i] = i * 10; return arr; // 返回指针 } int main() { int *ptr = create_array(5); printf("%d", ptr[2]); // 输出:20 free(ptr); // 释放内存 } ``` --- #### **2. 函数指针(指向函数指针)** **本质**:指针变量,存储函数的入口地址。 **语法**:`返回类型 (*指针名)(参数类型列表)` **用途**: - 实现回调函数(如 `qsort` 中的比较函数)。 - 动态调用不同函数(如状态机)。 **示例**: ```c int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int main() { int (*pFunc)(int, int); // 声明函数指针 pFunc = add; // 指向 add 函数 printf("5+3=%d\n", pFunc(5, 3)); // 输出:8 pFunc = sub; // 改为指向 sub 函数 printf("5-3=%d\n", pFunc(5, 3)); // 输出:2 } ``` --- #### **3. 指针数组(元素是指针数组)** **本质**:数组,每个元素都是指针类型。 **语法**:`数据类型 *数组名[数组长度]` **用途**: - 存储多个字符串(如命令行参数 `char *argv[]`)。 - 管理多个指针的集合。 **示例**: ```c int main() { char *names[] = {"Alice", "Bob", "Charlie"}; // 指针数组(每个元素是 char*) for (int i = 0; i < 3; i++) printf("Name %d: %s\n", i, names[i]); } ``` **输出**: ``` Name 0: Alice Name 1: Bob Name 2: Charlie ``` --- #### **4. 数组指针(指向数组指针)** **本质**:指针,指向整个数组(而非数组首元素)。 **语法**:`数据类型 (*指针名)[数组长度]` **用途**: - 操作二维数组(传递二维数组函数)。 - 处理动态分配的二维数组。 **示例**: ```c void print_matrix(int (*p)[3], int rows) { // 数组指针(指向含3个int数组) for (int i = 0; i < rows; i++) { for (int j = 0; j < 3; j++) printf("%d ", p[i][j]); // 等效于 *(*(p+i)+j) printf("\n"); } } int main() { int matrix[2][3] = {{1,2,3}, {4,5,6}}; int (*pArr)[3] = matrix; // 数组指针指向二维数组 print_matrix(pArr, 2); } ``` **输出**: ``` 1 2 3 4 5 6 ``` --- ### **对比总结** | **类型** | **本质** | **声明示例** | **核心用途** | |----------------|------------|--------------------------|----------------------------| | 指针函数 | 函数 | `int* func()` | 返回指针(如动态数组) | | 函数指针 | 指针 | `int (*pFunc)(int, int)` | 动态调用函数、回调 | | 指针数组 | 数组 | `char* arr[10]` | 存储多个字符串或指针 | | 数组指针 | 指针 | `int (*p)[5]` | 操作二维数组的行 | --- ### **常见混淆点解析** 1. **指针函数 vs 函数指针** - `int* f()`:指针函数,`f` 是函数,返回 `int*`。 - `int (*f)()`:函数指针,`f` 是指针,指向返回 `int` 的函数。 2. **指针数组 vs 数组指针** - `int* a[5]`:指针数组,`a` 是数组,元素为 `int*`(5个指针)。 - `int (*a)[5]`:数组指针,`a` 是指针,指向含5个 `int` 的数组。 3. **函数指针数组**(结合引用[2][^2]和[3][^3]) ```c void (*funcArray[3])(int); // 数组元素是函数指针 void (*(*pFuncArray)[3])(int) = &funcArray; // 指向该数组指针 ``` --- ### **应用场景** - **指针函数**:工厂模式(创建对象)、返回动态数据。 - **函数指针**:事件处理、算法策略切换。 - **指针数组**:命令行参数处理、多字符串管理。 - **数组指针**:矩阵运算、传递二维数组函数。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值