JNI常用的C语言知识

一、指针和数组

1、普通指针

指针是存储变量的内存地址,他有类型,地址没有类型。

int main(int argc, char const* argv[]) {
    //定义变量
    int i = 90;
    //定义指针p,对变量i取地址符( &i)
    int *p = &i;

    float fi = 0.8f;
    float *fp = &fi;
    
    double j = 44.7;
    //想通过四字节的指针复制给八字节的指针行不通,下面的是错误的
    p = &j;

    //空指针,访问内存地址NULL操作系统不允许,默认值是0
    int *pp = NULL;

    return 0;
}

2、二级指针

指针保存的是变量地址,保存这个变量还可以只指针变量。

int main(int argc, char const* argv[]) {
    //定义变量
    int i = 90;
    //定义指针p,对变量i取地址符( &i)
    int* p1 = &i;
    int** p2 = &p1;
    //加一个*是p1的地址,再加一个*是p1指针对应的值i
    **p2 = 93;

    printf("p1:%p,p2:%p\n", p1, p2);//【p1:0x7ffeef4145bc,p2:0x7ffeef4145b0】
    printf("%d",i);
    return 0;
}

3、数组指针

int main(int argc, char const* argv[]) {
    //数组在内存中是连续的
    int ids[] = {33, 44, 55, 66};
    printf("%p\n", ids);
    printf("%p\n", &ids);
    printf("%p\n", &ids[0]);
    //指针变量
    int *p = ids;
    printf("%d\n",*p);
    printf("p的地址:%p\n", p);
    //指针加法
    p++;
    printf("%d\n",*p);
    printf("p的地址:%p\n", p);
    return 0;
}

打印结果:

int main(int argc, char const* argv[]) {
	//开辟连续的内存空间
    int ids[5];
    int i = 0;
    for (; i < 5; i++) {
        ids[i] = i + 10;
        printf("%d\n", ids[i]);
    }
    printf("%d\n", *ids);
    return 0;
}

打印结果:

4、函数指针

void msg(const char* title) {
    printf("%s", title);
}

int main(int argc, char const* argv[]) {
    // msg("xxx");
    void (*func)(const char* msg) = msg;
    func("函数指针");
    return 0;
}

 二、动态内存分配

栈内存:windows下,分配确定的常数2M,自动分配自动释放。
堆内存:程序员手动分配,可以分配操作系统的80%的内存,需要手动释放。

1、静态内存分配

//栈内存
void stackFun() {
    int a[1024];
}

//堆内存
void heapFun() {
    //开辟
    void *p = malloc(1024 * 1024 * 10 * sizeof(int));
    //释放
    free(p);
}

//main函数
int main(int argc, char const* argv[]) {
    stackFun();
    heapFun();
    return 0;
}

 2、动态内存分配

数组的长度是动态的。
(1)重新分配内存:

//开辟
void* p = malloc(1024 * 1024 * 10 * sizeof(int));
//原来的内存不够用,需要重新分配,
void* p2 = realloc(p, sizeof(int) * 1024);

// p2分配的内存缩小,会导致数据丢失,p2分配的内存扩大内容,如果p后面扩展的有用,这连续扩展,还是原来的即p==p2,
//如果p后面存在了,分配的不连续,会将原来的数据复制到新的内存区域,则返回新的内存地址p!=p2,如果复制到新的内存区域还是不够放置
//数据,则返回NULL,开辟失败,原来的p还在

//这里只需要释放p2就可以了,p不需要释放
 if (p2 == NULL) {
    free(p2);
    p2 = NULL;
 }

(2)释放时候需要加上判断,给指针置空,标志释放成功

 if (p == NULL) {
    free(p);
    p = NULL;
 }
 if (p2 == NULL) {
    free(p2);
    p2 = NULL;
 }

(3)内存泄露问题 : P重新赋值之后在free,并没有释放

//40M
void* p1 = malloc(1024 * 1024 * 10 * sizeof(int));
free(p1);//重新赋值之前需要释放掉
//80M
void* p1 = realloc(p, 1024 * 1024 * 10 * sizeof(int)*2);

(4)不能多次释放

free(p);
free(p);

三、结构体的写法

1、方法一

struct Man {
    int age;
    char name[20];//表示字符数组,还可以char *name二者是有区别的
};

int main() {
    //struct Man m1 = {20,"JSON"};
    struct Man m1;
    m1.age = 22;
    strcpy(m1.name, "Rouse");
    printf("%s,%d\n", m1.name, m1.age);
    return 0;
}

如果是字符数组可以在声明时候赋值:

struct Man m1 = {20,"JSON"};

但是不能这样赋值:

m1.name = "JSON";

只能导入头文件#include <string.h>拷贝操作:

strcpy(m1.name, "Rouse");

char name[20]:可以修改里面的内容,但是不能修改字符串:name[0]='x'可以
char *name:可以修改字符串,但是不能修改里面的内容如:name++,*name=x不行

