C指针(二)

时间:2022-08-29 16:00:02

原文链接:http://www.orlion.ga/924/

一、指针与const限定符

const限定符与指针结合起来常见的情况有一下几种:

const int *a;
int const *a;

这两种写法是一样的,a是一个指向const int型的指针,a所指向的内存单元不可改写,所以(*a)++是不允许的,但a可以改写,所以a++可以。

int * const a;

a是一个指向int型的const指针,*a是可以改写的,但a不允许改写。

int const * const a;

a是一个指向const int型的const指针,因此*a与a都不允许改写。

指向非const变量的指针或非const变量的地址可以传给指向const变量的指针,编译器可以做隐式类型转换:

char c = 'a';
const char *pc = &c;

但是指向const变量的指针或const变量的地址不可以传给指向非const变量的指针,以免通过后者改写了前者所指向的内存单元,例如下面的代码编译器会警告:

const char c = 'a';
char *pc = &c;

字符串字面值类似于数组名,做右值使用时自动转换为指向首元素的指针,这种指针应该是const char *型。printf函数原型的第一个参数是const char *型,可以把char * 或const char *指针传给它。

二、指针与结构体

首先定义一个结构体类型然后定义这种类型的变量和指针:

struct unit {
    char c;
    int num;
};
struct unit u;
struct unit *p = &u;

要通过指针p访问结构体成员可以写成(*p).c和(*p).num,为了书写方便,c语言提供了->运算符,也可以写成p->c和p->num。

三、指向指针的指针与指针数组

int i;
int *pi = &i;
int *ppi = π

这样定义后,表达式*ppi取pi的值,表达式**ppi取i的值。

数组中的每个元素可以是基本类型,也可以是复合类型,因此也可以是指针类型。如定义一个数组a由十个元素组成,每个元素都是int *指针:

int *a[10];

这称为指针数组

main函数的原型是int main(int argc, char *argv[]);。argc是命令行参数的个数。而argv是一个指向指针的指针而不是指针数组。函数原型中的[]表示指针而不表示数组,等价于char **argv。为什么不写成char **argv而写char * argv[]呢?主要是为了给读代码的人提供有用的信息,argv不是指向单个指针,而是指向指针数组的首元素。数组中每个元素都是char *指针,指向一个命令行参数字符串。

四、指向数组的指针与多维数组

以下定义一个指向数组的指针,数组有10个元素:

int (*a)[10];

我们可以认为[]比*有更高的优先级,如果a先和*结合则表示a是一个指针,如果a先和[]结合则表示a是一个数组。现在看指向数组的指针怎么用:

int a[10];
int (*pa)[10] = &a;

a是一个数组,在&a这个表达式中,数组名做左值,取整个数组的首地址赋给指针pa。注意&a[0]表示数组a的首元素的首地址,而&a表示数组a的首地址,显然这两个地址相同,但这两个表达式的类型是两种不同的指针类型,前者是int *,而后者类型是int (*)[10]。*pa就表示pa所指向的数组a,所以取数组的a[0]元素可以用表达式(*pa)[0]。注意到*pa可以写成pa[0],所以(*pa)[0]这个表达式也可以改写成pa[0][0],pa就像一个二维数组的名字,它表示什么含义呢?下面把pa和二维数组放在一起分析。

int a[5][10];和int (*pa)[10];之间的关系类似int a[10];和int *pa之间的关系:a是由一种元素组成的数组,pa则是指向这种元素的指针。所以如果pa指向a的首元素:

int a[5][10];
int (*pa)[10] = &a[0];

则pa[0]和a[0]取得是同一个元素,唯一比原来复杂的地方在于这个元素是由10个int组成的数组,而不是基本类型。这样,我们可以把pa当成二维数组名来用,pa[1][2]和a[1][2]取的也是同一个元素,而且pa比a用起来更灵活,数组名不支持赋值、自增等运算,而指针可以支持,pa++使pa跳过二维数组的一行(40个字节),指向a[1]的首地址。

五、函数类型和函数指针类型

在C语言中函数也是一种类型,可以定义指向函数的指针,指针变量的内存单元存放一个地址值,而函数指针存放的就是函数的入口地址(位于.text段)。

例:

#include <stdio.h>
void say_hello(const char *str)
{
     printf("Hello %s\n", str);
}
int main(void)
{
     void (*f)(const char *) = say_hello;
     f("Guys");
     return 0;
}

变量f的类型声明void (*f) (const char *),f首先跟*号结合在一起,因此是一个指针。(*f)外面是一个函数原型的格式,参数是const char *,返回值是void,所以f是指向这种函数的指针。而say_hello的参数是const char *,返回值是void,正好是这种函数,因此f可以指向say_hello。say_hello是一种函数类型类似于数组类型,做右值使用时自动转换成函数指针类型。当然也可以写成void (*f)(const char *) = &say_hello;把函数ay_hello先取地址再赋给f,不需要自动类型转换。

