NDK开发预热-C语言基础-指针、函数指针、可变参数的函数、预处理器、结构体、共用体

C语言基础-指针、函数指针、可变参数的函数、预处理器、结构体、共用体

C语言基础

从现在开始准备学习ndk开发,从基础开始学习和记录,大神勿喷,如有不对的地方,可以指出,谢谢。
只学一点以后NDK会用到的C语言基础知识。

C 面向过程,一个一个的执行函数,没有类,没有面向对象
C++ 面向对象,思想和java的一摸一样 开始有class了

指针

C语言中,最重要的就是指针,很多人都说是最难的

指针的概念:指针变量 和 指针 的区别:指针变量就是定义指针的标记而已,指针就是指向的内存地址。
int *p = 内存地址; p=指针变量 指针=内存地址;
&:取地址符号;
因为在C语言中,是面向过程的方式,所有的函数是一个一个的顺序编译执行,所以调用的方法需要放在调用者的上面;

/**
 * TODO 替换两个数的函数
 * @param n1  传入n1的内存地址(指针)
 * @param n2  传入n2的内存地址(指针)
 */
void doMethod(int *n1,int *n2){
    printf("doMethod  num1内存地址:%#x====num2:%#x\n",n1,n2);
    // TODO *是取内存地址的值  n1、n2是个内存地址
    //用一个临时变量   temp  来存放  n1这个内存地址的值     *是取内存地址的值
    int temp = *n1;
    //将 n2的内存地址的值  赋值到n1的内存地址的值上面
    *n1 = *n2;
    //将 临时变量的值  赋值到n2的内存地址的值上面
    *n2 = temp;

}

int main() { 
 	// 指针的定义
    int* p;
    int * p2;
    int *p3;

    //指针的使用
    int value1 = 1233;
    //定义一级指针   *z  (*指针名称)
    //把这个value1的内存地址获取到 赋给一级指针z;     &:取地址符号
    int *z = &value1;
    // 输出z指针对应的值    * 取出指针内存地址对应的值
    printf("输出value1的值%d\n",*z);
    // 输出z指针的内存地址
    printf("输出value1的值%d\n",z);

    // TODO 多级指针
    int value2 = 2344;
    //把这个value2的内存地址获取到 赋给一级指针z01;     &:取地址符号
    int *z01 = &value2;
    // 取出 一级指针的内存地址
    int **z02 = &z01;
    // 取出 二级指针的内存地址
    int ***z03 = &z02;
    // 取出 二级指针的内存地址
    int ****z04 = &z03;
    // 需求:操作4级指针,取出值 2344
    printf("取值:%d\n",****z04);

    // TODO 指针实战
    //替换两个数
    int num1 = 10000;
    int num2 = 20000;
    printf("Main  num1内存地址:%#x====num2:%#x\n",&num1,&num2);
    //调用自己写的替换函数
    // TODO 由于函数的参数是 int *n1,int *n2   需要指针,所以这里穿的是内存地址
    // TODO 因为不同方法体里面 会是不同的内存地址 , 需要修改值就需要修改内存地址所对应的值
    printf("doMethod之前===num1:%d====num2:%d\n",num1,num2);
    doMethod(&num1,&num2);
    printf("doMethod之后===num1:%d====num2:%d\n",num1,num2);
}

可变参数的函数

#include <stdarg.h> //可变参数暴露的头文件

//TODO 为了 学习 函数
/**
 * 可变参数的函数
 * 可变参数 需要导入  #include <stdarg.h> 这个头文件
 * @param flag
 * @param ...   表示可变参数
 */
void add(char * flag,...){
    //等于开始
    va_list changeList;
    // flag 必须要传的参数,如果没有可以传NULL
    va_start(changeList,flag);

    //for遍历 可变参数
    for (int i = 0; i < 6; ++i) {
        //表示可变参数,需要传入int类型  va_arg表示往下取
        int item = va_arg(changeList , int);
        printf("可变参数有%d\n",item);
    }

    va_end(changeList);  //等于结束   和开始需要一起出现
}