2、方法二:结构体变量

struct Man{
    char name[20];
    int age;
} m1,m2,m3;

m1,m2,m3为结构体的别名。

struct Man{
    char name[20];
    int age;
} Man *Man;

Man结构体变量,*Man结构体变量的指针。在JNI中发现很多别名的结构体和名称一样。可以做到类似于JAVA的Man.age = 20的样子。

3、方法三:全局赋值

struct Man{
    char name[20];
    int age;
} Man = {"Jack",20};

4、方法四:匿名结构体

struct {
    char name[20];
    int age;
} m1;

5、方法五:结构体嵌套

struct Teacher {
    char name[20];
};

struct Student {
    char name[20];
    int age;
    struct Teacher t;
};

// struct Student{
//     char name[20];
//     struct Teacher{
//         int age;
//     } t;
// };

int main(int argc, char const* argv[]) {
    struct Student s1;
    s1.age =20;
    strcpy(s1.t.name,"json");
    return 0;
}

6、结构体大小

结构体大小,字节对齐,大小为最大数据类型的整数倍,不够的话会自动填充,如下为16(8X2)不是12(8+4);

struct Man{
    int a;
    double b;
};

7、结构体别名

取别名:

typedef int Age;
Age a = 9;

结构体取别名Man;WP为指针类型的别名:

typedef struct Man{
    char name[20];
} Man,*WP;

//简写也可以
 typedef struct Man *wp;
 typedef struct Man Man;

8、结构体函数指针成员

struct Girl {
    const char *name;
    int age;
    void (*sayHi)(const char*);
};

void sayHi(const char* text) {
    printf("%s\n", text);
}

int main() {
    struct Girl g;
    g.age = 20;
    g.name = "dd";
    g.sayHi = sayHi;
    g.sayHi("hellp");
    return 0;
}

9、结构指针的成员访问

typedef struct Girl {
    const char* name;
    int age;
    void (*sayHi)(const char*);
} Girl;

void sayHi(const char* text) {
    printf("%s\n", text);
}

typedef Girl* GirlP;

int main() {
	//Girl为结构体别名,用typedef struct修饰,不需要加struct
    Girl g;
    g.age = 20;
    g.name = "dd";
    g.sayHi = sayHi;
    g.sayHi("hellp");

    //传递指针可以直接传递,传递变量新开辟内存空间
    GirlP p = &g;
    p->sayHi("999"); //C语言中使用 -> 操作符来访问结构指针的成员
    return 0;
}

练习:

struct Song {
    char name[20];
    const char* singer;
} S, *SP;

int main(int argc, char const* argv[]) {
    S.singer = "张三";
    strcpy(S.name, "李四");
    printf("singer:%s , name:%s\n", S.singer, S.name);

    SP = &S;
    printf("singer:%s , name:%s\n", SP->singer, SP->name);
    return 0;
}

四、联合体

不同类型的变量共同占用一个内存,相互覆盖,互斥事件,目的是节省内存,大小为最大的成员所占用的字节数。

union MyTest {
	int a;
	int b;
	double c;
};

int main(int argc, char const* argv[]) {
    union MyTest t;
    t.a = 1;
    t.b = 2;
    t.c = 3;
    printf("%d,%d,%1f", t.a, t.b, t.c);
    return 0;
}

打印结果,只有t.c有数(0,0,3.000000)

五、枚举

固定的数据和java一样。

enum Day { SUN, MON, SEN };

int main(int argc, char const* argv[]) {
    enum Day d;
    d = SUN;
    return 0;
}

Day有默认值,相当于:

enum Day { SUN = 1, MON = 2, SEN = 3 };

六、define指令和宏定义

头文件:告诉编译器有这样一个函数,连接器负责找到这个函数

1、定义标识

#ifdef _cplusplus支持C++语法 endif

2、定义常数

#ifdef MAX 100;

没有类型,只是一个替换(宏定义),和java的static final不一样

int i = 99;
if (i < MAX){
    printf("---小于100")
}

3、定义宏函数

#ifndef要和endif成对出现

void jd_cont_read(){

}

void jd_cont_write(){

}

//NAME是一个参数,当调用的时候会把NAME替换
#define jni(NAME) jd_cont_##NAME
//输出日志 
#define LOG(LEVEL,FORMAT,...) printf(##LEVEL);printf(##FORMAT,__VA_ARGS_);
#define LOG_I(FORMAT,...) LOG("INFO:",##FORMAT,__VA_ARGS_);
#define LOG_D(FORMAT,...) LOG("DEBUG:",##FORMAT,__VA_ARGS_);

int main(int argc, char const *argv[]){
    jni(write);//jd_cont_write,如果有参数jni(write)("参数");
    LOG("%d%s","大小:",00)
    LOG_I("大小:",00)
    return 0;
}

防止循环引用:#pragma once

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值