1, #include
相当于java的导包操作 例如:#include <stdio.h>
<> 代表寻找系统的资源
“” 代表寻找我们自己写的资源
.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 <stdio.h> //引入标准库
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'};
结构体指针文章来源:https://www.toymoban.com/news/detail-633592.html
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;
}
结构体指针与动态内存开辟文章来源地址https://www.toymoban.com/news/detail-633592.html
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 JNI--C语言基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!