C 指针指针、函数指针、指针数组示例--第二部份

举报
Tiamo_T 发表于 2021/09/28 16:11:48 2021/09/28
【摘要】 在 C 编程语言中,指针的概念是使 C 与其他编程语言区别开来的最强大的概念。在本系列的第一部分中,我们讨论了C 指针的基本概念。在本文中,我们将尝试理解一些相对复杂的概念。本文通过示例解释了以下内容:常量指针和指向常量的指针。带示例的指针指针带示例的指针数组带示例的函数指针1. C 常量指针和指向常量的指针作为开发人员,您应该了解常量指针和指向常量的指针之间的区别。C 常量指针当指针指向的...

在 C 编程语言中,指针的概念是使 C 与其他编程语言区别开来的最强大的概念。在本系列的第一部分中,我们讨论了C 指针的基本概念

在本文中,我们将尝试理解一些相对复杂的概念。本文通过示例解释了以下内容:

  1. 常量指针和指向常量的指针。
  2. 带示例的指针指针
  3. 带示例的指针数组
  4. 带示例的函数指针


1. C 常量指针和指向常量的指针

作为开发人员,您应该了解常量指针和指向常量的指针之间的区别。

C 常量指针

当指针指向的地址不能改变时,称该指针为常量指针。

让我们举个例子:

char ch, c;
char *ptr = &ch
ptr = &c

在上面的例子中,我们定义了两个字符('ch' 和 'c')和一个字符指针 'ptr'。首先,指针“ptr”包含“ch”的地址,在下一行它包含“c”的地址。换句话说,我们可以说最初'ptr'指向'ch',然后它指向'c'。


但是在常量指针的情况下,一旦指针持有一个地址,它就不能改变它。这意味着一个常量指针,如果已经指向一个地址,就不能指向一个新地址。

如果我们看到上面的例子,那么如果 'ptr' 是一个常量指针,那么第三行就无效了。

常量指针声明为:

<type-of-pointer> *const <name-of-pointer>

例如 :

#include<stdio.h> 

int main(void)
{
    char ch = 'c';
    char c = 'a'; 

    char *const ptr = &ch; // A constant pointer
    ptr = &c; // Trying to assign new address to a constant pointer. WRONG!!!! 

    return 0;
}

编译上面的代码时,编译器给出以下错误:

$ gcc -Wall constptr.c -o constptr
constptr.c: In function ‘main’:
constptr.c:9: error: assignment of read-only variable ‘ptr’

所以我们看到,正如预期的那样,编译器抛出一个错误,因为我们试图改变常量指针持有的地址。

现在,我们应该清楚这个概念。让我们继续。

C 指向常量的指针

这个概念很容易理解,因为这个名字简化了这个概念。是的,顾名思义,这种类型的指针不能改变它指向的地址处的值。

让我们通过一个例子来理解这一点:

char ch = 'c';
char *ptr = &ch
*ptr = 'a';

在上面的例子中,我们使用了一个指向字符“ch”的字符指针“ptr”。在最后一行中,我们通过“ptr”更改地址指针处的值。但是如果这是一个指向常量的指针,那么最后一行将是无效的,因为指向常量的指针不能改变它指向的地址处的值。

指向常量的指针声明为:

const <type-of-pointer> *<name-of-pointer>;

例如 :

#include<stdio.h> 

int main(void)
{
    char ch = 'c';
    const char *ptr = &ch; // A constant pointer 'ptr' pointing to 'ch'
    *ptr = 'a';// WRONG!!! Cannot change the value at address pointed by 'ptr'. 

    return 0;
}

编译上述代码时,编译器给出以下错误:

$ gcc -Wall ptr2const.c -o ptr2const
ptr2const.c: In function ‘main’:
ptr2const.c:7: error: assignment of read-only location ‘*ptr’

所以现在我们知道了上面错误背后的原因,即我们不能改变常量指针指向的值。

2. C 指向指针的指针

到目前为止,我们已经使用或学习了指向字符、整数等数据类型的指针。但在本节中,我们将学习指向指针的指针。

正如指针的定义所说,它是一个特殊的变量,可以存储另一个变量的地址。那么另一个变量很可能是一个指针。这意味着指针指向另一个指针是完全合法的。

