好久没有写NDK啦,这两天在拿起来做开发,感觉基础知识有点生疏啦,正好趁周末温故一下,做个小笔记方便以后查看。
1、占位符
printf("%d\n", i);
我们做一些日志输出的时候往往会这么书写,这个%d是起到一个占位的作用。C语言中基本数据类型的占位符分别是:
1、int %d
2、float %f
3、short %d
4、long %ld
5、char %c
6、double %lf
7、字符串 %s
8、十六进制 %x
9、八进制 %o
2、遍历数组
//不标准 Linux会编译出错
for (int i = 0; i < 10; i++){
printf("%d\n", i);
}
//标准的写法
int n = 0;
for (; n < 5; n++){
printf("%d\n", n);
}
3、指针
先看几个常见的概念:
(1)、& 表示取地址符,
void change(int i){//会重新创建一个i地址
i = 300;
}
void change(int *p){
*p = 300;
}
//指针存取的是变量内存地址
void main(){
int i = 90;
printf("i的值为:%d\n",i);
int *p = &i;
//*p = 200;//间接赋值
//change(i);
change(p);
printf("i的值为:%d\n",i);
system("pause");
}
*/
(2)、指针有类型,地址没有类型,地址只是开始的位置,类型读取到什么位置结束。
void main(){
int i = 90;
int *p = &i;
double j = 99.9;
p = &j;
printf("double size:%d\n",sizeof(double));
printf("%#x","%lf\n",p,*p);//想通过4字节读取8字节变量的值,是不行的
getchar();
}
(3)、空指针的默认地址为0
(4)、多级指针
void main(){
int a = 50;
//p1保存的a的地址
int *p1 = &a;
//p2保存p1的地址
int **p2 = &p1;
printf("p1:%#x,p2:%#x\n",p1,p2);
**p2 = 99;
printf("%d\n",a);
getchar();
}
(5)、指针的运算
指针的运算,一般在数组遍历时才有意义,基于数组在内存中线性排列的方式。
void main(){
//数组在内存连续存储
int ids[] = {12,45,23,33,11};
//数组变量名:ids就是数组的首地址
printf("%#x\n", &ids);
printf("%#x\n", ids);
printf("%#x\n",&ids[0]);
//ids 等价于&ids[0],ids+1等价于&ids[1],ids+i等价于&ids[i]
//*ids等价于ids[0],*(ids+1)等价于ids[1],*(ids+i)等价于ids[i]
//指针变量
int *p = ids;
printf("%d\n",*p);
//指针的加法
p++;//p++向前移动sizeof(数据类型)个字节
printf("%d\n",*p);
getchar();
}
(6)、函数指针
void msg(char* msg,char* title){
MessageBox(0, msg, title, 0);
}
int msg(char* msg, char* title){
MessageBox(0, msg, title, 0);
return 0;
}
//程序是数据和指令的集合
void main(){
//msg();
//函数返回值类型,函数指针名称,函数的参数列表
//void(*fun_p)(char* msg, char* title) = msg;
//fun_p("内容","标题");
printf("%#x\n",msg);
int(*fun_p)(char* msg, char* title) = msg;
fun_p("内容", "标题");
getchar();
}
int add(int a,int b){
return a+b;
}
int minus(int a, int b){
return a - b;
}
//msg函数需要传递一个函数指针参数
//类似java回调函数
void msg(int(*fun_p)(int a,int b),int m,int n){
//int r = fun_p(m, n);
//MessageBox(0, "hhh","ddd", 0);
//printf("%d\n",r);
printf("执行一段代码...\n");
printf("执行回调函数...\n");
int r = fun_p(m, n);
printf("执行结果:%d\n",r);
}
void main(){
//加法
//int(*fun_p)(int a,int b)=add相当于
msg(add, 10, 20);
//减法
msg(minus,60,10);
getchar();
}
4、内存分配
1.栈区(stack),window下,栈内存分配2M(确定的常数)超出了显示就会stack overflow错误,栈溢出
自动分配、释放
2.堆区(heap) 操作系统80%内存
程序员手动分配、释放
3.全局区或静态区
4.字符常量区
5.程序代码区
/*
void main(){
//40M
//stack overflow错误,栈溢出
//静态内存分配 栈区
int a[1024 * 1024 * 40];
}
*/
/*
//堆内存
void main(){
//在内存上,分配40M的内容
//字节
//void * 任意类型的指针
int* p = (int*)malloc(1024 * 1024 * 10 * sizeof(int));
//释放内存
free(p);
}
*/
静态内存和动态内存区别:
静态分配内存大小是固定的,问题:1、很容易超出栈内存的最大值 2、为了防止内存不够用会开辟更多的内存,容易浪费
动态内存分配,在程序运行过程中,动态指定需要使用的内存大小,手动释放,释放之后这些内存还可以被重新使用。
//创建一个数组,动态指定数组的大小
//(在程序运行过程中,可以随意的开辟指定大小的内存,以供使用,相当于Java中的集合)
void main(){
//静态内存分配创建数组,数组的大小是固定的
//int i = 10;
//int a[i];
int len;
printf("输入数组长度:");
scanf("%d",&len);
//开辟内存,大小为len*4字节
int *p = malloc(len*sizeof(int));
//p是数组的首地址,p就是数组的名称
//给数组元素赋值(使用这一块刚刚开辟出来的内存区域)
int i = 0;
srand((unsigned)time(NULL));
for (; i < len; i++){
p[i] = rand() % 100;
printf("%d,%#x",p[i],&p[i]);
}
//手动释放内存
free(p);
getchar();
}
内存重新分配
void main(){
int len;
printf("输入数组长度:");
scanf("%d", &len);
//int *p = malloc(len*sizeof(int));
int *p = calloc(len, sizeof(int));
int i = 0;
srand((unsigned)time(NULL));
for (; i < len; i++){
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
int addLen;
printf("输入数组长度:");
scanf("%d", &addLen);
//扩大刚刚分配的内存空间
//1、原来的内存的指针 2、内存扩大之后的总大小
int *p2 = realloc(p, sizeof(int) * (len + addLen));
if (p == NULL){
printf("重新分配失败");
}
//重新赋值
i = 0;
for (; i < len + addLen;i++){
p2[i] = rand() % 200;
printf("%d,%#x\n", p2[i], &p2[i]);
}
//手动释放内存
if (p2 != NULL){
free(p2);
p2 = NULL;
}
getchar();
}
总结:
//重新分配内存的两种情况:
//缩小,缩小的那部分数据会丢失
//扩大(连续的)
//1、如果当前内存段后面有需要的内存空间,直接扩展这段内存空间,realloc返回的指针
//2、如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能满足这一要求的内存块,
// 将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存地址
//3、如果申请失败,返回NULL,原来的指针仍然有效
内存分配的细节
//1、不能多次释放
//2、释放完之后;给指针置NULL;标志释放完成
//3、内存泄漏,p重新赋值之后,void main(){ int* p1 = malloc(1024 * 1024 * 10 * sizeof(int)); //free(p1);//这里要释放一下 p1 = malloc(1024 * 1024 * 20 * sizeof(int)); free(p1); }
再free,并没有真正释放内存
5、字符串
C语言中对于字符串的初始有两种方式:
//使用字符数组存储字符串
void main(){
//char str[] = { 'w', 'e', 'r', 't','\0' };// \0表示结束
//char str[6] = { 'q', 'w', 'e', 'r', 't' };
char str[10] = "love you";
//可以修改
str[0] = 'L';
printf("%s\n",str);
printf("%#x\n",str);
getchar();
}
//字符指针
void main(){
//内存连续排列
char *str = "how are you";
//不可以修改
//str[0] = 'H';
//str += 1;
//*str = 'H';
printf("%s\n", str);
printf("%#x\n", str);
//使用指针加法截取字符串
str += 3;
while (*str){
printf("%c",*str);
str++;
}
printf("\n完成");
getchar();
}
一些字符串中常用的函数:
//strlen(s) : 返回S的长度,不包括字符串结束符NULL;
//strcmp(s1,s2) :比较两个字符串是否相同,若s1==s2,返回0,若s1>s2则返回正数,若s1<s2则返回负数;
//strcat(s1,s2):将字符串s2连接到s1上,返回 s1;
//strcpy(s1,s2):将s2,复制到s1,返回 s1.//strchr 该函数返回在字符串 str 中第一次出现字符 w 的位置,如果未找到该字符则返回 NULL。
//strstr(str1,str2) 若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
void main(){
char *s1= "i love you";
char *s2 = "how are you";
char d[20] = {'w'};
strcpy(d, s1);//将s1,复制到d,返回 d
printf("%s\n", d);
strcat(d, s2);//将字符串s2连接到d上,返回 d;
printf("%s\n", d);
getchar();
}
void main(){
char str[20];//静态内存分配,会开辟的比实际中大,会造成浪费
char *p = str;
//指向了同一块内存
printf("str:%#x\n", str);
printf("p:%#x\n", p);
strcpy(p,"i love you");
printf("%s\n", str);
getchar();
}
void main(){
char *str = "I want go to USA!";
printf("%#x\n",str);
char *p = strchr(str, 'w');
printf("%#x\n", p);
if (p){
printf("索引位置:%d\n", p - str);
}else{
printf("未找到");
}
getchar();
}
以上是一些C语言的基础知识,下篇会重点介绍下C语言的结构体