Android NDK开发-----C语言基础2(指针、函数、预处理器)

在Java语言中,是不存在指针这个名词的,而在C/C++中,是存在指针的,指针就是一个变量,它指向一个地址。

1、指针

在项目中,使用指针时,一定要赋初始值,否则该指针就是一个野指针

int* p1 = 0;

例如下面的代码:

	int a = 10;
	int* p2 = &a;
	
	printf("a的地址:%#x", &a);
	printf("p2的值:%#x", p2);
	printf("p2的地址:%#x", &p2);

(1)指针的值

&代表取地址,取的是int类型a的内存地址,并使用p2指针指向该内存地址。

所以p2的值就是a的地址,但是p2的地址,跟a的地址没有关系,因为两个变量在内存中的位置就不一样。

如果想要获取p2指向的内存的值,那么就需要解引用,解析地址的值。

printf("p2指向的内存的值:%d", *p2);

*p2就是解引用,得到的就是指向的a地址的值,10.

(2)指针所占的字节数

printf("p2所占的字节:%d",sizeof(p2));

通过sizeof可以获取指针所占的字节数,在32位的环境下,int类型就占4字节,在64位的环境下,就是8个字节。

(3)通过指针修改内存值

之前通过*p2可以对指针解引用,从而获取地址对象的值,那么通过重新赋值,是可以改变内存值的。

	*p2 = 100;
	printf("a的值:%d", a);

这个时候,内存的值就已经改变为了100.

(4)指针的自增

指针指向的是一个地址,当指针执行自增操作时,就会将指针指向的地址也发生相应的偏移。

	p2++;
	printf("p2的值:%#x", p2);

所以通过指针的特性,可以遍历数组中的值。

	int array[] = { 1,2,3,4 };
	int* array_int = array;
	for (size_t i = 0; i < 4; i++)
	{
		printf("数组中的值:%d\n", *array_int++);
	}

上述数组array指针赋值的时候 为什么没有加&,是因为数组在内存中是一块连续的内存,当输出array的值的时候,其实输出的就是array的内存首地址,也就是元素11的地址,然后执行指针递增,才能输出数组中全部的元素。

(5)多级指针

	int b = 10;
	//指针p7保存b的地址
	int* p7 = &b;
	int** p8 = &p7;

指针p7指向的是变量b的地址,那么指针p7也有地址,那么取指针p7的地址,就是指针的指针p8。

2、函数

函数就是Java中的方法,跟Java不同的是,在使用这个函数之前,就必须要将这个函数显式地声明。

void test() {

}

int main()
{
	test();
}

(1)函数含参

主要分为两种:传递值和传递引用

void changed(int i) {
	i = 10;
}

int i1 = 3;
changed(i1);

当调用函数changed的时候,如果传入的是一个int值,那么不会改变该int值的内存值,即便是在函数体中,对形参的值做了修改。

但是如果是传入该变量的地址,那么就可以通过指针来修改内存值。

void changed1(int* i) {
	*i = 10;
}

int i1 = 3;
changed1(&i1);

在之前谈到了多级指针,在函数中将会使用到很多。例如有一个p指针,指向了a的内存地址,如果想要将p指针的内存地址修改为指向b的内存地址,那么就可以使用多级指针完成。

void changed2(int** i) {
	int j = 100;
	*i = &j;
}

int i1 = 3;
int* p = &i1;
changed2(&p);

(2)可变参数

在Java中,采用String... args来表示可变参数,在C语言中,使用…来表示可变参数。

#include <stdarg.h>
void add(char *c,...)
{

}

在使用可变参数的时候,第一个参数是必须要传递进去的。

如何获取可变参数列表中的所有值,就依赖于这个#include <stdarg.h>头文件,通过这个头文件,可以使用va_listvar_arg来获取

    //获取可变参数的列表
	va_list list;
	//与va_end成对出现,从第一个形参之后开始,这也是为什么必须要有第一个形参的原因
	va_start(list, b);

	for (size_t i = 0; i < 5; i++)
	{
		//取出int类型的数据
		int j = var_arg(list, int);
		printf("可变的参数:%d\n", j);
	}
	//取出char类型的数据
	char k = var_arg(list, char);
	printf("可变的参数:%c\n", k);

	va_end(list);

(3)函数指针

函数指针指的就是指向函数的指针,先来看一个例子

void set(void (*p)(char*), char* msg) {
	p(msg);
}

set函数中,存在两个形参,其中第一个是一个函数指针,第二个是一个char类型的指针,也就是说这个函数可以传递一个函数给它

重点看下面这个函数指针,这个跟普通的函数其实结构是类似的,只是被放在了函数的形参中

void (*p)(char*)
#######################################
void:函数的返回值;
*p:p变量来表示这个函数,在方法体中可以调用这个函数
char*:指的是函数p的参数列表

在使用的时候,因为需要传递一个函数,因此需要再创建一个正常的函数体,作为第一参数传递进去。

void target(char* p) {
	pritnf("第一个形参参数:%c\n", p);
}

void(*p)(char*) = target;
set(p,"hello");
或者
set(target,"hello");

输出:  第一个形参参数:hello

也就是说其中的target函数就是在set形参中的p,
然后执行了target方法,输出了msg,也就是target中的p形参

但是使用上面第一种输出方式太麻烦,可以使用typedef来创建别名。

typedef void (*Func)(char*);

Func fun = target;
set(fun,"hello");

使用函数指针,也可以用来做回调使用。

void http(void (*Success)(char*), void (*Failure)(char*)) {
	Success("成功");
}
void httpOk(char* msg) {

}
void httpFail(char* msg) {

}
void (*Success)(char*) = httpOk;
void (*Failure)(char*) = httpFail;

http(httpOk, httpFail);

以上是常规写法,如果使用typedef来写。

typedef void (*Success)(char*);
typedef void (*Failure)(char*);

void http(Success success, Failure fail) {
	success("成功");
}
void httpOk(char* msg) {

}
void httpFail(char* msg) {

}
http(httpOk, httpFail);

3、预处理器

常见的预处理器有:
include:导入头文件
ifif
elifelse if
elseelse
endif:结束if
define:宏定义
ifdef:如果宏定义
ifndef:如果没有宏定义
undef:取消宏定义

宏分为:宏变量和宏函数

(1)宏变量

#define I 1

int main()
{
int d = I;
}

定义一个宏变量I为1,那么在.cpp文件中,可以自定义一个变量d = I,那么d的值就是1.

也就是说,在代码中所有的I都会被替换成1,做文本替换的作用。

(2)宏函数

#define test(i) printf("输出:d%\n",i)

test(100);

宏函数和宏变量基本一致,定义了的宏函数,可以直接在代码中使用,输出传入的参数。

因为宏函数只是执行文本替换,它在使用时也会有问题需要注意。

#define ADD(x,y) x*y

ADD(1 + 10, 10 + 10);

我们预期得到的是11*20,但是因为宏函数只会执行文本替换,实际的运算是:

1+10*10+10 = 11+100

宏函数的优点:使用到宏函数的地方会执行文本替换,节省函数调用的开销;
缺点:不会对代码进行检查,生成的目标可能和预期不一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Awesome_lay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值