首页 > 代码库 > JNI开发基础之C语言总结

JNI开发基础之C语言总结

目前只涉及到指针,无文件操作和网络操作:

jni和ndk介绍:
java的运行时环境 - java虚拟机
windows windows 下的java虚拟机
linux - java虚拟机 
.class
android2.2 以上 
wifi 热点共享.

1.操作手机网卡, 把手机网卡的状态置为混杂模式.
2.android下的极品飞车.
重力引擎,碰撞效果的模拟.
3. c 出现使用几十年.
开源的c的代码库.
opencv  intel开发, 图形和视频实时处理的方法库.
人脸识别.
rmvb 视频 avi 
视频和音频编解码的代码库. (引擎) 
ffmpeg   
opengl c代码库
4.车载电脑项目
obd (onboard debug system) 
在线的调试系统.
获取 汽车 胎压 转速 发动机的温度  
obd 获取出来的数据都是通过电信号 
lame MP3的编码库 c语言开发.
千千静听 mp3

ndk  android native develop kits 
android 本地开发工具集 
可以把c/c++ ->编译成一个 linux下可以执行的二进制文件 
java代码里面就可以通过jni 调用执行二进制的文件 

C语言基础:
// java数据类型 和长度int  4个字节 double  8个字节  float 4个字节 long 8个字节 
// short 2个字节 boolean  1个字节 char 2个字节 byte   1个字节 
// char, int, float, double, signed, unsigned, long, short and void
// c语言中 数据类型比java少一些 在c语言中没有 boolean类型的数据  int 1 代表真 0 代表假
// c 语言中没有String类型的数据   java中表示一个字符串 String  , c语言中表示字符串 通过char类型的数组来表示字符串
// c 语言没有byte类型   所有用char的类型表示byte类型 

c语言跟Java联合用法:
#include <stdio.h>    // java  import xxx.xx.pack    引用函数的声明  
#include <stdlib.h>
 main() // 程序的入口函数  
{
      printf("Hello world !\n"); // 控制台打印一个hello world 
      system("java -classpath D:\\ HelloWorld");
      
      system("pause");    // 调用windows下系统的命令 让程序暂停执行  方便观察程序的执行结果  
}
输入输出:
%d  -  int
%ld – long int
%c  - char
%f -  float
%lf – double
%x – 十六进制输出 int 或者long int 或者short int
%o -  八进制输出
%s – 字符串

Int len;
Scanf(“%d”,&len);

 main() // 程序的入口函数  
{    int i = 3;
     float f = 3.1415;
     double d = 6.2815;
     char c = ‘A‘;   //通过单引号定义字符 
     short s = 2;   
       
     printf("int i=%d\n",i);
     printf("float f=%f\n",f);
     printf("char c=%c\n",c);
     printf("double d=%lf\n",d);
     printf("short s=%d\n",s);
     
     char arr[20] ; //定义一个长度为20的数组
     scanf("%s",arr);  // 从键盘接受一个字符串,放在c数组里面 
      printf("s =%s\n",arr); 
    /* int j ;
     scanf("%d", &j);//&代表的是取地址  
     printf("j=%d\n",j); 
    */    
     system("pause");    // 调用windows下系统的命令 让程序暂停执行  方便观察程序的执行结果  
}

指针部分:
// 所有的变量都会分配一块内存空间
// 指针就是用来表示一块内存空间的地址的  
// 地址可以用过 &这个符号获取到某个变量的在内存中的地址 
// 这个地址如果想把他存放起来 就需要有一个变量 去存放这个地址
//  存放内存地址的变量 就是指针变量  
                
// 指针和指针变量 
// 指针是用来表示一块内存地址的,
// 指针变量是用来存放一个内存地址的 .
指针和指针变量的关系
指针就是地址,地址就是指针
地址就是内存单元的编号
指针变量是存放地址的变量
指针和指针变量是两个不同的概念
但是要注意: 通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样;

