ndk学习之C语言基础复习----指针、函数、预处理器
指针:
指针乃C、C++的灵魂之所在,所以有必要好好的复习复习。什么是指针?一句话来概括:“指针是一个变量,它的值是一个地址。”,其中指针变量的声明有如下三种形式:

其中第一种是被推荐的写法。
其中还需要注意:在声明指针时如果未赋值,则是一个野指针【也就是有可能指向了一个不能被使用的地址从而造成程序的错误】,所以在声明时一定要赋值,如下:

那如果想取变量的地址则可以用“&”符,如下:

那如果想获取指针指向变量地址的值则需要用“*”解引用的操作,如下:

下面来看一下p指针占用了几个字节:

需要注意的是:由于目前是在64位系统上运行的,所以是8个字节,如果是在32位运行则长度是4个字。
有了指针之后就可以用它去操纵内存,下面来通过指针的形式来修改变量的值,如下:

指针是可以进行++、--操作的,比如用指针来遍历数组,下面来看下:

其中“*array_p1++”是先取了值,然后再对其指针进行++,如果是写成"*++array_p1",则是先对指针进行加加,然后再取值,最终输出就会漏掉一个,如下:

其中还有一种直接通过数组来进行相加也能达到遍历的目的,如下:

要取其数组的内容则需要解引用:

另外还有一个细节:为啥数组取地址时木有加“&”符号:

这是因为在C中数组名就是数组的首地址,下面来看下:

下面有个概念需要弄清楚:“数组指针”和“指针数组”,这个在面试可能会经常变问到,下面来看下:

其中指向的数组的元素个数为3,如果咱们想要通过数组指针array_p2来获得第二维的55,如何来写呢?
首先肯定得要将数组的指针+1,来定位到第二维的数组,所以array_p2+1,然后再取出它的值则是*(array_p2+1),接着这个值是一个数组,所以还得数组名+1来将指针移到要输出的第二个元素上来,所以此时为*(array_p2+1)+1,最后再解引用取出指针的值,所以整个的式子如:*(*(array_p2+1)+1),下面来验证一下:


接下来更绕的来了,先把代码写出来:

我去!!什么鬼~const char~~char const~~搞死人的节奏~~但是在学C时必须要理解它的,下面一个个来理解,在理解之前,先记着这个原则:“从右往左看 const 修饰谁 谁就不可变”:

意味着不能通过p2来修改tem的值,如下:

因为const是修饰的char,而非p2变量,所以p2的内容可以被更改,如下:

继续来理解下一个:

这个跟上一个效果是一模一样的,为啥?因为const只能修饰char,不能修饰*。
继续看下一个:

还是按照从右往左的原则,const这次修饰的是变量p4,也就是说p4的内容是不允许修改的,如下:

但是可以通过指针修改指向地址的值,如下:

好,继续,下面两个是啥都不能变了,如下:

拿p5举例,既不能修改p5指针的值,如下:


下面再来看一个跟指针相关的东东---多级指针:

解引用则为:

函数:
函数声明:
C中的函数跟Java的方法基本类似,但是在C中的函数需要注意:我们使用的函数必须在之前声明,否则会编译不过,如下:

可以在之前做一个声明既可:

所以一般函数都声明在头文件中,然后一.c文件中头部进行include,这样就如同上面的声明一样了。
函数传参:
- 传值:把参数的值给函数,如下:
也就是说不会改变原有变量的值。
- 传引用:
也就是可以通过指针来修改原值,有了这个特性,那么多级指针就变得非常有意义了,如下:

可变参数:
在Java中我们知道可变参数是由...来弄的,其实在C中也类似,其中我们经常打印的printf()函数就接收一个可变参数,查看一下源码便知:

所以咱们也来弄一个可变参数:

呃~~报错了~~参数中不能只有可变参数,必须要有一个确定参数,所以修改如下:

接着问题来了,如何来取出可变参数的值呢?看下面:

然后接着进行遍历,根据类型:

注意:其确定参数给NULL值是可以的,反正是要有一个,什么类型的都可以,不能没有确参,如下:

函数指针:
定义:指向函数的指针。举个例子:

其中"void (*p) (char*)"就是一个函数指针,void表示该函数无返回值;(char*)表示函数的参数列表,目前只接收一个参数;(*p)表示指向函数的指针。
接着咱们来调用一下:

其实也就相当于Java中的方法回调的意思,另外可以将函数的声明定义成一个typedef,如下:

可以用函数指针模拟HTTP请求,如果成功就执行某个函数,失败则执行某个函数,如下:

预处理器:
预处理器主要是完成文本替换的,常用的预处理器如下:

下面具体来看一下:
- #include:这个就不多说了。
- #if、#elif、#else、#endif:在实际代码编写中会遇到这样的写法,如下:
假如不想要这段代码了,则直接更改条件既可:

适用的场合就是假如写的代码不想要了,则不用注释掉了。
#define、#ifdef、#ifndef:这里可以配合#define的宏定义来配合上面的一些条件来使用,如下:



其中定义的宏是可以被取消的,如下:

其中#define宏定义分为两种:宏变量和宏函数,具体如下:

这样在代码中就可以使用I来表示1了,如下:

而在之前说过预处理其实也就是做文本替换用的,所以代码中所有的I就会被预处理器替换为1。
接下来看一下宏函数:
此时就可以在代码中进行调用了,如下:

但是宏函数也有陷阱需要注意,看下面这个:


如果修改一下:

期望的结果应该是(1 + 10)* (10 + 10) = 220,但是运行看:

居然变成了:1 + 10 * 10 + 10了,所以需要特别注意,可以加个括号解决:

下面来看一下宏函数有哪些优缺点:
优点:它只是文本替换,使用到宏函数的地方会执行替换,不会有函数调用的开销(将参数压栈,释放栈之类的)。
缺点:1、不会对我们的代码执行检查,不像普通的函数在编写阶段就会给出相印的错误提示。2、假如宏函数是一个非常复杂的函数,那么每个调用它的地方就会完全替换,造成代码冗余使得最终生成的目标文件(如so)增大了,比如:
如果代码中调了两次它,如下:

实际上文本替换之后就是:

其实内联函数跟宏函数的执行模式是一样的,也是执行代码替换,但不是一个概念,内联函数在编写时会做检查,另外它里面的代码不能编写过于复杂的代码,如使用了switch、while等复杂控制逻辑,否则会将内联函数降级为普通函数,那何为内联函数呢?其实就是inline关键字,如下:

- #pragma:这个用得较少,在VS中在头文件中会自动有一个如下东东:
它表示该头文件只能被引用一次,其实通用的写法是用它:

其效果都是一样的。
ndk学习之C语言基础复习----指针、函数、预处理器的更多相关文章
- ndk学习之C语言基础复习----虚拟内存布局与malloc申请
在这一次中来学习一下C语言的内存布局,了解它之后就可以解释为啥在用malloc()申请的内存之后需要用memset()来对内存进行一下初始化了,首先来了解一下物理内存与虚拟内存: 物理内存:通过物理内 ...
- ndk学习之c++语言基础复习----C++线程与智能指针
线程 线程,有时被称为轻量进程,是程序执行的最小单元. C++11线程: 我们知道平常谈C++线程相关的东东基本都是基于之后要学习的posix相关的,其实在C++11有自己新式创建线程的方法,所以先来 ...
- ndk学习之c++语言基础复习----C++容器、类型转换、异常与文件流操作
继续来复习C++,比较枯燥,但是这是扎实掌握NDK开发的必经之路,不容小觑. 容器: 容器,就是用来存放东西的盒子. 常用的数据结构包括:数组array, 链表list, 树tree, 栈stack, ...
- ndk学习之C语言基础复习----基本数据类型、数组
关于NDK这个分类在N年前就已经创建了,但是一直木有系统的记录其学习过程,当然也没真正学会NDK的技术真谛,所以一直也是自己的一个遗憾,而如今对于Android程序员的要求也是越来越高,对于NDK也是 ...
- ndk学习之c++语言基础复习----面向对象编程
关于面向对象编程对于一个java程序员那是再熟悉不过了,不过对于C++而言相对java还是有很多不同点的,所以全面复习一下. 类 C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程 ...
- ndk学习之C语言基础复习----结构体、共用体与C++开端
自己实现sprintf功能: 关于C中的系统函数sprintf在上次[https://www.cnblogs.com/webor2006/p/7545627.html]学习中已经用到过了,这里再来回顾 ...
- MySQL学习笔记_8_SQL语言基础复习
SQL语言基础复习 一.概述 SQL语句注释方式 1)以"#"开头直到行尾的所有内容都是注释 2)以"--"(--后还有一个空格)开头直到行尾的所有内容都是注释 ...
- GO学习-(10) Go语言基础之指针
Go语言基础之指针 区别于C/C++中的指针,Go语言中的指针不能进行偏移和运算,是安全指针. 要搞明白Go语言中的指针需要先知道3个概念:指针地址.指针类型和指针取值. Go语言中的指针 任何程序数 ...
- 嵌入式-C语言基础:指针函数
指针函数:返回指针的函数,即函数返回一个地址.例如:int * a(int x,int y);由于()的优先级比*高,因此a先与()结合,a(int x,int y)显然是一个函数,函数前面带一个in ...
随机推荐
- Android SDK目录说明
在SDK目录下有很多文件夹,了解这个文件夹都包含哪些内容,更有利于Android的开发,也是作为Android开发者应该了解的.本文就SDK目录进行说明. SDK目录如下: add-ones:Goog ...
- linux下使用Vsftpd服务传输文件
FTP协议占用两个端口号: 21端口:命令控制,用于接收客户端执行的FTP命令. 20端口:数据传输,用于上传,下载文件数据. 过程: 首先安装vsftpd服务程序使用命令 yum install v ...
- 重新渲染layui的radio
修改后添加这一段 layui.use('form', function() { var form = layui.form; form.render(); }); 也可以用官方的方法:https:// ...
- [转帖]HTTP请求方法:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE 说明
HTTP请求方法:GET.HEAD.POST.PUT.DELETE.CONNECT.OPTIONS.TRACE 说明 平时的Rest开发,用到的都是GET,POST,PUT,DELETE类型的请求. ...
- appium desired_caps参数大全
appium desired_caps参数大全 --------------------------------------------------------- 常用参数记录 ----------- ...
- C++中静态成员函数和普通成员函数存储方式相同
先从一个示例查看类的创建过程中,静态成员函数和普通成员函数的存储区别. #include "stdafx.h" #include<iostream> #include& ...
- Vue起飞前的准备
Vue起飞前的准备 一.什么是ECMAScript,以及es6的诞生? 1997年 ECMAScript 1.0 诞生 1999年12月 ECMAScript 3.0诞生,它 是一个巨大的成功,在业界 ...
- 【贪心】洛谷2019 OI春令营 - 普及组 作业
[P3817 小A的糖果 小A有N个糖果盒,第i个盒中有a[i]颗糖果. 小A每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中加起来都只有x颗或以下的糖果,至少得吃掉几颗糖. [贪 ...
- k8s-gitlab搭建
Gitlab官方提供了 Helm 的方式在 Kubernetes 集群中来快速安装,但是在使用的过程中发现 Helm 提供的 Chart 包中有很多其他额外的配置,所以我们这里使用自定义的方式来安装, ...
- 阿里数据迁移DTS【otter】和阿里巴巴mysql数据库binlog的增量订阅&消费组件 【canal】
[链接]alibaba/otterhttps://github.com/alibaba/otter https://github.com/alibaba/canal