C语言基础知识总结(一)

好久没有写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语言的结构体



 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值