为什么使用指针
指针的重要性 
直接访问硬件 (opengl 显卡绘图)
快速传递数据(指针表示地址)
返回一个以上的值(返回一个数组或者结构体的指针)
如:int f(int *p,int * q){
*p = 33;
*q = 55;
}
main(){
int i= 3;int j= 5;
f(&i,*&j);
printf("i=%d\n",i);
printf("j=%d\n",j);
}
表示复杂的数据结构(结构体)
方便处理字符串 ;
如: char arr[20] ={‘h‘,‘e‘,‘l‘,‘l‘,‘o‘,‘\0‘};    
// 利用char类型指针 方便的表示一个字符串  
char* arr1= "hello world";
printf("%s",arr1)
指针有助于理解面向对象

//*号 操作符
// *号的几种含义  
//1. *号 代表一个乘法符号  3*5  = 15;
//2 . *号放在某种数据类型的后面,代表就是这种数据类型的指针  int*   float* 
//3 .  *号放在一个指针变量的前面 -> 代表取这个指针变量所存放的地址里面对应的数据  ;**p;
如:
 int i =5;// 定义一个int 类型的变量i 值 =5 
 int* p ; // 指针变量 定义一个int* 类型的变量p
 p = &i;  // 就是把i的指针赋给指针变量p  ,现在指针变量p里面存放的内容(数据)  就是i的地址  
// 通过上述实验 p和 i 是两个不同的变量 ,改变i的值 不会影响 p的值,同理,更改p的值 也不会影响i的值  
//*p 和i 其实代表的是同一个变量,代表是同一块内存空间  
内存分配图示:


// 问 java 中有值传递和引用传递 吗? 他们的区别是什么? 
// 其实在java中只有值传递 , 没有引用传递  
// Person p = new Person();  p里面存放的内容 就是person对象的地址 ;其实New出来后就实现了toString()方法;

/*/交换两个数字
int temp;
temp  = i;
i = j;
j = temp; */
// swap1(i,j);
swap2(&i,&j);

void swap2(int* p , int* q){ // 传递的形参为 i 和j 变量的地址  
     // *p  代表 i   *q 代表就是 j
     int temp;
     temp = *p;
     *p = *q; 
     *q = temp;

指针常见错误:
/*    int* p; //定义一个指针变量   垃圾值 -> 野指针  
//printf("*p=%d\n",*p); 
*p = 1231; 
// 立刻蓝屏;指针变量如果没有赋值就不能使用  
*/ 
/* int d = 324233;
char* c; ;  // 编译错误  不符合的指针类型  
 c = &d;
 printf("*p = %c\n",*c); 
类型不相同的指针不可以互相转换 

指针与函数:
/**
   使用指针的时候 不可以访问已经被系统回收掉的数据  
   子函数执行完毕后 子函数里面所有的局部变量都会别系统回收  
*/
f(int** q){
    int i = 3; 
    printf("子函数 i的地址 %#X\n",&i); 
    // *q   代表的就是p变量  
    *q = &i; 
/**
   使用指针的时候 不可以访问已经被系统回收掉的数据  
   子函数执行完毕后 子函数里面所有的局部变量都会别系统回收  
*/
 main() 
{  
 // 希望在主函数里面去使用子函数里面的变量 i 
// f(); 
// 希望在主函数里面得到子函数 里面int i变量的地址 
int* p ; //存放子函数f中 int i的地址的一个变量
f(&p); 
// printf("主函数 i的地址 %#X\n",p); (注意这里打印就没有意义啦,如果真要打印,第一次打印是原来没有回收的地址,第二次就是个随便的随机数)
// printf("i的值为 %d\n",*p); 
system("pause");    // 调用windows下系统的命令 让程序暂停执行  方便观察程序的执行结果               
}
子函数:(为什么用指针,可以返回一个以上的值(返回一个数组或者结构体的指针))
如何让子函数更改主函数里面的数据;如何让子函数返回一个以上的值;
1:子函数的形参主函数中要修改的变量的地址
2:调用子函数的时候把要修改的变量的地址传递给子函数;
3:在子函数里面修改这个地址里面存在的变量的内容;
4:主函数使用这个变量的时候里面的值就发生了变化;
通过被掉函数修改主调函数普通变量的值
1.实参必须是普通变量的地址
2.形参必须是指针变量
3.被掉函数中通过修改 *形参名的方式修改主调函数相关变量的值
如:
Void f(int *p ,int *q)
{ *p =1;
  *q=2;
}
Int main(void)
{ int a =3,b=5;
F(&a,&b);
Printf(“%d %d \n”,a,b);
Return 0;
}

指针的运算
int main(void)
{
int i = 5;
int j = 10;
int * p = &i;
int * q = &j;
int a[5];
p = &a[1];
q = &a[4];
printf("p和q所指向的单元相隔%d个单元\n", q-p);
//p - q 没有实际意义
return 0;
}
指针占用几个字节:
 在32位的操作系统上因为程序最大能使用的内存空间的地址就是2的32次方;指针只需要4位就可以表示出来所有的内存空间;
而64并且编译支持64为 8位;
{
char ch = ‘A‘;
int i = 99;
double x = 66.6;
char * p = &ch;
int * q = &i;
double * r = &x;
printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r));  //都是4;
return 0;
}

