Android JNI--C语言基础

1,  #include

相当于java的导包操作 例如:#include

<> 代表寻找系统的资源
“” 代表寻找我们自己写的资源
.h .hpp(声明文件 头文件)
.c  .cpp (实现文件)

2,代码结构

int main() {  // 函数的主入口
    printf("哈喽!");//打印

    getchar(); // 阻塞程序
    return 0;
}

3,基本数据类型

 int i = 100;
 double d = 200;
 float f = 200;
 long l = 100;
 short s = 100;
 char c = 'd';
上面这些基本数据类型与java一致
需要注意的是 字符串类型 java中是String  而C语言中是  char * str = "我是字符串";
sizeof获取字节数 下面是每个基本类型所占用的字节数 可以通过此api获取
sizeof(int)//int 占几个字节  == 4
sizeof(double)//double 占几个字节 == 8
sizeof(char)//char 占几个字节 ==1
sizeof(long)//long占几个字节 == 4
sizeof(short)//short占几个字节 == 2
sizeof(float)//float占几个字节 == 4


 bit——计算机的最小单位

          byte——1byte = 8bit       (byte就是字节)

          kb——1kb = 1024byte

          mb——1mb = 1024kb

          gb——1gb = 1024mb

          tb——1tb = 1024gb

          pb——1pb = 1024tb

4,打印

打印需要注意的是,不和java一样随便打印的,需要占位符

    printf("i的值是:%d\n", i); // d == 整形
    printf("d的值是:%lf\n", d); // lf == long float
    printf("f的值是:%f\n", f); // f == float
    printf("l的值是:%d\n", l); // d == 整形
    printf("s的值是:%d\n", s); // s == short
    printf("c的值是:%c\n", c); // c == char
    printf("字符串:%s\n", str); // s == String

5,指针地址

在java中,万物皆对象。

在Linux中,万物皆文件

在C 语言中,万物皆指针

指针其实可以理解为地址

  & 代表取出地址 例如:

int number = 10;
printf("此number变量的地址是:%p\n", &number); // 地址 == 00B3FE90
* 代表取出地址所对应的值 例如:
int number = 100;
printf("number的值是:%d\n", *(&number));//100 

&number代表number所对应的地址  *(&number)代表取number地址所对应的值 所以输出100
int * 代表int类型的指针 说明定义的是一个指针变量,就是一个变量而已,只不过是指针的变量而已

例如:

int a = 100;
int * b = &a;
printf("a的值是:%d\n", *b);

输出100  因为b是a的指针,也就是a地址,
*b代表取地址所对应的值,也就是取a的地址所对应的值100

只要记住一句话,内存地址就是指针,指针就是内存地址

下面看一个错误的用法:

int i = 100;
int * p = i;

这种写法是错误的,因为p只能接收指针,给它传一个100,显然是不行的

正确的写法应该是:

int i = 100;
int * p = &i;
指针变量 只能接收指针

如何通过指针修改变量值呢?举个例子:

int i = 100;
int * p = &i;
i = 200;
printf("*p的值是:%d\n", *p);

输出200,因为i变为了200,p指向i,*p取得是i的值


*p = 300;

printf("i的值是:%d\n", i);


输出300,因为*p代表i的地址所对应的值修改成300

6,函数

C语言不允许函数重载,Java可以,C++可以

函数需要先声明,再实现。 函数不能写在main函数的下面,会报错

下面来看一个简单的函数例子:

#include  //引入标准库

void change(int * i); // 先声明  参数为指针变量

int main() {

    int i = 100;

    change(&i);//传入指针

    printf("%d\n", i);//输出666

    return 0;//main函数返回值 也可以写为NULL
}


// 再实现
// 使用指针 来修改
void change(int * i) {

    *i = 666;
}

由于C语言不支持函数重载,所以声明的函数,不需要写参数

例如:

void change();//不需要填写参数

