问:如何解释int num = 5;
答:num是一个变量,所以要从三方面来回答,数据类型(占内存大小),作用范围(使用范围),存储类型来看。
问:如何解释指针以及指针的指针
*:取内容
&:取地址
int num = 5;
int * p = #
num++:指的是num对应的内存空间自增,num = 6
p++:p对应的内存空间+1,步长为4,即0x1004
(*p)++:p指向的内存空间加1,即num = 6
结论:
p == & num;
* p == num == * ( & num);
int num = 5;
int * 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个字节