指针和数组的关系
数组名,下标和指针的关系,指针变量的运算
数组名
int a[5] //a是数组名,5是数组的大小,元素个数
int a[3][4] // 3行4列 a[0][0]就是数组的第一个元素
Int b[5]
A=b ;//错误
一维数组名是个指针常量,它存放的是一维数组第一个元素的地址
int a[5];
int a[3][4];
printf("%#X\n",&a[0]);
printf("%#X\n",&a);
下标和指针的关系
如果p是个指针变量 则
p[i] 等价于 *(p+i)
// arr是一个什么东西呢?
printf("arr = %#X\n",arr); 
// 打印 数组的第一个元素的地址 
printf("arr[0]地址 = %#X\n",&arr[0]); 
// 打印数组中的第二个元素 
printf("arr[1]=%d\n",arr[1]); 
printf("arr[1]=%d\n", *(arr+1));
//问题: arr[i]  *(arr+i)  代表的是同一个变量么? 
// 代表的是同一块内存空间 指向的是同一个变量  
//通过实验 : 数组名表示的 就是这个数组第一个元素 的首地址
如:
#include <stdio.h>    
#include <stdlib.h>
#define pi 3.1415 
// 写一个子函数 打印数组里面的每一个元素 
void printArr(int* arr, int len){ // arr是数组的首地址  len数组的长度  
     int i=0;
     for(;i<len;i++){ // 在c99 的语法格式下  for循环的初始化条件 不能写在 for 循环的括号里面  
        // printf("arr[%d]=%d\n",i,arr[i]);  // arr[i] 和  *(arr+i) 代表的含义相同 
        printf("arr[%d]=%d\n",i, *(arr+i));
     }
main() 
{  
       // int arr[10]={1,2,3,4,5};
       // printArr(&arr[0],10);
        //1 .定义一个数组  缺陷 数组的长度 必须事先申请好 
        //int arr[1000];   //  这一句代码一旦执行 ,就立刻会在内存里面申请 5个内存空间 每个内存空间的大小可以存放一个int类型的数据  
        // 没有办法动态的增加这一块空间的大小, 也没办法减小这一块内存空间  
        //2. 循环赋值
        //3. 打印数组里面的内容 
        int* arr = (int* )malloc(sizeof(int)*10); 
        arr =  realloc(arr,sizeof(int)*8); //空间的长度为12了 
        int i =0;
        for(;i<5;i++){
           printf("请输入arr[%d]=的值\n",i);
           scanf("%d",arr+i);                            
        }
        printArr(arr,5);
        system("pause");    // 调用windows下系统的命令 让程序暂停执行  方便观察程序的执行结果  
         // 作业: 从键盘接受一个数组 数组的大小 
         // 动态的更改 数组的大小增加2 
}

动态内存分配问题
程序运行时内存分配的过程
 

传统数组的缺点
1.数组长度必须实现指定,并且只能是常整数.int a[5];int len;int a[len];//error
2.传统形式的数组,程序员没法手动释放空间,数组一旦定义,系统为该数组分配的空间一直存在,函数运行完毕,数组的空间就被释放
3.数组的长度不能在函数运行中动态增加或者缩小
4.A函数定义的数组只有在A没执行完毕前被使用,a函数运行完毕后,a的数组就无法被其他函数使用
如:
图示:

#include <malloc.h>
f(int** address){ //address 存放的是q的地址 
 // 动态的在堆内存里面申请一块空间 
   int* p ;
    p = (int*)malloc(sizeof(int)*3); 
   *p = 3;
   *(p+1) = 4;
   *(p+2) = 5 ; 
   printf("子函数里面 地址%#X\n",p); 
   *address  = p; 
   // 在子函数里面把p释放掉了  
   free(p);
 main() 
{  
       int* q ; 
       f(&q); 
       printf("主函数里面 地址%#X\n",q); //主函数和子函数打印的地址是一样的;
       printf("*q = %d\n",*(q+0)); 
       printf("*q = %d\n",*(q+1)); // 残留的内存映像  ,这样用是错误的;
       printf("*q = %d\n",*(q+2)); 
       //动态内存分配 程序员可以自己手工的决定一个变量的生命周期  
       //手工的释放调用内存空间  
       //不要使用已经回收掉的内存空间里面的数据  
        system("pause");    // 调用windows下系统的命令 让程序暂停执行  方便观察程序的执行结果        
}

静态内存和动态内存:
静态内存是系统是程序编译执行后系统自动分配,由系统自动释放,静态内存是栈分配的.动态内存是堆分配的
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多.
  堆和栈的区别:
1.申请方式
栈:   
由系统自动分配.例如,声明一个局部变量int  b; 系统自动在栈中为b开辟空间.例如当在调用涵数时,需要保存的变量,最明显的是在递归调用时,要系统自动分配一个栈的空间,后进先出的,而后又由系统释放这个空间. 
堆:   
需要程序员自己申请,并指明大小,在c中用malloc函数   
如char*  p1  =  (char  *)malloc(10);   
但是注意p1本身是在栈中的.
  2.  申请后系统的响应   
 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。   
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,    会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。   
3.申请大小的限制   
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(vc编译选项中可以设置,其实就是一个STACK参数,缺省2M),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。   
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。  
 4.申请效率的比较:   
栈:由系统自动分配,速度较快。但程序员是无法控制的。   
堆:由malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.   
 5.堆和栈中的存储内容   
栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。   
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。   
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。   
6.内存的回收
栈上分配的内存,编译器会自动收回;堆上分配的内存,要通过free来显式地收回,否则会造成内存泄漏。
  堆和栈的区别可以用如下的比喻来看出:   
使用栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。   
使用堆就像是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

多级指针:

如:
int i = 5; 
int*  p = &i;  
int** q = &p;
int*** r = &q;
printf("i=%d\n",***r); //i=5;

函数的指针和指针的函数:
/*1.定义int (*pf)(int x, int y);
2.赋值 pf = add;
3.引用 pf(3,5);
*/
#include <stdio.h>    
#include <stdlib.h>
int add(int x , int y){
    return x+y;
}
 main() 
{  
int (*pf) (int x, int y); //定义一个函数的指针的声明 名字叫pf  返回值 int 接受参数两个int  
pf = add; 
printf("result=%d\n", pf(3,6)); 
system("pause");    // 调用windows下系统的命令 让程序暂停执行  方便观察程序的执行结果         
}

结构体:
每个学生都有age score name....
 int age;
 int score;
 char name[100]; // c语言里面的字符串一般都存放在字符数组中.
结构体定义格式:
第一种
struct Student{
  int age;
  float score;
  char sex;
}
第二种
struct Student2{
 int age;
 float score;
 char sex;
} st2;
第三种 
struct{
 int age;
 float score;
 char sex;
} st3
如:
struct Student
{
int age; //4
float score; // 4
long id;  //4
char sex ; //2   vc 6.0 14
};
如:
 main() 
    struct Student st={80,55.6f,100010 ,‘F‘};   
     printf("age = %d\n",st.age);    
    printf("score = %f\n",st.score);     
    printf("id = %ld\n",st.id);    
     printf("sex = %c\n",st.sex);    
    // 结构体的长度 
    printf("长度 = %d\n",sizeof(st)); //跟编译器的不一样;v6的是:14;而devC++的是:16(取最大的全部个数相加        4+4+4+4=4*4);
    struct Student st2;
    st2.age = 10;
    st2.score = 88.8f;
    st2.sex=‘F‘;
   struct Student* pst;
    pst = &st;
    //printf("age = %d\n", (*pst).age);   //*&="";&*两个综合运算后为变量;
    printf("age = %d\n", pst->age); //pst->age 在计算机内部会被转换为 (*pst).age
    system("pause");    // 调用windows下系统的命令 让程序暂停执行  方便观察程序的执行结果 
}

联合体:
如:
main( ){ 
struct date { int year, month, day; } today
 // 联合体 是定义一块相同的内存空间 存放里面的数据  ;mix.I mix .k mix.ii共用相同的地址
union { long i; int k; char ii;double d; } mix
// 联合体的作用就是用来表示一组数据类型 数据的数据类型为这一组中的某一种数据类型 
//注意 :   联合体里面的数据内容会相互覆盖 
printf("date:%d\n",sizeof(struct date));  //date:12 =4*3;
printf("mix:%d\n",sizeof(mix));  //mix:8
mix.ii = ‘A‘;
printf("k=%d\n",mix.k);//k=65
system("pause");

枚举:
Typedef:声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。 
如:
#include <stdio.h>
typedef int haha;
enum WeekDay
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};//0,1,2,3,4,5,6;
//{
//Monday=10,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
//};//10,11,12,13,14,15,16;
int main(void)
{
  haha i = 3; //此时;haha就表示的就是int类型;
  printf("%d\n",i);
  //int day;
  enum WeekDay day = Sunday;
  printf("%d\n",day);//6
  system("pause");
  return 0;
}

文件操作:

rt 只读打开一个文本文件,只允许读数据 
wt 只写打开或建立一个文本文件,只允许写数据 
at 追加打开一个文本文件,并在文件末尾写数据 
rb 只读打开一个二进制文件,只允许读数据 
wb 只写打开或建立一个二进制文件,只允许写数据 
ab 追加打开一个二进制文件,并在文件末尾写数据 
rt+ 读写打开一个文本文件,允许读和写 
wt+ 读写打开或建立一个文本文件,允许读写 
at+ 读写打开一个文本文件,允许读,或在文件末追加数据 
rb+ 读写打开一个二进制文件,允许读和写 
wb+ 读写打开或建立一个二进制文件,允许读和写 
ab+ 读写打开一个二进制文件,允许读,或在文件末追加数据 

对于文件使用方式有以下几点说明: 

文件使用方式由r,w,a,t,b+六个字符拼成,各字符的含义是: 

r(read):  
w(write):  
a(append): 追加 
t(text): 文本文件,可省略不写 
b(banary): 二进制文件 

如:

C写代码:

#include<stdio.h>

 main(){

    // java 

    // File file = new File("1.txt") 

    // 打开一个文件的c语言的函数 

    // sharedpreference  db content.openoutput()  

    // 获取到文件的指针  

    FILE* fp = fopen("1.txt","wt");

    char* str ="haha wirte file";

    // fwrite方法接受4个参数  

    // 第一个参数 写什么内容 

    // 第二个参数 是一次 数据写多长  

    // 第三个参数 是写多少次 

    // 第四个参数 写到哪个文件里面  

    int len = fwrite(str,sizeof(char),15,fp);

    printf("len =%d\n",len);

    //释放文件句柄 

    fclose(fp); 

    system("pause");   

}

C读代码:

#include<stdio.h>

 main(){

           FILE* fp = fopen("1.txt","rt");    

           char arr[20]={};

           //第一个参数 是读出来内容放在哪里

            // 第二个 参数 一次读的数据的长度  

            // 第三个参数 读多少次 

            // 第四个参数 从哪个文件里面读 

            int read = fread(arr,sizeof(char),15,fp);

            printf("arr = %s\n",arr);

            printf("读了%d个char类型\n",read); 

            fclose(fp);

            system("pause");   

}



网络操作:


2014年10月4日, 星期六 edit by samy;



来自为知笔记(Wiz)


附件列表

     

    JNI开发基础之C语言总结