int main() {
    int a = 100;
    int b = 200;

    change(&a, &b);

    printf("交换完成后的效果:%d,%d\n", a, b);//200  100

}

// 只接收指针(内存地址)
void change(int * a, int * b) {

    int temp = *a;

    *a = *b;

    *b = temp;
}

7,多级指针:

直接看例子:

int num = 20;

int * num_p = # // 取出num的内存地址给 num_p(一级指针)

int ** num_p_p = &num_p; // 取出num_p的内存地址给  num_p_p(二级指针)

int *** num_p_p_p = &num_p_p;//取出num_p_p的内存地址给 num_p_p_p (三级指针)

printf("获取最终的值:%d\n", **num_p_p);//输出20

printf("获取最终的值:%d\n", *** num_ppp);//输出20

是几级指针,取值时,前面就加几个*

8,数组与数组指针

 定义数组
 int [] arr = {1,2,3,4}; 错误的写法
 int arr[]  = {1,2,3,4}; 正确写法
遍历数组
int i = 0;
for (i = 0; i < 4; ++i) {
   printf("%d\n", arr[i]); // 取值
}

数组的内存地址 等于第一个元素的内存地址,不是其他元素的地址

上面数组的内存地址可以写为:arr    &arr   &arr[0]

既然数组就是一个内存地址,那么

int * arr_p = arr;

数组可以用地址变量赋值

操作数组,就是对数组的指针进行操作。

int arr[]  = {1,2,3,4};

将数组地址赋值给指针变量arr_p  此时指针默认指向第一个元素
int * arr_p = arr; 

printf("%d\n", *arr_p); //输出1 因为此时指针指向第一个元素的内存地址

arr_p ++; // 指针挪动   此时指向元素二的内存地址了

printf("%d\n", *arr_p); // 输出2 因为此时指针指向第二个元素的内存地址

arr_p += 2;

printf("%d\n", *arr_p); // 输出4 因为此时指针指向第四个元素的内存地址

数组是连续的内存空间,数组每次挪动4个字节

通过循环给数组赋值:

定义数组
int arr[4];

赋值给指针变量arrP
int * arrP = arr;

循环赋值操i作
int i = 0;
for (i = 0; i < 4; ++i) {
  // 1.拿到 元素一 元素二 元素三 元素四 的内存地址   (arrP + i)
  // 2.取出 元素一 元素二 元素三 元素四 的内存地址 所对应的值 * (arrP + i)
  * (arrP +i) = (i + 10001);
}

9,函数指针

void(*methid)(int,int) 函数指针 

void 返回值

(*methid) 函数名

(int,int)参数  可以随便写 这里只是举个例子

下面来看具体用法:

void add(int num1, int num2); // 先声明


void opreate(void(*method)(int,int), int num1, int num2) {
    method(num1, num2);
}


int main() { 

    opreate(add,  10, 10);
   
    printf("main函数的 add指针是多少:%p\n", add);

    // &add和add是一样的值吗
    printf("%p, %p\n", add, &add); //  一样的

    return 0;
}

// 再实现 使用
void add(int num1, int num2) {
    printf("num1 + num2 = %d\n", (num1 + num2));
}

10,C语言中的布尔类型

C语言和Java的不同就是,C语言没有true和false  只有0和非0

只要记住一句话即可,非0即true  0就是false

11,静态开辟

在函数局部内,创建的数据,在执行函数的时候会进栈操作,函数执行完毕,会执行弹栈。因此会释放栈内的成员,栈内的数据也称之为栈内成员,这种方式开辟的内存,称为静态开辟,执行完会弹栈。不会占用内存空间。

静态开辟,即在栈区开辟内存 
int a;
int arr[6];
char 'c';
等等操作都属于静态开辟内存
进栈
void staticAction() {
    int arr[5]; // 静态开辟 栈区 (栈成员)
} 
函数的末尾会弹栈(隐士):执行完毕会弹栈  会释放所有的栈成员
栈区:占用内存大小 最大值: 大概 2M  大于2M会栈溢出  平台有关系的
堆区:占用内存大小 最大值: 大概80%  40M没有任何问题,基本上不用担心 堆区很大的
大概80%: Windows系统 给我们的编译器给予的空间  的 百分之百八十

