C基础笔记二

指针踩坑点

1、指针

指针是一种数据类型,可用来保存内存地址,也占据一定内存。

1.1、空指针和野指针

NULL指针,它作为一个特殊的指针变量,表示不指向任何东西。注:不允许向NULL和非法地址拷贝内存。

char *p = NULL;//给p指向的内存区域拷贝内容strcpy(p, "charles"); //error

在使用指针时,要避免野指针的出现,野指针指向一个已删除的对象或未申请访问受限内存区域的指针。野指针如何产生呢?

指针变量未初始化

任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

指针释放后未赋值为NULL

指针在free或delete后未赋值 NULL。但它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。因此,释放后的指针应立即将指针置为NULL。

指针操作超越变量作用域

不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

注:

  • 在指针声明时,* 号表示所声明的变量为指针;在指针使用时,* 号表示操作指针所指向的内存空间。

  • * 相当通过地址找到指针指向的内存,再操作内存;* 放在等号的左边赋值是给内存赋值,写内存;* 放在等号的右边取值从内存中取值,读内存。

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;}Personstruct 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(){Studenta从偏移量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));

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值