动态内存与结构体

动态内存
堆内存 手动申请 手动释放
#include <stdlib.h>
       void *malloc(size_t size);//申请动态内存

申请size个字节的动态内存(堆内存 heap)
返回void *类型的指针 使用时需要转换成其他类型的指针
如果申请失败则返回NULL 成功则返回一个在heap上的内存地址

申请多少个字节的动态内存使用多少个字节,千万别越界访问
越界访问动态内存非常危险,编译不会检查
可能正确执行 可能代码会有逻辑错误 可能程序会崩溃

new/delete --> malloc --> brk/sbrk --> mmap/unmmap --> valloc
不管申请多少个字节的动态内存 系统至少分配33页的动态内存

一般来说,小内存一般用栈,大内存一般用堆

void free(void *ptr);//释放动态内存
同一块动态内存不能多次释放
多次释放 double free (核心段错误)

void *calloc(size_t nmemb, size_t size);//申请动态内存

申请nmemb个size字节大小的内存 总共nmemb*size个字节
calloc会把申请的动态内存全部清"零"

 void *realloc(void *ptr, size_t size);//调用动态内存大小

调用动态内存ptr的大小 ptr一定要是动态内存
size: 把动态内存调整为size个字节
调整之后的动态内存通过返回值得到 一定要去接收返回值 如果失

返回NULL
ptr参数的内存可能会被释放 新的内存通过返回值返回
原来动态内存中的数据会拷贝到新的动态内存里面
也有可能直接在后面扩容(后面的内容没有使用的情况下)

练习:

写个函数,把一个字符串里的函数逐个单词逆序 从控制台读取字符串 保存到动态内存中,然后调用该函数
“Hello world java” —> “olleH dlrow avaj”

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//按单词逆转 找到每个单词的首尾
char *reversebyword(char *s){	
	if(s==NULL){
		return NULL;	
	}
	//Hello a java 
	char *pstr = s;//最后需要返回 记录一下而已
	char *ps = s;//指针指向单词首 pstart 记录单词开始
	char *pe = s;//指针指向单词尾 pend  记录单词结尾
	for(;;s++){
		if(*s==' '||*s=='\0'){
			if(pe-1>ps){//单词长度超过1 pe会指向单词后面的空格  需要把单词逆转
				--pe;
				while(ps<pe){
					char c = *ps;
					*ps = *pe;
					*pe = c;
					++ps;
					--pe;
				}
			}
			if(*s=='\0'){
				break;	
			}
			ps = s+1;//跳过空格
			pe = s+1;//跳过空格
		}else{//不是空格
			++pe;
		}
	}
	return pstr;
}

int main(){
	char *str = calloc(100,1);
	fgets(str,100,stdin);
	size_t len = strlen(str);
	if(str[len-1]=='\n'){
		str[len-1] = '\0';	
	}

	reversebyword(str);
	puts(str);
	free(str);
	return 0;	
}

结构体

姓名 char name[40];
char *name = malloc(40);
年龄 int age;
int score[3];
变量与变量之间没有关联
数据传递比较复杂

结构体是一种自定义类型
数组: 同类型的一组变量
结构体: 不同类型的一组变量

结构体需要定义 定义类型
struct 类名{
类型 成员名;//定义结构体类型时 不能对成员进行赋值(初始化)

};
在结构体内部定义的变量 称为 成员变量

定义结构体变量:
结构体类型
struct 类名 变量名;
如果不对变量进行初始化 则结构体变量中的每一个成员都是垃圾值

结构体变量访问成员用 .
结构体变量名.成员名

结构体变量的初始化
struct 类名 变量名 = {...}; //在{}在依次给成员变量初始值 
struct 类名 变量名 = {.成员名=v,.成员名=v,...};
struct 类名 变量名 = {};// 全部初始化为"零"

结构体变量定义之后 ,不能再进行整体赋值
变量名 = {};//错误的
如果需要改变结构体成员的值,需要对特定的成员进行赋值操作

//定义结构体类型   一般在main函数外面   多文件编程时 一般在.h中定义类型
struct Stu{
	int no;
	char name[40];
	int score[3];
};
	
struct Stu s;//定义结构体变量  每个成员都是垃圾值
struct Stu s1 = {110,"张三",{100,100,100}};
struct Stu s2 = {.name="李四",.no=119,.score={100,80,90}};
struct Stu s3 = {};//全部初始化为"零"

访问结构体变量的成员 和 赋值 :
s1.no = 120;
strcpy(s1.name,"王五")
s1.score[0] = 100; s1.score[1] = 90; s1.score[2] = 100

结构体变量之间可以相互初始化和赋值

结构体指针变量:
struct 类型名 * 变量名;
结构体指针变量访问成员:
(*结构体指针变量).成员名
结构体指针变量->成员名
运算符的优先级: . > *
. ->

练习:

1.定义学生类型结构体,成员有 学号、姓名、语数外三门成绩
从键盘输入五位学员信息 输出这五个学员的信息 并计算平均值

#include <stdio.h>

//结构体类型
struct Stu{
	int no;
	char name[40];
	int score[3];
};

void readStu(struct Stu *ps){
	printf("请输入学号:");
	scanf("%d",&ps->no);//&(ps->no)
	printf("请输入姓名:");
	scanf("%s",ps->name);
	printf("请输入三门功课的成绩:");
	int i;
	for(i=0;i<3;i++){
		scanf("%d",&ps->score[i]);	
	}
}

void show(struct Stu stuArr[],size_t len){
	int i;
	for(i=0;i<len;i++){
		printf("学号:%d\n",stuArr[i].no);
		printf("姓名:%s\n",stuArr[i].name);
		printf("成绩:");
		int j;
		int s = 0;
		for(j=0;j<3;j++){
			printf("%d ",stuArr[i].score[j]);	
			s += stuArr[i].score[j];
		}
		printf(" avg:%g\n",s/3.0);
	}
}

int main(){
	struct Stu s = {};
	struct Stu stuArr[5] = {};//定义结构体数组变量
	int i;
	for(i=0;i<5;i++){
		readStu(&stuArr[i]);	
	}
	show(stuArr,5);	
	return 0;	
}


结构体的对齐补齐:

结构体成员对齐摆放: 从自身长度的整数倍的位置开始存放 超过4按4计算
结构体长度补齐: 结构体的长度会补齐为成员最大长度的整数倍 如果超过4按4计算

struct A{//4    ssc*
	short s;
	char c;
};
struct B{//8    c---iiii
	char c;
	int i;
};
struct C{//8    ppppc***
	int (*p)[5];
	char c;
};
struct D{//8    ppppcc**
	char *p;
	char s[2];
};
struct E{//20  c---12c***
	char c;
	int *arr[3];
	char x;
};

位域 位段

struct A{
	char c1:1;
	char c2:3;
	char c3:2;
};
sizeof(struct A)  == 1
struct B{
	char c1:4;
	char c2:3
	char c3:2
};
sizeof(struct B) == 2
struct C{
	int i1:1;
	int i2:2;
};
sizeof(struct C) == 4

struct A{
	char c1:4;
	char c2:3;
};
struct A a = {};
char c = 95;
//01011111
memcpy(&a,&c,1);
printf("%d",a.c1);// 1111        -1
printf("%d",a.c2);// 101         -3
浮点类型不能作为位域的成员类型
位段不可以取地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值