12,动态开辟

使用malloc函数,可以动态开辟内存,这种方式的空间属于在堆中开辟,函数执行完毕之后,不会释放堆空间,因此我们一定要手动释放free,并把指针指向NULL。避免悬空指针。

悬空指针:指针指向一块内存,如果这块内存稍后被操作系统回收(被释放),但是指针仍然指向这块内存,那么,此时该指针就是“悬空指针”

void *p = malloc(size);
assert(p);
free(p); 
// 现在 p 是“悬空指针”

正确的写法

void *p = malloc(size);
assert(p);
free(p); 
// 避免“悬空指针”
p = NULL;

野指针:是没有被初始化过的指针,所以不确定指针具体指向

void *p;  // 此时 p 是“野指针”

因为“野指针”可能指向任意内存段,因此它可能会损坏正常的数据,也有可能引发其他未知错误。在实际的C语言程序开发中,定义指针时,一般都要尽量避免“野指针”的出现,可通过赋初值方式解决:

void *p = NULL;
void *data = malloc(size);

下面再来讲讲动态开辟:

 int * arr = malloc(1 * 1024 * 1024); // 堆区开辟 4M

 free(arr); // 释放掉
 arr = NULL; // 重新指向一块内存地址00000
realloc:前面已经开辟的空间,使用realloc可以扩展空间大小,举例如下:
动态开辟一个大小为5的数组
int * arr = (int *) malloc(5);
for (int i = 0; i < 5; ++i) {
  arr[i] = (i + 10001); 
}

//使用realloc 将arr扩展为大小为11的数组
int * new_arr = (int *) realloc(arr, 5+6);

int j = 5; // 5开始
for (; j < (5+ 6); j++) { 
   arr[j] = (j + 10001);
}

别忘了释放
free(new_arr);
new_arr = NULL;

13,结构体

在C语言中,结构体相当于 java中的类

结构体的定义形式为


struct 结构体名
{
   成员列表(可以是基本的数据类型,指针,数组或其他结构类型)
};

例如:

struct Dog {
    // 成员
    char name[10];
    int age;
    char sex;

};
必须要在最后写;
struct Dog dog;
这样写完,成员是没有任何初始化的,成员默认值 是系统值,乱码

赋值操作
  
strcpy(dog.name, "旺财");
dog.age = 3;
dog.sex = 'G';

第二种写法:

struct Person {
    // 成员
    char * name; // 字符指针 = "赋值"
    int age;
    char sex;
} ppp = {"zhangsan", 33, 'W'};

结构体指针

struct Cat {
    char name[10];
    int age;
};

int main() { // 栈

    // 结构体
    struct Cat cat = {"小花猫", 2};

    // 结构体 指针    -> 调用一级指针成员
    struct Cat * catp = &cat;
    catp->age = 3;
    strcpy(catp->name, "小花猫2");
    printf("name:%s, age:%d \n", catp->name, catp->age);

    return 0;
}

结构体指针与动态内存开辟

struct Cat {
    char name[10];
    int age;
};

int main() { // 堆

    // VS的写法:Cat * cat = (Cat *) malloc(sizeof(Cat));
    struct Cat *cat = malloc(sizeof(struct Cat));

    strcpy(cat->name, "金色猫");
    cat->age = 5;

    printf("name:%s, age:%d \n", cat->name, cat->age);

    // 堆区的必须释放
    free(cat);
    cat = NULL;

    return 0;
}

14,结构体取别名

struct Student_{
    char name[10];
    int age;
    char sex;
};

typedef struct Student_Student_; // 给结构体取别名

typedef Student_* Student; // 给结构体指针取别名

 

 

你可能感兴趣的:(android,c语言,开发语言)