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 ...
随机推荐
- 用MOQ来Mock静态方法的 2种方法(含Moq和Fakes的配合使用)
Moq是无法直接模拟静态方法的,解决方式有两种: 1.需要修改正式代码,在源代码中建一个新的方法把静态方法包起来,调用的时候源代码调用时调用新方法而不是原来的静态方法. 在测试的时候,Mock掉这个新 ...
- KMeans聚类
常用的聚类方法: ①分裂方法: K-Means算法(K-平均).K-MEDOIDS算法(K-中心点).CLARANS算法(基于选择的算法) ②层次分析方法: BIRCH算法(平衡迭代规约和聚类).CU ...
- 最新 创蓝253java校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.创蓝253等10家互联网公司的校招Offer,因为某些自身原因最终选择了创蓝253.6.7月主要是做系统复习.项目复盘.Le ...
- Jquery生成二维码(微信中长按图片识别二维码功能)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- python set集合(16)
在python变量中除了以前文章所提到的整形int / 浮点数float / 布尔值bool / 列表list / 字典dict 之外,还有一个类型我们还没有做详细介绍,这个变量类型就是集合set. ...
- [转帖]Linux系统下x86和ARM的区别有哪些?
Linux系统下x86和ARM的区别有哪些? https://www.cnblogs.com/alantu2018/p/9209143.html 其实界限 越来越小了.. 问题: 最近在用三星的一款i ...
- 如何安装Oracle--新手安装Oracle教程
1. 将win32_11gR2_database_1of2.zip与win32_11gR2_database_1of2.zip 解压到当前目录 PS:选中两个压缩包后右键解压到当前文件夹:必须同时解压 ...
- Redis 常用命令学习三:哈希类型命令
1.赋值与取值命令 127.0.0.1:6379> hset stu name qiao (integer) 1 127.0.0.1:6379> hset stu sex man (int ...
- IOS IAP APP内支付 Java服务端代码
IOS IAP APP内支付 Java服务端代码 场景:作为后台需要为app提供服务,在ios中,app内进行支付购买时需要进行二次验证. 基础:可以参考上一篇转载的博文In-App Purcha ...
- # VsCode 配置C++调试运行
VsCode 配置C++调试运行 打开命令面板快捷键为F1,软件上写的Ctrl+Shift+P似乎没用 先安装插件使得可以运行 先自行在vsc扩展中搜索C++安装C/C++插件 再参考知乎专栏中安装c ...