JNI C语言基础
通过JNI(java native interface)可以调用优秀的C开源库:
opnecv 人脸识别 ffmpeg 音视频解码
openGL 跨编程语言、跨平台的编程接口规格的专业的图形程序接口
MediaFramework Surface Manager
Webkit
SQLite
Linux Kernel:Display Driver Bluetooth Driver
图形图像处理(C、C++实现)
1. C开发
开发工具:Dev-C++
C HelloWorld
C 基本数据类型
C 输出、输入函数
内存地址概念
指针入门 多级指针 数值和指针
结构体
联合体
枚举
1.1 HellloWorld
#include<stdio.h> // .h c的头文件。stdio: standard io 标准输入输出
#include<stdlib.h> // stdlib: standard library 标准函数库 java.lang等
main(){
printf("helloworld!\n"); //System.out.println(); "\n"换行符
}
1.2 基本数据类型
各种类型的存储大小与系统位数有关,目前通用的以64位系统为主
java基本类型 C基本类型
boolean 无(替代:0 false ,1 true)
byte(-128 127) 无
char char 1个字节(signed char -128 到 127,unsigned char 0 到 255)
short short 2个字节
int int 4个字节 -2,147,483,648 -2,147,483,647(unsigned int 0-4,294,967,295)
long long 4个字节 -2,147,483,648 -2,147,483,647(unsigned long 0-4,294,967,295)
float float 4个字节 1.2E-38 到 3.4E+38 6 位小数
double double 8个字节 2.3E-308 到 1.7E+308 15位小数
long double 16 字节 3.4E-4932 到 1.1E+4932 19 位小数
void void
1.3 输出、输入函数
printf("要输出的内容", 变量);
%d - int
%ld – long int
%lld - long long
%hd – 短整型
%c - char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
24910 1100001 01001110
12345678 10111100 01100001 01001110 */
char c='a';
short s = 123;
int i = 12345678;
long l = 1234567890;
float f = 3.1415;
double d = 3.1415926;
printf("c = %c\n", c); //c = a
printf("s = %hd\n", s); //s = 123
printf("i = %hd\n",i); //i = 24910
printf("l = %ld\n",l); //l = 1234567890
printf("f = %.4f\n",f); //f = 3.1415默认输出6位有效数字的小数 想手动指定 加上.X
printf("d = %.7lf\n",d); //d = 3.1415926
printf("%#x\n",i); //0xbc614e
printf("%#o\n",i); //057060516
char cArray[]={'a','b','c','d','\0'};
char cArray[]="你好";
printf("cArray = %s",cArray);
system("pause");
scanf("占位符",输入内容要存放的内存地址)
main(){
printf("请输入班级的人数:");
int count;
scanf("%d", &count); //&取地址符
printf("班级的人数是%d\n",count);
char cArray[20];//c的数组不检测下标越界
printf("请输入班级的名字:");
scanf("%s",&cArray);
printf("班级的人数是%d,班级的名字%s\n",count,cArray);
printf("count的地址%d\n",&count);
printf("cArray的地址%d\n",&cArray);
system("pause");
}
1.4 内存
1.4.1 内存地址
内存的最小存储单元是一个字节(Byte),所有存储单元从0开始编号,某一个编号就是对应存储单元的内存地址。
假设内存有8个存储单元,编号为0到7,则需要3位地址总线,原理如下:
内存地址(0到7)使用3位地址总线的状态值表示,则对应的可表示为:000、001、010、011、100、101、110、111
32位操作系统,指的是32地址总线,支持的最大内存为232 Byte,即4G Byte:
232 =2(2+10+10+10)=22*210*210*210 =4G
32位操作系统,地址的表示用2进制是32位,用16进制则是8位,如0x00000001。
64位操作系统,内存地址用2进制是64位,用16进制则是16位,如0x00000000D2400000。系统的硬件也会占用一些内存,实际留给系统的可用存储会小一些,如下图为64位系统下网卡占用内存情况:
Windows下sleep未定义的问题:
引入头文件windows.h
#include <windows.h>
sleep(2000)改为 Sleep(2000);
1.4.2 内存分配
1.静态内存分配:
栈内存 系统统一分配 统一回收。
值只能使用一次,之后内存被回收,可能因为重新使用而值发生变化。
2.动态内存分配:
c malloc memory allocation 内存分配
c的堆内存 程序员手动申请手动释放 malloc free
申请一块堆内存 动态内存分配
堆内存 不连续的 堆内存大小不固定 取决机器的状态
main(){
//malloc 接收的参数 申请内存大小 返回一个内存地址值 申请到的也是一块连续的内存空间
int* pointer = malloc(sizeof(int)*5);
*(pointer+0) = 1;
*(pointer+1) = 2;
*(pointer+2) = 3;
*(pointer+3) = 4;
*(pointer+4) = 5;
//C for 循环 循环的临时变量i 要先声明再使用
int i;
for(i = 0;i<5;i++){
printf("第%d个元素的值= %d\n",i,*(pointer+i));
}
free(pointer);
printf("第一个元素的值%d\n",*(pointer+0)); //这里已经回收 取不到预期的值
system("pause");
}
// 内存空间不够用,重新申请内存 之前的值会复制到新的对内存中
int increment;
scanf("%d",&increment);
pointer = realloc(pointer,sizeof(int)*(count+increment));
1.5 指针
1.5.1 取内存地址
取地址符 &
main(){
int i = 123;
printf("%#x\n",&i);
}
1.5.2 指针 存内存地址
int* pointer // pointer指针,用于保存内存地址(int*表示定义了int类型的指针变量)
int* pointer = &i; // 用取地址符&i 把变量i地址取出来,用指针变量pointer 保存
*pointer // 通过指针取地址里的值
*pointer = 456; // 通过指针修改地址里的值
main(){
int i = 123;
printf("i的地址:%#x\n",&i); // i的地址:0x61ff08
int* pointer = &i;
printf("pointer的值 = %#x\n",pointer); // pointer的值 = 0x61ff08
printf("*pointer的值%d\n",*pointer); //*pointer的值123
*pointer = 456;
printf("i的值是%d\n",i); //i的值是456
system("pause");
}
1.5.3 指针使用常见错误
1. 野指针问题
指针定义了,没有使用地址进行赋值,这时修改野指针的地址里的值是不允许的。
指针使用之前要初始化 赋给它一个自己程序中声明的变量的地址。
int* pointer;
printf("pointer的值 = %#x\n",pointer); // 这是一个随机的操作
*pointer = 123; //异常 拿到一个内存地址就改里面的值,系统是不允许的。
2. 类型不一致
int i;
double d = 3.1415; //8个字节
int* pointer = &d; //只取出数据 低4位的4个字节
printf("pointer的值=%#x\n",pointer);
printf("*pointer = %d\n",*pointer);
附:小端模式(Little-endian), 是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内在的低地址中;
1.5.4 交换两个数的值(值传递/引用传递)
值传递和引用传递传递的实际上 都是数值 只不过引用传递传递的是地址值
如果想通过一个子函数来修改main函数中变量的值 一定要用引用传递
/**
* 在该方法作用域内对两个变量进行交换,这两个变量是拷贝的副本(在新的内存地址中),不影响调用者的主函数中变量的值
*/
swap(int i, int j){
int temp = i;
i = j;
j = temp;
}
/**
* 将两个地址中的值进行了交换
*/
swap2(int* pointer, int* pointer2 ){
int temp = *pointer;
*pointer = *pointer2;
*pointer2 = temp;
}
main(){
int i = 123;
int j = 456;
// swap(i,j); // 这样不能交换i j的值,因为方法蚕食使用值传递(拷贝副本)
//swap2(&i,&j); // 这样可以交换i j的值
printf("i的值%d,j的值%d\n",i,j);
system("pause");
}
1.5.5 指针变量的长度
int* pointer;
double* pointerD;
printf("int类型的指针变量占%d个字节\n",sizeof(pointer)); //4个字节
printf("double类型的指针变量占%d个字节\n",sizeof(pointerD)) //4个字节
32位操作系统地址总线是32位,4个字节的变量即可内存地址,指针变量占4个字节
64位操作系统,指针变量占8个字节 ,与什么类型的指针没有关系
1.6 数组
数组实际上就是一块连续的内存空间。
数组变量名的地址实际上是第一个元素的地址.
// char array[] = {'a','b','c','d','\0'}; // 地址连续
printf("array[0]的地址%#x\n",&array[0]); //array[0]的地址0x61ff07
printf("array[1]的地址%#x\n",&array[1]); //array[1]的地址0x61ff08
printf("array[2]的地址%#x\n",&array[2]); //array[2]的地址0x61ff09
printf("array[3]的地址%#x\n",&array[3]); //array[3]的地址0x61ff0a
printf("array的地址%#x\n",&array); //array的地址0x61ff07
char* pointer = &array;
printf("*(pointer+0)=%c\n",*(pointer+0)); //*(pointer+0)=a
printf("*(pointer+0)=%c\n",*(pointer+1)); //*(pointer+0)=b
printf("*(pointer+0)=%c\n",*(pointer+2)); //*(pointer+0)=c
printf("*(pointer+0)=%c\n",*(pointer+3)); //*(pointer+0)=d
printf("*(pointer+0)=%d\n",*(pointer+0)); //*(pointer+0)=97
printf("*(pointer+1)=%d\n",*(pointer+1)); //*(pointer+1)=98
printf("*(pointer+2)=%d\n",*(pointer+2)); //*(pointer+2)=99
printf("*(pointer+3)=%d\n",*(pointer+3)); //*(pointer+3)=100
int array[] = {1,2,3,4}; // 地址差4
printf("array[0]的地址%#x\n",&array[0]);
printf("array[1]的地址%#x\n",&array[1]);
printf("array[2]的地址%#x\n",&array[2]);
printf("array[3]的地址%#x\n",&array[3]);
printf("array的地址%#x\n",&array);
// char* pointer = &array; //char*类型指针+1挪动1个字节 不能取到各元素的值,打印的结构是 1 、0 、0 、0
int* pointer =&array; //+1 挪动4个字节 刚好对应下一个元素地址 能取出各元素的值
printf("*(pointer+0)=%d\n",*(pointer+0)); //*(pointer+0)=1
printf("*(pointer+1)=%d\n",*(pointer+1)); //*(pointer+1)=2
printf("*(pointer+2)=%d\n",*(pointer+2)); //*(pointer+2)=3
printf("*(pointer+3)=%d\n",*(pointer+3)); //*(pointer+3)=4
1.7 多级指针
二级指针只能保存一级指针的地址。
三级指针只能保存二级指针的地址。
int i = 123;
int* pointer1 = &i;
int** pointer2 = &pointer1; //二级指针
printf("pointer = %d\n",*pointer2); //取pointer1
printf("i = %d\n",**pointer2); //取i 的值
int*** pointer3 = &pointer2; //三级指针
printf("pointer2 = %d\n",*pointer3); //取pointer2
printf("pointer1 = %d\n",**pointer3); //取pointer1
printf("i = %d\n",***pointer3); //取变量 i 的值
/**
main函数获取子函数中临时变量的地址 给指针赋值
*/
function(int** pointer2){
int i = 123;
*pointer2 = &i;
printf("i的地址%#x\n",&i);
}
main(){
int* pointer1;
function(&pointer1);
printf("pointer1的值%#x\n",pointer1);
system("pause");
}
1.8 结构体
结构体的大小大于等于结构体中每一变量的占字节数的和
结构体的大小是最大的那个变量所占字节数的整数倍 (内存对齐便于位运算)
C结构体中不能定义函数
void study(){
printf("good good study!\n");
}
typedef struct Student{
int age; //8
int score; // 4
char sex; //1
//函数指针
void(*studypointer)();函数指针的定义 返回值(*函数指针变量名字)(返回值);
} stud;
main(){
stud stu = {18,100,'f'};
stu.studypointer = &study;
stu.studypointer(); //通过函数指针 调用函数
struct Student* stuPointer = &stu;
printf("*stuPointer.age = %d\n",(*stuPointer).age);
(*stuPointer).sex ='m';
printf("stu.sex = %c\n",stu.sex);
printf("stuPointer->age = %d",stuPointer->age); //结构体指针使用间接引用运算符访问成员
system("pause");
}
函数指针的定义:
void fun()
{
printf("调用成功\n");
}
int main()
{
void(*pfun)() = &fun;
(*pfun)();
system("pause");
return 0;
}
1.9 联合体
共用体 嵌入式设备上起到节省内存空间的作用
union u{
int num; //4个字节
double d; //8个字节
}
main(){
union u u1;
printf(“union占%d个字节\n”,sizeof(u1)); //8个字节
printf("u1.num = %d\n",u1.num);
}
1.10 枚举
规定取值范围
不手动指定 从0开始,后面的值依次递增。指定了某1项的值,则后面的依次递增,前面的从0开始依次递增。
enum weekday {
MON,TUE,WEND,THUR=2,FRI,SAT,SUN
};
main(){
enum weekday day = MON;
printf();
system("pause")
}
1.11 自定义类型
typedef int mytype ;
main(){
mytype i = 123;
}
typedef void* jobject; // 任意类型的指针 对应java的object类型
typedef jobject jclass;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jobjectarray;
typedef jarray jbooleanarray;
typedef jarray jbytearray;
typedef jarray jchararray;
typedef jobject jweek;
typedef jobject jthrowable;
可以起到别名的作用,增加可读性。