c语言==指针(8)

问:如何解释int num = 5;

答:num是一个变量,所以要从三方面来回答,数据类型(占内存大小),作用范围(使用范围),存储类型来看。

这里写图片描述

问:如何解释指针以及指针的指针

*:取内容
&:取地址

int num = 5int * p = #

这里写图片描述
num++:指的是num对应的内存空间自增,num = 6
p++:p对应的内存空间+1,步长为4,即0x1004
(*p)++:p指向的内存空间加1,即num = 6

结论:
p == & num;
* p == num == * ( & num);

int num = 5int * p = #
int ** pp = &p;

这里写图片描述
(*pp)++:即pp指向的内存空间加1,即p++,即0x1004
pp++:pp对应的内存空间加1,即0x2004
(**pp)++:pp指向的内存空间指向的内存空间加1,即num++

结论:
num == * p == * * pp == * (& num)
p == &num == * pp;

变量名++,就是对应的内存空间加1,(根据步长),*(变量名)++就是指向的内存空间加1,

字符串指针

char * ptr = “hello world”;
*ptr = ‘L’;
ptr++;

这里写图片描述
这个程序是错误的。
首先给这个指针赋初值,会把这个字符串的首地址给这个指针。但是这个初值是保存到常量段的,不可以进行改变,会报段错误。
ptr++就是对应的内存空间加1,是一个字节,所以加1,变为0x4001

char * ptr[3] = {“hello1”,“hello2”,“hello3”};
意思就相当于给三个字符数组分别像上面一样赋了初值。数组里保存的三个指针都对应了相应的地址。

对指针变量进行输入输出

int * p;
scanf("%d" , p); //给p指向的内存空间进行输入
printf("%d\n" ,*p);  //输出p指向空间的值
//但是要输出字符串的字符指针,printf只要写p就行了。因为给的是一个首地址

会出现段错误,因为虽然给p分配空间了,但是没给p保存的地址分配空间,相当于系统给了一个垃圾地址,可能有用,可能没用。
此时的p是野指针,会造成内存泄漏

野指针的成因
1、指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。
2、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
3、指针操作超越了变量的作用范围。这种情况让人防不胜防。

野指针的避免-正确使用指针
野指针出现了就是程序有问题,它在程序里是不能做任何判定的,所以只能避免
通常避免野指针的办法是正确的使用指针
1.声明一个pointer的时候注意初始化为null
int* pInt = NULL;
2.分配完内存以后注意ASSERT
pInt = new int[num];
ASSERT(pInt != NULL);
3.删除时候注意用对操作符
对于new int类型的,用delete
对于new int[]类型的,用delete []
4.删除完毕以后记得给他null地址
delete [] pInt;
pInt = NULL;
5.记住,谁分配的谁回收,不要再一个函数里面分配local pointer,送到另外一个函数去delete
6.返回local address是非常危险的,如必须这样做,请写注释到程序里面,免得忘记

野指针的科普

避免野指针的正确打开方式:

1、在定义一个指针的时候,就给他置空。
2、然后malloc给他分配空间。
3、malloc之后判断他是否真的给指针分配了空间
4、分配了空间之后要注意清空所分配的空间。
5、使用之后要释放指针并将指针再次置空

1、将指针置为NULL。
NULL有一个头文件,宏定义。即#define NULL (void *)0
所以NULL的意思就是0地址。值得注意的是,我们是不能够对0进行访问与操作的。所以,如果不进行接下来的第二步,会报段错误。所以把他置NULL的意义也包括易于检查野指针。

//void * 是一个万能指针, 吗

2、malloc分配空间。

char * ptr;
ptr = (char *)malloc (sizeof(char) * MAX_SIZE);

//强制转换为(char *)的原因:c语言中指针之间的赋值都必须是同等类型的,防止少取或越界。
//乘以sizeof(char)的原因:为了好的移植性,因为不同的机器char的字长可能不一样。
//有时候也会在头文件的位置宏定义,MAX_SIZE=sizeof(char) * 100的原因:为了更好的维护性

3、判断是否真的分配了空间。

if(NULL == ptr)
{
    printf("ptr is NULL\n");
    exit(1);
}