可以通过函数指针来调用函数:f("Guys"),也可以先用*f取出它所指的函数类型再调用函数:(*f)("Guys")。

可以先定义函数类型F:

typedef int F(void);

这种类型的函数不带参数,返回值是int,可以这样声明f和g:

F f, g;

相当于声明:

int f(void);
int g(void);

下面这个函数声明是错误的:

F h(void);

因为函数可以返回void类型、标量类型、结构体、联合体,但不能返回函数类型,也不能返回数组类型。而下面这个函数声明时正确的:

F *e(void);

函数e返回一个F *类型的函数指针,但是这样:

int (*fp)(void);

是声明了一个函数指针,而不是声明一个函数。e也可以这样声明:

F *fp;

C指针(二)的更多相关文章

  1. C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 &vert; IT宅&period;com

    原文:C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | I ...

  2. int &lpar;&ast;p&rpar;&lbrack;4&rsqb; p 是二级指针 二维数组 二级指针 &period;xml

    pre{ line-height:1; color:#2f88e4; background-color:#e9ffff; font-size:16px;}.sysFunc{color:#3d7477; ...

  3. C&plus;&plus;笔记-数组指针&sol;二维数组转换指针

    参考资料: 1. 作者 BensonLaur  :https://www.cnblogs.com/BensonLaur/p/6367077.html 2. https://blog.csdn.net/ ...

  4. C-指针&comma;二级指针&comma;二维数组作为函数参数使用&comma;C语言链表&lpar;详解&rpar;

    一级指针 int *p;            //表示定义一个int型(4字节)的指针p &p                 //表示p自身的地址位置 p                  ...

  5. C&plus;&plus; 智能指针二

    /* 智能指针shared_ptr注意点 */ #include <iostream> #include <string> #include <memory> // ...

  6. C&plus;&plus;指针二(易错模型)

    规则一:Main(主调函数)分配的内存(在堆区,栈区.全局区)都可以在被调用函数里使用.如果在被调用函数里面的临时区(栈)分配内存,主调用函数是不能使用的. #include "stdio. ...

  7. C&plus;&plus; 指针二维数组, C&plus;&plus;二维指针数组笔记

    C++ 二维动态数组 一. 已知第一维 #include <iostream> using namespace std; int main(int argc, char const *ar ...

  8. c语言中数组,指针数组,数组指针&comma;二维数组指针

    1.数组和指针 ] = {,,,,};// 定义数组 // 1. 指针和数组的关系 int * pa = array; pa = array; // p[0] == *(p+0) == array[0 ...

  9. C语言中的指针&lpar;二&rpar;

    指针指向谁,就把谁的地址赋给指针,指针变量和指针指向的内存变量是不一样的.不停的给指针赋值,相当于是不断的改变指针的指向. 在开发中要避免野指针的存在,在指针使用完毕之后,记得要给指针赋值成为NULL ...

随机推荐

  1. WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  2. browserify学习总结

    前言 在未接触browserify,虽然我知道它是一个前端构建工具,但还是有几个疑问: 1. browserify出现的日期? 2. 能构建哪些文件? 3. 附加的browserify代码体积是多大? ...

  3. error C2512&colon; &OpenCurlyDoubleQuote;Rectangle”&colon; 没有合适的默认构造函数可用

    解决办法可能为: 1.再构造一个空的显性构造函数:

  4. const与&num;define宏常量 , inline与&num;define

    1.预处理 预处理器是在真正的编译开始之前由编译器调用的独立程序.预处理器可以删除注释.包含其他文件以及执行宏替代. 预处理命令(宏定义#define..#undef. 文件包含#include. 条 ...

  5. 短信轰炸PC版

    前言 之前用过android版短信轰炸的apk,于是想反编apk查看源码找短信接口,做一个PC版本的,不料反编失败.后不了了之... 昨日逛论坛时无意中看到一个网站有此功能,打开一试究竟,效果可以,于 ...

  6. vim常用命令总结 (转&rpar;

      在命令状态下对当前行用== (连按=两次), 或对多行用n==(n是自然数)表示自动缩进从当前行起的下面n行.你可以试试把代码缩进任意打乱再用n==排版,相当于一般IDE里的code format ...

  7. spring AOP简单入门

    AOP(aspect oriented programming)面向切面编程. 大致意思是在方法的执行过程中织入其他要执行的方法. 项目结构图 先介绍一下通过代理的方式实现aop,几个文件和上一篇一样 ...

  8. AST的作用

    ·代码版本兼容:例如babel ·代码混淆和压缩:将语义变量变无意义 ·开发工具:webpack.vue-cli ·编译:编译器.IDE

  9. ZKWeb网页框架1&period;9正式发布

    1.9.0更新的内容有 更新项目工具 更好的支持Linux 添加工具函数 Exception.ToDetailedString (获取例外的详细信息) Exception.ToSummaryStrin ...

  10. Control-Tree

    Fast Failover for Control Traffic in Software-defined Networks 2012 应该是第一篇关于控制树的,讨论了关于In-Band控制平面单个控 ...