int main() {
	//TODO 调用函数 使用可变参数
    add(NULL,1,2,3,4,5,6);
}

函数指针

指向函数的指针 就是函数指针;
有点像java中传入接口回调,然后通过接口进行回调实现的方法;

// TODO 函数指针=======================start============
/**
 * 函数指针
 * 整体  void(*onShowAction)(char*):
 *  void==函数的返回
 * (*onShowAction)== 函数的名称
 * (char*)==函数的参数
 *
 * @param tt
 * @param msg
 */
void say(void(*onShowAction)(char*) , char * msg){
    onShowAction(msg);
}
// TODO 理解java的实现
void * myPrintln(char *content){
    printf("函数myPrintln content:%s\n",content);
}
// TODO 函数指针  实战(登录) ====start==============

void login(void(*loginSuccess)(int64_t resCode,char * resultInfo),
        void(*loginError)(int64_t errorCode,char * errorInfo)){
    //TODO 非0 就是true ,等于0就是false
    //因为这里是非0  所以为true,这里就会调用loginSuccess这个函数指针
    bool isLoginOk = 9;
    if(isLoginOk){
        loginSuccess(1000,"欢迎回来\n");
    }else{
        loginError(999,"请重新登录\n");
    }
}
/**
 * 登录成功的处理
 * @param resCode
 * @param resultInfo
 * @return
 */
void * loginSucceesAction(int64_t resCode,char * resultInfo){
    printf("登录成功,resCode:%d;resultInfo:%s",resCode,resultInfo);
}
/**
 * 登录失败的处理
 * @param errorCode
 * @param errorInfo
 * @return
 */
void * loginErrorAction(int64_t errorCode,char * errorInfo){
    printf("登录失败,resCode:%d;resultInfo:%s",errorCode,errorInfo);
}

// TODO 函数指针  实战(登录) ====end==============

// TODO 函数指针=======================end============



int main() {
	 //void(*test01)(char*)  与函数指针的参数定义一样,然后这个就用 myPrintln这个方法实现
    //将这个方法实现传入到say函数中
    //第一种写法
    void(*test01)(char*) = myPrintln;
    say(test01,"第一种写法==测试下函数指针\n");
    //第二种写法
    say(myPrintln,"第二种写法==测试下函数指针\n");
    // TODO 函数指针的实战 (登录)
    login(loginSucceesAction,loginErrorAction);
}

预处理器

预处理器: 指向流程,并不是在编译时期;

    // TODO 第一种写法
#if 0
    printf("写法一\n");
#elif 9
    printf("写法二\n");
#endif      //必须要写的
    printf("一定会执行的\n");


    // TODO 第二种写法  可以配合宏
    //定义一个宏
#define NAME 'Kaizi'    //先理解为 java的常量
char * name = NAME;
#define DEBUG_MODE     //定义一个宏
// TODO ifdef  和 ifndef  不能同时存在

//#ifdef DEBUG_MODE;  //可以理解为 有没有定义 DEBUG_MODE  这个宏  如果有定义的话,会执行这里,如果没有会走else
//    printf("配合宏,当前模式为Debug模式\n");
#ifndef DEBUG_MODE  //是否没有定义 DEBUG_MODE  这个宏  如果没有定义的话,会执行这里
    printf("配合宏,当前模式为Debug模式\n");
#else
    printf("配合宏,当前模式为Relase模式\n");
#endif
    printf("一定会执行的\n");

结构体

结构体 可以理解为 java的 class;但是C里面不是对象,不需要new,用 struct 关键字 ;
结构体的使用,在栈中就定义了 ;
结构体里面所有的成员,默认都是public;
结构体所占的字节数 == 结构体里面所有成员的字节数相加;

