动态内存
堆内存 手动申请 手动释放
#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
浮点类型不能作为位域的成员类型
位段不可以取地址