假设我们有一个指针“p1”指向另一个指向字符“ch”的指针“p2”。在内存中,这三个变量可以可视化为:

所以我们可以看到,在内存中,指针p1保存着指针p2的地址。指针 p2 保存字符 'ch' 的地址。

所以'p2'是指向字符'ch'的指针,而'p1'是指向'p2'的指针,或者我们也可以说'p2'是指向字符'ch'的指针。

现在,在代码“p2”中可以声明为:

char *p2 = &ch;

但是 'p1' 被声明为:

char **p1 = &p2;

所以我们看到'p1'是一个双指针(即指向一个字符的指针的指针),因此在声明中是两个*。

现在,

  • 'p1' 是 'p2' 的地址,即 5000
  • '*p1' 是 'p2' 持有的值,即 8000
  • '**p1' 是 8000 时的值,即 'c'

我认为应该很清楚这个概念,让我们举一个小例子:

#include<stdio.h> 

int main(void)
{
    char **ptr = NULL; 

    char *p = NULL; 

    char c = 'd'; 

    p = &c;
    ptr = &p; 

    printf("\n c = [%c]\n",c);
    printf("\n *p = [%c]\n",*p);
    printf("\n **ptr = [%c]\n",**ptr); 

    return 0;
}

这是输出:

$ ./doubleptr 

 c = [d] 

 *p = [d] 

 **ptr = [d]

3. C 指针数组

就像整数或字符数组一样,也可以有指针数组。

指针数组可以声明为:

<type> *<name>[<number-of-elements];

例如 :

char *ptr[3];

上面的行声明了一个包含三个字符指针的数组。

让我们举一个工作示例:

#include<stdio.h> 

int main(void)
{
    char *p1 = "Himanshu";
    char *p2 = "Arora";
    char *p3 = "India"; 

    char *arr[3]; 

    arr[0] = p1;
    arr[1] = p2;
    arr[2] = p3; 

   printf("\n p1 = [%s] \n",p1);
   printf("\n p2 = [%s] \n",p2);
   printf("\n p3 = [%s] \n",p3); 

   printf("\n arr[0] = [%s] \n",arr[0]);
   printf("\n arr[1] = [%s] \n",arr[1]);
   printf("\n arr[2] = [%s] \n",arr[2]); 

   return 0;
}

在上面的代码中,我们拿了三个指向三个字符串的指针。然后我们声明了一个可以包含三个指针的数组。我们将指针“p1”、“p2”和“p3”分配给数组的 0,1 和 2 索引。让我们看看输出:

$ ./arrayofptr 

 p1 = [Himanshu] 

 p2 = [Arora] 

 p3 = [India] 

 arr[0] = [Himanshu] 

 arr[1] = [Arora] 

 arr[2] = [India]

所以我们看到这个数组现在保存了字符串的地址。

4. C 函数指针

就像指向字符、整数等的指针一样,我们可以拥有指向函数的指针。

函数指针可以声明为:

<return type of function> (*<name of pointer>) (type of function arguments)

例如 :

int (*fptr)(int, int)

上面这行声明了一个函数指针 'fptr',它可以指向一个返回类型为 'int' 并接受两个整数作为参数的函数。

让我们举一个工作示例:

#include<stdio.h> 

int func (int a, int b)
{
    printf("\n a = %d\n",a);
    printf("\n b = %d\n",b); 

    return 0;
} 

int main(void)
{
    int(*fptr)(int,int); // Function pointer 

    fptr = func; // Assign address to function pointer 

    func(2,3);
    fptr(2,3); 

    return 0;
}

在上面的例子中,我们定义了一个函数“func”,它接受两个整数作为输入并返回一个整数。在 main() 函数中,我们声明了一个函数指针“fptr”,然后为其赋值。请注意,函数名可以被视为函数的起始地址,因此我们可以使用函数名将函数地址分配给函数指针。让我们看看输出:

$ ./fptr 

 a = 2 

 b = 3 

 a = 2 

 b = 3

因此,从输出中我们可以看到,通过函数指针调用函数产生的输出与从函数名称调用函数产生的输出相同。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。