C基础笔记二
Posted Charles梦想家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C基础笔记二相关的知识,希望对你有一定的参考价值。
指针踩坑点
1、指针
指针是一种数据类型,可用来保存内存地址,也占据一定内存。
1.1、空指针和野指针
NULL指针,它作为一个特殊的指针变量,表示不指向任何东西。注:不允许向NULL和非法地址拷贝内存。
char *p = NULL;//给p指向的内存区域拷贝内容
strcpy(p, "charles"); //error
在使用指针时,要避免野指针的出现,野指针指向一个已删除的对象或未申请访问受限内存区域的指针。野指针如何产生呢?
n 指针变量未初始化
任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
n 指针释放后未赋值为NULL
指针在free或delete后未赋值 NULL。但它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。因此,释放后的指针应立即将指针置为NULL。
n 指针操作超越变量作用域
不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。
注:
-
在指针声明时,* 号表示所声明的变量为指针;在指针使用时,* 号表示操作指针所指向的内存空间。
-
* 相当通过地址找到指针指向的内存,再操作内存;* 放在等号的左边赋值是给内存赋值,写内存;* 放在等号的右边取值从内存中取值,读内存。
int* ptr = NULL;
int a = 20;
ptr = &a;
*ptr = 20;//*在左边当左值,必须确保内存可写
int b = *p; //*号放右面,从内存中读值
1.2、间接赋值
通过指针间接赋值的条件:
1)2个变量(一个普通变量,一个指针变量)2)通过 * 操作指针指向的内存。
void example1(){
int a = 100; //两个变量
int *p = NULL;
p = &a; //指针指向谁,就把谁的地址赋值给指针
*p = 22; //通过*操作内存
}
间接赋值结果:
1、用1级指针去间接修改了0级指针的值。
2、用2级指针去间接修改了1级指针的值。
3、用n级指针去间接修改了n-1级指针的值。
1.3、指针易错点
1、越界
char buf[3] = "abc";
printf("buf:%s\\n",buf);
2、指针叠加会改变指针指向
char *p = (char *)malloc(50);
char buf[] = "abcdef";
int n = strlen(buf);
for (int i = 0; i < n; i++)
{
*p = buf[i];
p++;
}
free(p);
3、返回局部变量地址
char * returnStr()
{
char str[] = "charles"; //栈区
printf("[returnStr]= %s\\n", str);
return str;
}
4、同一块内存释放多次(避免)
void example(){
char *p = NULL;
p = (char *)malloc(50);
strcpy(p, "acharles");
if (p != NULL)
{
free(p);
}
if (p != NULL)
{
free(p);
}
}
1.4、const指针用法
struct Person{
char name[64];
int id;
};
//每次都对对象进行拷贝,效率低,应该用指针
void printPersonByValue(struct Person person){
printf("Name:%s\\n", person.name);
printf("Name:%d\\n", person.id);
}
//但是用指针会有副作用,可能会不小心修改原数据,因而可以用const修饰指针
void printPersonByPointer(const struct Person *person){
printf("Name:%s\\n", person->name);
printf("Name:%d\\n", person->id);
}
void example(){
struct Person p = { "charles", 007};
printPersonByPointer(&p);
}
结构体
1、结构体类型定义
typedef struct _PERSON{
char name[64];
int age;
}Person
struct Person{
char name[64];
int age;
}p1; //定义类型同时定义变量
struct Person{
char name[64];
int age;
}p1 = {"john",10}; //定义类型同时初始化变量
struct Person p3 = {"charles",23}; //通过类型直接定义
1.1、结构体使用
void example(){
//在栈上分配空间
struct Person p1;
strcpy(p1.name, "charles");
p1.age = 20;
//如果是普通变量,通过点运算符操作结构体成员
printf("Name:%s Age:%d\\n", p1.name, p1.age);
//在堆上分配空间
struct Person* p2 = (struct Person*)malloc(sizeof(struct Person));
strcpy(p2->name, "Obama");
p2->age = 23;
//如果是指针变量,通过->操作结构体成员
printf("Name:%s Age:%d\\n", p2->name, p2->age);
}
1.2、结构体内存对齐(重点)
内存的最小单元是一个字节,当cpu从内存中读取数据的时候,是一个一个字节读取;但是实际上cpu将内存当成多个块,每次从内存中读取一个块,这个块的大小可能是2、4、8、16,32,64等。
原因:内存对齐是操作系统为了提高访问内存的策略。操作系统在访问内存的时候,每次读取一定长度,以避免访问一个变量可能产生二次访问。
结构体对齐原则:
1. 数组成员对齐规则。第一个数组成员应该放在offset为0的地方,以后每个数组成员应该放在offset为最小(#pargama pack(n))整数倍的地方开始。
2. 结构体总的大小,也就是sizeof的结果,必须是最小(结构体内部最大成员,#pargama pack(n))的整数倍,不足要补齐。
3. 结构体成员的对齐规则。如果一个结构体B里嵌套另一个结构体A,还是以最大成员类型的大小对齐。
#pragma pack(4)
typedef struct _STUDENT{
int a;
char b;
double c;
float d;
}Student;
typedef struct _STUDENT2{
char a;
Student b;
double c;
}Student2;
void example(){
Student
a从偏移量0位置开始存储
b从4位置开始存储
c从8位置开始存储
d从16位置开存储
所以Student内部对齐之后的大小为20 ,整体对齐,整体为最大类型的整数倍 也就是8的整数倍 为24
printf("sizeof Student:%d\\n",sizeof(Student));
Student2
a从偏移量为0位置开始
b从偏移量为Student内部最大成员整数倍开始,也就是8开始
c从8的整数倍地方开始,也就是32开始
所以结构体Sutdnet2内部对齐之后的大小为:40 , 由于结构体中最大成员为8,必须为8的整数倍 所以大小为40
printf("sizeof Student2:%d\\n", sizeof(Student2));
以上是关于C基础笔记二的主要内容,如果未能解决你的问题,请参考以下文章