4、清空所分配的空间
memset:将某字符串的前n个字符变成某数
bzero:将某字符串的前n个字符变成某数

memset(ptr,0,MAX_SIZE * sizeof(char));
ptr指的是字符串的首元素的地址
0指的是用来替换的数字。
sizeof(char)*MAX_SIZE指的是从首元素开始被替换的元素个数

bzero(ptr,MAX_SIZE * sizeof(char));
bzero只可以置0,但是不可以置为别的数,而memset可以

5、释放指针并置空
free(ptr);
ptr = NULL;
因为后面可能还有好多行代码,还有要用到ptr的情况。要是不置空的话,ptr还是指向原来分配的那个空间的首地址。

malloc的实现

malloc()在操作系统中的实现

  在 C 程序中,多次使用malloc () 和 free()。不过,您可能没有用一些时间去思考它们在您的操作系统中是如何实现的。本节将向您展示 malloc 和 free 的一个最简化实现的代码,来帮助说明管理内存时都涉及到了哪些事情。

  在大部分操作系统中,内存分配由以下两个简单的函数来处理:

  void *malloc (long numbytes):该函数负责分配 numbytes 大小的内存,并返回指向第一个字节的指针。

  void free(void *firstbyte):如果给定一个由先前的 malloc 返回的指针,那么该函数会将分配的空间归还给进程的“空闲空间”。

  malloc_init 将是初始化内存分配程序的函数。它要完成以下三件事:将分配程序标识为已经初始化,找到系统中最后一个有效内存地址,然后建立起指向我们管理的内存的指针。这三个变量都是全局变量:

  清单 1. 我们的简单分配程序的全局变量

    int has_initialized = 0;
     void *managed_memory_start;
     void *last_valid_address;

  如前所述,被映射的内存的边界(最后一个有效地址)常被称为系统中断点或者 当前中断点。在很多 UNIX? 系统中,为了指出当前系统中断点,必须使用 sbrk(0) 函数。 sbrk 根据参数中给出的字节数移动当前系统中断点,然后返回新的系统中断点。使用参数 0 只是返回当前中断点。这里是我们的 malloc 初始化代码,它将找到当前中断点并初始化我们的变量:

  清单 2. 分配程序初始化函数

/* Include the sbrk function */

 #include 
 void malloc_init()
 {
 /* grab the last valid address from the OS */
 last_valid_address = sbrk(0);
 /* we don't have any memory to manage yet, so
  *just set the beginning to be last_valid_address
  */
 managed_memory_start = last_valid_address;
 /* Okay, we're initialized and ready to go */
  has_initialized = 1;
 }

  现在,为了完全地管理内存,我们需要能够追踪要分配和回收哪些内存。在对内存块进行了 free 调用之后,我们需要做的是诸如将它们标记为未被使用的等事情,并且,在调用 malloc 时,我们要能够定位未被使用的内存块。因此, malloc 返回的每块内存的起始处首先要有这个结构:

  清单 3. 内存控制块结构定义

struct mem_control_block {
 int is_available;
 int size;
 };

   现在,您可能会认为当程序调用 malloc 时这会引发问题 —— 它们如何知道这个结构?答案是它们不必知道;在返回指针之前,我们会将其移动到这个结构之后,把它隐藏起来。这使得返回的指针指向没有用于任何其他用途的内存。那样,从调用程序的角度来看,它们所得到的全部是空闲的、开放的内存。然后,当通过 free() 将该指针传递回来时,我们只需要倒退几个内存字节就可以再次找到这个结构。

  在讨论分配内存之前,我们将先讨论释放,因为它更简单。为了释放内存,我们必须要做的惟一一件事情就是,获得我们给出的指针,回退 sizeof(struct mem_control_block) 个字节,并将其标记为可用的。这里是对应的代码:

  清单 4. 解除分配函数

void free(void *firstbyte) {
 struct mem_control_block *mcb;
 /* Backup from the given pointer to find the
  * mem_control_block
  */
 mcb = firstbyte - sizeof(struct mem_control_block);
 /* Mark the block as being available */
 mcb->is_available = 1;
 /* That's It!  We're done. */
 return;
 }

  如您所见,在这个分配程序中,内存的释放使用了一个非常简单的机制,在固定时间内完成内存释放。分配内存稍微困难一些。以下是该算法的略述:

  清单 5. 主分配程序的伪代码

