一、数组清零方式。
1、定义数组同时初始化0。
char A[10] = {0}; ---> 剩余没有赋值的成员都会赋值为0
缺点:在程序中只能定义一次数组,清零只是只有一次。
2、清空某段内存空间的数据。 ---> bzero() --> man 3 bzero
功能: bzero - write zero-valued bytes
//写入一些零值到内存空间上
#include <strings.h>
void bzero(void *s, size_t n);
参数:
s:需要清空的内存空间的地址
n:清空的长度
返回值:无
特点:多次调用,多次清零。
#include <stdio.h>
#include <strings.h>int main(int argc,char *argv[])
{
char A[10] = {"hello"};
A[8] = 'k';
A[9] = 'l';
int i;
for(i=0;i<10;i++)
{
printf("A[%d]:%c\n",i,A[i]);
}
bzero(A,sizeof(A));
for(i=0;i<10;i++)
{
printf("A[%d]:%c\n",i,A[i]);
}
return 0;
}
二、堆区空间的申请与释放。
1、堆区空间的特点?
堆空间,不像栈区那样通过申请变量而得到栈区空间,而是通过写代码调用malloc()函数去申请得到。
代码定义了局部变量 --> 等价于在栈区申请空间。
代码定义了全局变量 --> 等价于在bss段/data段申请空间。
代码定义"hello" --> 等价于在常量区申请空间。
代码出现malloc函数 --> 等价于在堆区申请空间。
堆区空间: 主动申请,主动释放。
注意:如果在一个函数中申请了堆区空间,请问在函数返回时,需要拿橡皮擦出来吗?
不需要,因为堆区空间不会因为函数返回而释放。
2、如何申请堆区空间?
1)申请堆区空间 -- malloc() --> man 3 malloc
功能: allocate dynamic memory
申请堆区空间
头文件:
#include <stdlib.h>
函数原型:
void *malloc(size_t size);
参数:
size:需要申请堆区空间的字节数
返回值:
成功:堆区空间的起始地址 --> 由于这个地址是void*的,所以后面使用这个地址需要强制转换为某种类型才可以使用。
失败:NULL
2)释放堆区空间。 -- free() --> man 3 free
功能: free dynamic memory
释放堆区空间
头文件:
#include <stdlib.h>
函数原型:
void free(void *ptr);
参数:
ptr:需要释放的那段堆区空间的地址
返回值:无
用户在程序中必须主动释放堆区空间,如果不主动释放,那么在程序结束时,也会主动释放。
-------------------------------------------
#include <stdio.h>
#include <stdlib.h>int main(int argc,char *argv[])
{
int *p = malloc(8); //等价于在堆区申请了4个字节的空间
p[0] = 10;
p[1] = 20;
printf("p[0] = %d\n",p[0]); //p[0] = *(p+0) = *p
printf("p[1] = %d\n",p[1]);
free(p);
return 0;
}
-------------------------------------------
3、如果堆区空间释放之后,还能不能访问这段堆空间。
能,但是那片空间的数据有可能已经更改。
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
//申请空间: 该空间从不属于你变成属于你。
int *p = malloc(20);
p[0] = 10;
p[1] = 20;
p[2] = 30;
p[3] = 40;
p[4] = 50;
printf("p[0] = %d\n",p[0]);
printf("p[1] = %d\n",p[1]);
printf("p[2] = %d\n",p[2]);
printf("p[3] = %d\n",p[3]);
printf("p[4] = %d\n",p[4]);
//释放空间: 该空间从属于你变成不属于你。
free(p);
p = NULL; //以后写代码,为了安全起见,最好写上这句话,目的就是为了我们再次去访问该堆区空间。
printf("p[0] = %d\n",p[0]);
printf("p[1] = %d\n",p[1]);
printf("p[2] = %d\n",p[2]);
printf("p[3] = %d\n",p[3]);
printf("p[4] = %d\n",p[4]);
return 0;
}
练习1: 在一个自定义函数中申请堆空间,测试看看函数返回时候,这个堆区空间还在不在。
#include <stdio.h>
#include <stdlib.h>
int *func(int x)
{
int *p = malloc(4);
p[0] = x;
return p;
}
int main(int argc,char *argv[])
{
int a = 100;
int *p = func(a);
printf("p[0] = %d\n",p[0]);
free(p);
p = NULL;
return 0;
}
三、关于堆栈的易错点。
问题:以下代码是否有问题?如果没有问题,请画出内存图并指出每句话是什么意思,如果有问题,请指出问题所在。
1、
char A[10] = "hello"; //在内存空间中连续申请10个字节的空间,然后使用变量A间接访问这片空间,然后将常量区中的"hello"字符串拷贝到这个变量A所代表的空间上
strcpy(A,"world"); //把常量区的"world"字符串拷贝到变量A所代表的空间。
2、
char *p = "hello";
strcpy(p,"world");
3、
char A[10];
char *p = A;
strcpy(p,"hello");
4、
char A[10];
char *p = A;
p = "hello";
5、
char *p = malloc(sizeof(char)*10);
p = "hello";
6、
char *p = malloc(sizeof(char)*10);
strcpy(p,"hello");
第5与第6的区别:
第5,一开始p是指向堆区,然后接下来p就指向常量区。
第6,一开始p是指向堆区,然后接下来p依然是指向堆区,但是把常量区hello这个字符串拷贝到p所指向的堆区空间。
四、结构体。
1、什么是结构体?
多个相同类型的变量放在一个集合中,那么这个集合叫做数组。 --> int A[5] --> 5个int类型的数据
多个不同类型的变量放在一个集合中,那么这个集合叫做结构体。
结构体中可以包含哪些类型的数据呢?
1)基本数据类型:char/short/int/long/float/double
2)非基本数据类型:数组/指针/结构体
结构体中不可以包含哪些类型的数据呢?
不可以在结构体中定义函数。
总结: 结构体可以理解为就是一种新的数据类型,这种新类型有什么东西组成的呢?而是由我们用户自定义的。
也就是说,你想这个结构体里面有什么东西组成,由你来决定。
2、结构体既然说是属于用户自定义的类型,那么这种类型如何定义的?
关键词:struct
模型:
struct 结构体名字{
/* 结构体的组成变量 */
....;
....;
....;
....;
}; --> 定义结构体之后,记住{}后面有一个分号。
例如:
struct A{
char a;
int b;
double c;
char B[10];
};
这个"struct A"就是新类型的名字,这种类型是由一个字符变量,一个整型变量,一个浮点型变量,一个字符数组组成。
3、结构体的出现是为了解决什么问题?
如果一个"东西",需要描述这个东西的类型非常多,那么就可以考虑使用结构体类型。
例如:要描述关老师这个人,需要姓名,年龄,电话号码..,那么就可以考虑使用结构体。
struct person{
char name[10];
int age;
char tel[12];
};
“struct person”就是一种新的数据类型,专门用于描述一个人的。
4、如何定义结构体变量?
1)先声明好结构体的样子。
struct person{
char name[10];
int age;
char tel[12];
};
系统就会知道"struct person"这种新类型由“char name[10] + int age + char tel[12]”这三个东西组成。
"struct person"就是新类型的数据类型的名字。
2)struct person就是新类型的数据类型的名字。
3)定义结构体变量。
定义变量公式: 数据类型 + 变量名
struct person ggy; --> ggy变量里面有三个成员
5、如何定义结构体指针?
1)先声明好指针指向的目标。
struct person ggy;
2)定义结构体指针。
struct person *p = NULL;
3)赋值。
p = &ggy;
4)解引用。
*p = *(&ggy) = ggy
6、结构体变量如何访问成员?
1)先声明好结构体的样子。
struct person{
char name[10];
int age;
char tel[12];
};
2)定义结构体变量。
struct person ggy;
3)结构体变量是使用"."来访问成员的。
strcpy(ggy.name,"ggy");
ggy.age = 10;
strcpy(ggy.tel,"10086");
7、结构体指针如何访问成员?
1)先声明好结构体的样子。
struct person{
char name[10];
int age;
char tel[12];
};
2)定义结构体变量。
struct person ggy;
3)定义结构体指针。
struct person *p = &ggy;
4)结构体指针使用"->"来访问成员。
strcpy(p->name,"ggy");
p->age = 10;
strcpy(p->tel,"10086");
还可以这样:
strcpy((*p).name,"ggy");
(*p).age = 10;
strcpy((*p).tel,"10086");
8、结构体成员赋值。
1)使用变量或者指针来对单个成员赋值。
struct usr_data{
char name[10];
int age;
};
struct usr_data a;
struct usr_data *p = &a;
a.age = 18;
p->age = 18;
2)使用初始化列表。
struct usr_data a = {"ggy",18};
struct usr_data a = {"ggy"}; --> 剩余没有赋值的成员如果是普通变量,则是0,如果指针变量,则是NULL。
3)整个结构体赋值。
struct usr_data a = {"ggy",18};
struct usr_data b;
b = a;
练习1: 研究结构体数组怎么写?
定义数组的步骤:例子1: 定义具有5个int类型数据的数组。
1)确定一个数组名。 A
2)确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面 A[5]
3)确定数组每一个元素是什么 int x
4)将第3步的结果的变量名去掉 int
5)将第4步的结果写在第2步结果的前面就可以 int A[5] --> 最终结果
A[0] --> int
A[1] --> int
A[2] --> int
A[3] --> int
A[4] --> int
例子2: 定义5个struct person类型数据的数组。
struct person{
char name[10];
int age;
};
1)确定一个数组名 B
2)确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面 B[5]
3)确定数组每一个元素是什么 struct person x
4)将第3步的结果的变量名去掉 struct person
5)将第4步的结果写在第2步结果的前面就可以 struct person B[5]
B[0] --> struct person
B[1] --> struct person
B[2] --> struct person
B[3] --> struct person
B[4] --> struct person
练习2: 做一个通讯录,里面存放三个同学的信息,每一个同学信息有学号,姓名,年龄,电话号码。
程序运行之后,先依次对三个同学的学号,姓名,年龄,电话号码进行注册(键盘输入信息),全部同学的信息都注册完了之后,就输出全部同学的信息。
#include <stdio.h>
#include <strings.h>
struct person{
int num; //学号
char name[10]; //姓名
int age; //年龄
char tel[121]; //电话号码
};
int main(int argc,char *argv[])
{
//1. 定义具有3个struct person类型的数组
struct person addr_list[3];
bzero(addr_list,sizeof(addr_list));
//addr_list[0] -> 第一个同学 addr_list[1] -> 第二个同学 addr_list[2] -> 第三个同学
//2. 依次对同学进行赋值。
int i;
for(i=0;i<3;i++)
{
printf("pls input %d std num:",i+1);
scanf("%d",&addr_list[i].num);
printf("pls input %d std name:",i+1);
scanf("%s",addr_list[i].name);
printf("pls input %d std age:",i+1);
scanf("%d",&addr_list[i].age);
printf("pls input %d std tel:",i+1);
scanf("%s",addr_list[i].tel);
}
//3. 输出全部同学的信息
for(i=0;i<3;i++)
{
printf("%d %s %d %s\n",addr_list[i].num,addr_list[i].name,addr_list[i].age,addr_list[i].tel);
}
return 0;
}
五、计算结构体在内存中占用的字节数。
int -> 4个字节
double -> 8个字节
struct person -> ?个字节
例子1:
struct data{
char name[24]; 24 32 ---> 32
long a;
};
例子2:
struct data{
long a; 32 24 --> 32
char name[24];
};
例子3:
struct data{
double a; 16 9 8 --> 16
char b;
};
例子4:
struct data{
char a;
short c;
char b;
short d;
char e;
char f; 8 12 2 --> 10
};
例子5:
struct data{
char a;
char b;
short c;
short d;
char e;
char f; 8 --> 8
};
例子6:
struct data{
char a;
short c;
short d;
char e;
char f;
char g;
short h;
char w; 11 14 16 --> 14
};
例子7:
struct data{
int a;
double b;
char c; 24 13
};
例子8:
struct data{
int a;
char c; 16
double b;
};
例子9:
struct data{
char c; 16
int a;
double b;
};
练习4: 计算以下结构体在内存中占用多少个字节? 82 64 104 94 92 144 88 96 84 86 80 97 120 --> 96
struct data{
int a;
char b;
int (*px)[5];
char A[9];
short c;
double e;
float d;
double *p;
int B[3];
char (*p)(char,int);
char q;
short w;
char o;
int t;
};
char -> 可以放在任意一个位置
short -> 12 34 56 78 -> 不能 23 45 67
int float -> 1234 5678 -> 不能 2345 3456 4567
double -> 12345678
六、联合体。
1、为什么会有联合体的出现?
为了解决结构体占用空间较大的情况。
2、联合体如何定义?
结构体:
struct data{
int a;
double b;
char c; --> 占用24个字节
};
联合体关键词:union,只需要将struct修改为union即可。
联合体
union data{
int a;
double b;
char c; --> 占用8个字节
};
3、结论。
1)在联合体中,所有的成员的起始地址都是一样的。
#include <stdio.h>
union data{
int a;
double b;
char c;
};
int main()
{
printf("%ld\n",sizeof(union data));
union data ggy;
printf("%p\n",&ggy.a);
printf("%p\n",&ggy.b);
printf("%p\n",&ggy.c);
return 0;
}
运行结果:
gec@ubuntu:/mnt/hgfs/GZ2180/01 C语言/10/code$ ./union
8
0x7fff5fa15c60
0x7fff5fa15c60
0x7fff5fa15c60
2)联合体的大小是以联合体最大的那个成员说了算。(看看是否要补0)
#include <stdio.h>
union data{
int a;
char A[9];
char c;
};
int main()
{
printf("%ld\n",sizeof(union data)); //12
return 0;
}
3)不能同时使用联合体的成员。
#include <stdio.h>
union data{
int a;
double b;
char c;
};
int main()
{
union data ggy = {10,3.14,'x'};
printf("%ld\n",sizeof(union data)); //8
return 0;
}
4)联合体与结构体一样,都是作为函数参数的,传递一个结构体变量/联合体变量过去,其实就是把整个值传递过去。
#include <stdio.h>
struct data{
int a;
double b;
char c;
};
int func(struct data *p) // B = A
{
//printf("%d\n",B.a);
printf("p->a = %d\n",p->a);
return 0;
}
int main()
{
struct data A;
A.a = 10;
func(&A);
printf("%ld\n",sizeof(struct data)); //8
return 0;
}
注意:一般很少传递结构体变量作为函数参数,因为结构体变量太大的,一般都是传递结构体的地址,使用一个结构体的指针变量来接着它,因为无论结构体多大,结构体指针都是占用8个字节。
七、枚举类型。
1、什么是枚举类型?
其实枚举类型就是一些整型变量,意义就是给常量一个身份。
例如:
1 -> success
0 -> warning
-1-> error
2、枚举类型作用场景是什么?
1)作用于switch语句。
2)函数返回值。
旧的写法:
void func()
{
if(xxxx)
return 1;
else if(xxxx)
return 0;
else
return -1;
}
使用枚举类型之后:
void func()
{
if(xxxx)
return success; --> 这样做的好处:别人看起来就知道返回值是什么情况。
else if(xxxx)
return warning;
else
return error;
}
3、如何定义枚举类型?
关键词:enum
模型:
enum mydata{
success = 1,
warning = 0,
error = -1,
};
#include <stdio.h>
enum mydata{
success = 1,
warning = 0,
error = -1,
};
int func(int n)
{
if(n == 18)
return success;
else if(n < 18)
return warning;
else
return error;
}
int main(int argc,char *argv[])
{
int age = 18;
int ret = func(age);
if(ret == 1)
printf("success!\n");
else if(ret == 0)
printf("warning!\n");
else
printf("error!\n");
return 0;
}
4、赋值的问题。
enum mydata{
success = 1, //1
warning = 0, //0
error = -1, //-1
};
enum mydata{
success, //0 如果没有赋值,则第一个成员从0开始。
warning, //1 后面没有赋值的成员都会在前一个成员基础上+1
error, //2
};
enum mydata{
success = 10, //10
warning = 20, //20
error, //21 后面没有赋值的成员都会在前一个成员基础上+1
};
enum mydata{
success = 10, //10
warning = 9, //9
error, //10
}