[ndk,1]ndk开发,C语言入门讲解
一、C语言helloworld案例入门:
#include
#include
main() //主函数,跟java类似 程序的入口函数
{
printf("Hello world !/n"); //在控制台输出
system("java cn.itcast.demo.HelloWorld");//调用执行java文件
system("pause");
}
//system("pause");是:调用cmd里面的暂停命令,让cmd窗口暂停执行以下 ,方便我们观察程序的执行结果.
二、C语言的基本数据类型:
c语言的数据类型
char, int, float, double, signed,unsigned, long, short and void
1.在c99的语法的中 是没有boolean的数据类型的,true false 非0代表真 0代表的是假.
2.c语言里面没有byte类型 ,
如何让c语言表示 java中的byte呢? 可以利用char类型代替java中的byte类型
3.c语言里面没有String的字符串 , c想表示一个字符串 一般是通过char数组来表示的
sizeof(); 参数是数据类型 返回值就是这个数据类型在内存中占用多少空间
4. 注意: signed, unsigned:只能修饰整数类型的数据 不能修饰浮点型的数据。
signed表示有符号,unsigned表示无符号。
5.java中的char是两个字节 ,c语言里面 1个字节
*/
#include
main()
{ // %d 代表的是一个占位符
printf("char占用的字节为%d/n",sizeof(char)); 1个字节
printf("int占用的字节为%d/n",sizeof(int)); 4个字节
printf("float占用的字节为%d/n",sizeof(float)); 4个字节
printf("double占用的字节为%d/n",sizeof(double)); 8个字节
printf("short占用的字节为%d/n",sizeof(short)); 2个字节
printf("long占用的字节为%d/n",sizeof(long));
printf("signed int占用的字节为%d/n",sizeof(signedint));
printf("unsigned int占用的字节为%d/n",sizeof(unsignedint));
//printf("unsigneddouble占用的字节为%d/n",sizeof(unsigned double)); 该行代码会报错。
system("pause");
}
三、输入输出函数:
1、C语言中的删除操作使用占位符实现的,不同类型的数据对应不同占位符:
%d - int
%ld – long int
%c - char
%f - float
%lf – double
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
%hd - short
short
2、C语言的输入函数:
Int len;
Scanf(“%d”,&len); //扫描键盘的输入函数:
参数1:输入数据的指定类型的占位符。
餐宿2:指定保存输入数值的变量地址。
例:
#include
main()
{
int i = 129024;
long l = 333L;
char c ='B';
float f = 3.1415926f;
double d = 3.1415;
short s = 257;
printf("int i =%d/n",i);
printf("int l =%ld/n",l);
printf("int c =%d/n",c);
printf("int f =%f/n",f);
printf("int d =%lf/n",d);
printf("short s=%hd/n",s);
char cc;
printf("请输入一个int类型的值/n");
//& 取地址
scanf("%c",&cc);
printf("char cc =%c/n",cc);
//接收输入的字符串:使用char数组:
//定义一个char类型的数组 叫arr 用来存放字符串
char arr[]={' ', ' ',' ','',' '};
printf("请输入一个字符串/n");
scanf("%s",arr);
printf("数组的内容%s/n",arr);
system("pause");
}
四、指针:
1、所谓指针就是一个地址,地址其实就是一个内存空间的编号.
2、每个变量在内存中都有一个地址 :通过& 符号获取变量的地址.
例:
#include
main(){
int i;
//把i变量在内存中的地址 打印出来
//打印出变量的地址,指针就是一个地址 ,
//地址为十六进制:如:0xXXXX ,#表示打印的数值前面带0x
printf("i的地址为%#x/n",&i);
//60秒的倒计时
for(i =60; i>0;){
printf("剩余时间%d/n",i);
sleep(4000);
i--;
}
printf("游戏结束/n");
system("pause");
}
3、指针和指针变量:
例:
#include
main(){
// 1. 指针就是地址, 通过一个地址可以访问到一块内存空间, 如果修改这个内存空间里面的内容
int i = 5; //定义一个int类型的变量 变量的名字 叫 i 里面存放的内容 是一个int类型的值
// 2. 指针变量 用来存放一个变量地址的变量
int* p; //定义了一个int* 类型的变量 变量的名字叫p 里面存放的内容 是一个
//int* 类型的值
//也可以定义为:int *p; 或 int * p;
p = &i; // p里面存放的就是 i变量的地址了.
printf("p的值为%#x/n",p);
// 指针和指针变量的关系
// 指针是一个地址 ..
// 指针变量是存放一个地址的变量
//其实是两个完全不同的概念,但是 世面的书籍 习惯上把指针跟指针变量没有做区别
// * 号操作符的使用:
1.使用场景,定义一个指针变量 int* double* long*
2.表示两个数据相乘 3*5 = 15;
3. 如果*号后面跟的是一个指针变量, 用*可以访问指针变量内部存放的地址 里面存储的变量
//*p; //获取p变量里面存放地址所指向的变量,就是i对应的值;
//*p 和 i 代表的是同一个变量
printf("*p的值为%d/n",*p);
system("pause");
}
五、指针的常见错误:
1、指针未经赋值不能使用: 以下代码会报错:
#include
main(){
int i = 5;
int* p ;
printf("*p的值为%d/n",*p);
system("pause");
}
2、不同的指针类型 不能进行相互的转化
#include
/**
不同的指针类型 不能进行相互的转化
每一种变量类型 在内存中占用的空间 是不相同的.
*/
main(){
short s = 278;
int i = 2777777; //4个byte
int* p = &i;
short* q = &i; // short 在内存空间里面占用的是两个 byte
printf("*p=%d/n",*p);
printf("*q=%hd/n",*q);
system("pause");
}
3、不能使用已经被系统回收掉的内存空间里的数据:
六、案例:交换两个数字.
通过修改地址里面的值 达到了交换数据的目的
#include
void swap2(int* p, int* q){//p代表的是 i的地址, q代表的是 j的地址
// 把i变量和 j变量的地址 传递给了子函数,在子函数里面直接通过修改地址里面的值 达到了交换数据的目的
// *p 代表的就是 i变量
// *q 代表的就是 j变量
int temp;
temp = *p;
*p = *q;
*q = temp;
}
//主方法
main(){
int i = 3;
int j = 5;
// swap(i,j);
swap2(&i,&j);
printf("i=%d/n",i);
printf("j=%d/n",j);
system("pause");
}
七、指针的作用:
1、直接访问硬件 (opengl 显卡绘图)
2、 快速传递数据(指针表示地址)
3、 返回一个以上的值(返回一个数组或者结构体的指针)
例:在子函数中同时修改两个变量对应的值:
#include
//相当于是让子函数 修改了主函数里面连个数据的值, 子函数返回一个以上的数据
void f(int* p , int* q){ // p表示的是i的地址, q代表的是 j的地址
*p = *p+3;
*q = *q+3;
}
main(){
int i = 3;
int j = 5;
f(&i,&j); //调用子函数
printf("i=%d/n",i);
printf("j=%d/n",j);
system("pause");
}
4、 表示复杂的数据结构(结构体)
5、 方便处理字符串
// c语言是没有String类型的 要在c语言里面表示一个字符串 一般都是通过字符数组的方式表示
#include
main(){
// char arr[]={'h','e','l','l','o'};
//char类型的指针变量
char* arr ="hello";
//输出字符传
printf("%s/n",&arr[0]);//arr[0]字符数组的首地址。
system("pause");
}
6 、指针有助于理解面向对象
八、指针和数组之间的关系:
1、数组:
数组在内存中是一块连续的内存空间。
数组的名称 等于数组中第一个元素的地址(首地址)。
int arr[]={1,2,3,4};
printf("arr = %#x /n",arr);
printf("第一个元素的地址为%#x/n",&arr[0]);
上面两行代码输出的值相同。
2、指针操作数组:
例:开发一个子方法 把数组的每一个元素都打印出来
#include
// 第一个参数 数组的首地址, 第二个参数 数组的长度
void printArr(int* parr , intlen){
int i;
for( i=0;i
//输出方式一:根据数组的下标获取数组中的值:
printf("arr[%d]=%d/n",i,parr[i]);
// *parr //数组第一个元素对应的内容
//*(parr+1) 获取数组第二元素对应的内容(因为内存是连续的空间)
printf("arr[%d]=%d/n",i,*(parr+i));
}
}
main(){
int arr[]={1,2,3,4,5,6,7,8,9};
printArr(arr,9);
system("pause");
}
九、指针的运算:
1、指针的运算只对连续的内存空间才有意义,数组是一块连续的内存空间:
例:数组元素指针之间的运算:
#include
main(){
//数组是一块连续的内存空间
int intarr[] = {1,2,3,4,5,6,7,8,9};
// int* arr0 = &intarr[0];
int* arr0 = intarr;//数组第一个元素的地址
int* arr1 = &intarr[1];
int* arr2 = &intarr[2];
printf("arr0地址为%#x/n",arr0);
printf("arr1地址为%#x/n",arr1);
printf("arr2地址为%#x/n",arr2);
printf("arr2与arr0之间的距离为%d/n", arr2 - arr0);
// 指针的运算 相减和相加得到的结果其实都是 内存地址的偏移量,不是单纯的内存地址整数加减结果.结果代表的有特殊的业务含义, 几个内存空间之间的偏移量.
char chararr[] ={'1','2','3','4','5'};
// int* arr0 = &intarr[0];
char* chararr0 = chararr;//数组第一个元素的地址
char* chararr1 = &chararr[1];
char* chararr2 = &chararr[2];
printf("chararr0地址为%#x/n",chararr0);
printf("chararr1地址为%#x/n",chararr1);
printf("chararr2地址为%#x/n",chararr2);
printf("chararr2与chararr0之间的距离为%d/n", chararr2 - chararr0);
system("pause");
}
九、指针的长度:
#include
main(){
//在32位的操作系统上, 2的32次方个内存地址 4个byte
// 在我当前的32电脑操作系统上 用的devcpp工具, gcc
int i =3;
int* ip = &i;
double d = 3.14159;
double* dp = &d;
float f = 6.28f;
float* fp = &f;
short s = 255;
short* sp = &s;
//指针的长度都为4。
printf("int的指针长度为%d/n", sizeof(ip));
printf("double的指针长度为%d/n",sizeof(dp));
printf("float的指针长度为%d/n",sizeof(fp));
printf("short的指针长度为%d/n",sizeof(sp));
system("pause");
}
十、动态内存和静态内存:
1、动态内存
动态内存创建在堆空间上的 一块不连续的内存空间 可以占用整个操作系统的全部内存
堆内存就是操作系统维护的剩余的内存空间,
在java里面new出来的的所有的对象都是分配在堆内存里面。
在C语言里面,申请堆内存空间使用malloc 函数。
malloc函数接受一个参数 ,参数代表动态的在堆内存申请多大的内存空间.
malloc的返回值 代表的是申请的这块内存空间的首地址.
使用free(intp);函数释放堆内存空间,将堆内存标记为可用。
例:申请堆内存空间:
#include
//引入动态内存分配的头文件
#include
f(int** padress){
//*padress 拿到了主函数里面的p变量
// 申请一块内存空间 空间的大小为8个字节,空间的首地址把他赋给 intp的变量
int* intp = (int*)malloc(sizeof(int)*2);
*intp = 33; // 在堆内存申请的空间里面放置一个int类型的值 值为33
*(intp+1) = 99; //
*padress = intp; //把堆内存的地址赋给 主函数里面的p变量
//free(intp); //释放堆内存空间,将堆内存标记为可用。
printf("子函数i的地址为%#X/n",intp);
}
main(){
int* p ;// 用来存放 子函数里面的i变量的地址
//int -> int* ->int**
//在主函数里面把子函数的i变量的地址给获取出来
f(&p);
// 手动的回收掉申请的那块内存空间
free(p); // 99 内存中的残留的影像,或者幻影
printf("主函数i的地址%#X/n",p);
printf("主函数 i的值为%d/n",*p);
printf("主函数 第二个的值为%d/n",*(p+1));
system("pause");
}
2、静态内存 创建在栈空间上的 一块连续的内存空间 2M
3、动态内存优点:
动态内存可以跨函数使用,静态函数不可以。
动态内存中数组的长度能在函数运行中动态增加或者缩小
动态的增加数组长度:使用reallco函数。
例:动态的增加数组长度:使用reallco函数:
#include
//引入动态内存分配的头文件
#include
void printArr(int* arr , intlen){
int i;
for(i = 0; i
printf("学生编号%d的成绩为%d/n",i,arr[i]);
}
}
main(){
printf("请输入学生的总数/n");
int len;
scanf("%d",&len);
//根据len动态的创建一个数组
int* pgrade = malloc(sizeof(int)*len);
int i;
for(i=0;i
printf("请输入学生%d的成绩/n",i);
scanf("%d",(pgrade+i));
}
printf("打印学生的成绩列表/n");
printArr(pgrade,len);
printf("请输入增加学生的个数/n");
int addnumber;
scanf("%d",&addnumber);
pgrade = realloc(pgrade,sizeof(int)*(len+addnumber));
int j;
for(j=len;j<(len+addnumber);j++){
printf("请输入新添加的学生%d的成绩/n",j);
scanf("%d",(pgrade+j));
}
printf("重新打印学生的成绩列表/n");
printArr(pgrade,len+addnumber);
system("pause");
}
十一、堆和栈的区别:
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来显式地收回,否则会造成内存泄漏。
堆和栈的区别可以用如下的比喻来看出:
使用栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就像是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
十二、java的值传递和引用传递:
public StringgetPersonName(Person p){
return person.getName();
}
p变量里面存放的是一个 堆内存里面person对象的地址. (值). // 感觉java是只有值传递。
p变量他代表的是person对象的一个引用, 引用传递.
十三、多级指针:
例:使用r取到i的值:
#include
main(){
int i = 3;
int* p = &i;
int** q = &p;
int*** r = &q;
//取到i 的值
printf("i=%d/n",***r);
system("pause");
}
十三、函数的指针:
获取函数的首地址,通过首地址,就可执行该函数。
例:
#include
/**
1.定义int (*pf)(int x, int y);
2.赋值 pf = add;
3.引用 pf(3,5);
*/
int add(int x, int y){
return x+y;
}
main(){
int (*pf)(int x,int y); // 定义一个函数的指针指针的类型是返回值为int 接受的参数 int
pf =add;
printf("result=%d/n", pf(3,6));
system("pause");
}
十四、结构体:使用struct 定义结构体:
#include
//定义一个结构体
struct Student
{
char sex; //1
int age; //4
float score; //4
int id; //4
};
main(){
//
struct Student st={80,55.6f,10000,'F' };
//获取结构体的指针
struct Student* pst = &st;
printf("age=%d/n",st.age);
//结构体的长度
printf("结构体的长度%d/n",sizeof(st));
//通过指针获取结构体中的变量
printf("age=%d/n", (*pst).age);
//通过指针获取结构体中的变量 的第二种写法:
printf("age=%d/n", pst->age);
system("pause");
}
十四、联合体:
// 联合体的作用是声明一块公用的内存空间,长度跟公用的数据类型中最长的一个条目一致,
当多次为同一个联合体赋值时,会覆盖:
#include
main( )
{
struct date { int year, month, day;}today;
union { long i; int k; char ii; } mix;
// 联合体的作用是声明一块公用的内存空间
// 长度跟公用的数据类型中最长的一个条目一致
printf("date:%d/n",sizeof(struct date));
printf("mix:%d/n",sizeof(mix));
//下面的赋值,先定义的会被覆盖。
mix.i = 99;
mix.k = 18;
mix.ii = 'A';
printf("i=%ld",mix.i);
system("pause");
}
十五、枚举:
#include
enum WeekDay
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};
int main(void)
{
//int day;
enum WeekDay day = Sunday;
printf("%d/n",day);
system("pause");
}
十六、typedef:
声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。
typedef int haha;//声明一个int型的数据类型
例:
#include
typedef int haha;
int main(void)
{
haha i = 3;
printf("%d/n",i);
system("pause");
return 0;
}
- 09-29如何通过wrap malloc定位C/C++程序的内存泄漏
- 02-25打车软件大战升级,补贴还能维持多久?
- 12-23BMP文件右旋90度[c语言]
- 12-23寻找直方图中面积最大的矩形(C语言版)
- 12-23[ndk,2]ndk开发案例和错误处理
- 12-23[ndk,1]ndk开发,C语言入门讲解
- 12-23C语言连续存储实现队列机制
- 12-23Objective-c 数据类型
- 01-11全球最受赞誉公司揭晓:苹果连续九年第一
- 12-09罗伯特·莫里斯:让黑客真正变黑
- 12-09谁闯入了中国网络?揭秘美国绝密黑客小组TA
- 12-09警示:iOS6 惊现“闪退”BUG
- 12-05亚马逊推出新一代基础模型 任意模态生成大模
- 12-05OpenAI拓展欧洲业务 将在苏黎世设立办公室
- 12-05微软质疑美国联邦贸易委员会泄露信息 督促其
- 12-05联交所取消宝宝树上市地位 宝宝树:不会对公
- 12-04企业微信致歉:文档打开异常已完成修复