1. If our allocator has not been initialized, initialize it.
 2. Add sizeof(struct mem_control_block) to the size requested.
 3. start at managed_memory_start.
 4. Are we at last_valid address?
 5. If we are:
    A. We didn't find any existing space that was large enough
       -- ask the operating system for more and return that.
 6. Otherwise:
    A. Is the current space available (check is_available from
       the mem_control_block)?
    B. If it is:
       i)   Is it large enough (check "size" from the
            mem_control_block)?
       ii)  If so:
            a. Mark it as unavailable
            b. Move past mem_control_block and return the
               pointer
       iii) Otherwise:
            a. Move forward "size" bytes
            b. Go back go step 4
    C. Otherwise:
       i)   Move forward "size" bytes
       ii)  Go back to step 4

  我们主要使用连接的指针遍历内存来寻找开放的内存块。这里是代码:

  清单 6. 主分配程序

void *malloc(long numbytes) {
 /* Holds where we are looking in memory */
 void *current_location;
 /* This is the same as current_location, but cast to a
  * memory_control_block
  */
 struct mem_control_block *current_location_mcb;
 /* This is the memory location we will return.  It will
  * be set to 0 until we find something suitable
  */
 void *memory_location;
 /* Initialize if we haven't already done so */
 if(! has_initialized) {
 malloc_init();
 }
 /* The memory we search for has to include the memory
  * control block, but the users of malloc don't need
  * to know this, so we'll just add it in for them.
  */
 numbytes = numbytes + sizeof(struct mem_control_block);
 /* Set memory_location to 0 until we find a suitable
  * location
  */
 memory_location = 0;
 /* Begin searching at the start of managed memory */
 current_location = managed_memory_start;
 /* Keep going until we have searched all allocated space */
 while(current_location != last_valid_address)
 {
 /* current_location and current_location_mcb point
  * to the same address.  However, current_location_mcb
  * is of the correct type, so we can use it as a struct.
  * current_location is a void pointer so we can use it
  * to calculate addresses.
  */
 current_location_mcb =
 (struct mem_control_block *)current_location;
 if(current_location_mcb->is_available)
 {
 if(current_location_mcb->size >= numbytes)
 {
 /* Woohoo!  We've found an open,
  * appropriately-size location.
  */
 /* It is no longer available */
 current_location_mcb->is_available = 0;
 /* We own it */
 memory_location = current_location;
 /* Leave the loop */
 break;
 }
 }
 /* If we made it here, it's because the Current memory
  * block not suitable; move to the next one
  */
 current_location = current_location +
 current_location_mcb->size;
 }
 /* If we still don't have a valid location, we'll
  * have to ask the operating system for more memory
  */
 if(! memory_location)
 {
 /* Move the program break numbytes further */
 sbrk(numbytes);
 /* The new memory will be where the last valid
  * address left off
  */
 memory_location = last_valid_address;
 /* We'll move the last valid address forward
  * numbytes
  */
 last_valid_address = last_valid_address + numbytes;
 /* We need to initialize the mem_control_block */
 current_location_mcb = memory_location;
 current_location_mcb->is_available = 0;
 current_location_mcb->size = numbytes;
 }
 /* Now, no matter what (well, except for error conditions),
  * memory_location has the address of the memory, including
  * the mem_control_block
  */
 /* Move the pointer past the mem_control_block */
 memory_location = memory_location + sizeof(struct mem_control_block);
 /* Return the pointer */
 return memory_location;
  }

  这就是我们的内存管理器。现在,我们只需要构建它,并在程序中使用它即可。

malloc relloc calloc的用法及区别及实现

超链接

指针名加上一个整数,如1,ptr + 1,
若ptr是char类型的,则加1
若是int类型的,则加4

指针名减去另一个指针名,得到的是两指针之间的距离,即数据的个数。

int a[] = {1,2,3}
sizeof(a) = 12
因为是三个数,每个数据是4个字节,所以是12个字节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值