// TODO 并不是等于java中的 import 导入
// TODO 是预处理器的时候完成的    等于  文本替换  将Main.h里面的东西全部复制过来
// TODO 不使用的头文件  不要导入进来  因为会占用体积
#include "Main.h"

// TODO 结构体 ================================start====================
// TODO 结构体  可以理解为 java的 class
// 第一种
struct Student{
    // TODO 结构体里面所有的成员,默认都是public
    char *name;        //这里是字符串  char *
    int age;
    char sex
};
// TODO 结构体     后面跟着的s1和s2的标识  可以在使用的时候  直接拿着用
// 第二种
struct Worker{
    // TODO 结构体里面所有的成员,默认都是public
    char *name;        //这里是字符串  char *
    int age;
    char sex
} s1 , s2;
// TODO 结构体   typedef是使用别名   后面跟着 DOG  别名就是DOG
// 第三种
typedef struct {
    char * dogName;
    int dogAge;
    char dogSex;
} DOG;

//#pragma pack(2);    //内部使用对其的算法 为了节约内存  一般我们不会操作  源码中会有,所以理解就好
// TODO 结构体
struct Person{
    // TODO 结构体里面所有的成员,默认都是public
    int age1;
    int age2;
    int age3;
};
//#pragma pack();     //还原操作

// TODO 结构体 ================================end====================

int main() {
// TODO 结构体,之前我们操作的,都是面向过程的,代表的就是结构体
    //第一种使用
    struct Student student; //在栈中就定义了  这样就等于初始化了
    student.name = "凯子";
    student.age = 18;
    student.sex = 'M';
    printf("name:%s;age:%d;sex:%c\n",student.name,student.age,student.sex);
    
    //第二种使用
    // TODO 结构体,这里用 s1  和  s2  这两个标识
    // TODO 结构体,s1 和 s2 就等于上面的   struct Student student;  就不需要这样初始化了
    s1.name = "凯";
    s2.name = "子";
    printf("s1.name:%s;s2.name:%s;\n",s1.name,s2.name);

	//第三种使用
    DOG dog;
    dog.dogName="旺财";
    dog.dogAge=2;
    dog.dogSex='M';

    struct Person person;
    //里面是  3个int  一个int是4个字节   所以这里是  12
    printf("person结构体 占用的字节数:%d\n", sizeof(person));
}

共用体(联合体)

共用体(联合体):在相同的内存位置当中,存储不同的数据;
共用体(联合体):所占大小是最大成员字节大小,在共用体中只有一块内存空间,共用体 共用内存;
使用的时候,需要用关键字 union
共用体(联合体):相同类型下,会保证最新的数据;
举例:有这样一个需求,很多的扩展成员属性,会被使用,我们要保证最最新的值信息

// TODO 共用体(联合体) ================================start====================
// TODO 共用体(联合体):在相同的内存位置当中,存储不同的数据
// TODO 共用体 最大成员字节大小,在共用体中只有一块内存空间,共用体 共用内存
union MyCount{
    int countValueInfo;
    int countValueInfo2;
    int countValueInfo3;
    int countValueInfo4;
    int countValueInfo5;
    int countValueInfo6;
    int countValueInfo7;
};
// TODO 共用体(联合体) ================================end====================

int main() {
	union MyCount myCount;  //在栈中已经自己初始化好了
    myCount.countValueInfo = 10;
    printf("myCount.countValueInfo:%d\n",myCount.countValueInfo);   //10
    myCount.countValueInfo2 = 200;
    printf("myCount.countValueInfo:%d\n",myCount.countValueInfo);   //200
    myCount.countValueInfo7 = 3;
    printf("myCount.countValueInfo:%d\n",myCount.countValueInfo);   //3
    printf("myCount字节数:%d\n", sizeof(myCount));     //4个字节   共用内存
}

辛苦各位童鞋观看到最后,如果博客中有不对的地方望指出,大神勿喷,谢谢~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值