目录
五、free()和delete是如何处理动态开辟内存的指针的?
目录
五、free()和delete是如何处理动态开辟内存的指针的?
一、#运算符
在字符串中包含宏参数;#把文本符号转化为可以替换的语言标识
字符串化;"#x"-->把宏参数转换成字符串;
#include <stdio.h>
//#define SQUARE(x) (printf("x square is %d",(x)*(x)))
//在字符串中包含宏参数;#把文本符号转化为可以替换的语言标识符
#define SQUARE(x) (printf(""#x" square is %d\n",(x)*(x)))
void main(void)
{
SQUARE(4+2);
SQUARE(4);
return 0;
}
输出结果:
4+2 square is 36
4 square is 16
二、## 运算符 预处理粘合剂
## 用它把两个语言符号替换成一个语言符号
#include <stdio.h>
#define XNAME(n) xn //有两个语言符号x、n;
int main(void)
{
int XNAME(1) = 10; //x1 = 10 这里x和n并没有合并成xn语言符号
printf("%d\n",x1);
return 0;
}
输出结果:报错;xn并没有没有替换成x1。
#include <stdio.h>
//#define XNAME(n) xn //有两个语言符号x、n;
#define XNAME(n) x##n //有两个语言符号x、n;
int main(void)
{
int XNAME(1) = 10; //x1 = 10
printf("%d\n",x1);
return 0;
}
输出结果10
三、 结构体中使用字符数组还是字符指针
#include <stdio.h>
#include <string.h>
#define SIZE 80
struct std
{
unsigned int id;
//unsigned *name;//四个字节,只能放一个地址,指针未初始化随机分配,此地址可能不允许被访问;
unsigned name[SIZE];
unsigned int age;
}per;
int main(void)
{
per.id = 0001;
strcpy(per.name,"ABCD");
//per.name = "ABCD";//把"ABCD"此字符串所在的地址赋给了指针变量,此地址是在静态内存当中不允许
//被修改
per.age = 10;
printf("%s\n",per.name);
}
总结:在结构体中存放字符串时,优先考虑使用数组,提前开辟大空间。
四、内存越界
char *p1 = "ABCDEFG";
char *p2 = (char *)malloc(strlen(p1));
strcpy(p2,p1);
//malloc()开辟一片以字节为单位的动态内存空间,返回值为void型。
//设想:p2指向了新开辟的大小为strlen(p1)的内存空间,再把p1里的字符串复制给p2
//此处 strlen(p1) = 6,不包含"\0"。
//strcpy:包含"\0"空字符一定拷贝的。
//printf():在打印时,直到遇到"\0"结束。
在上段代码中,在开辟内存空间时,少了一位。
五、free()和delete是如何处理动态开辟内存的指针的?
free():只是把指针指向的内存释放掉了,指针仍然存在。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char *p = (char *)malloc(100);//开辟一块100字节的内存空间。
strcpy(p,"ABCABC");
printf("p = %s\n",p);
free(p);//p仍然存在,可以操作
if(p != NULL)
{
strcpy(p,"hello world");
printf("p = %s\n",p);
}
return 0;
}
delete:删除掉,
#icnlude <iostream>
#include <cstring>
using namespace std;
int main(void)
{
char *p = new char[100];
strcpy(p,"ABCABC");
cout << "p = "<< p << endl;
delete []p;
if(p != 0)
{
strcpy(p,"hello world!");
cout << "p = "<< p << endl;
}
return 0;
}
总结:free delete只是释放了内存空间,此时p就是一个悬空指针,为指向任何内存空间。尽量不要在使用,在释放掉内存之后,可以再把指针指向一个空地址,p = NULL;
六、Liunx内核源码分析
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
1.(TYPE *)0:将0强制类型转换为TYPE类型指针,p = (TYPE *)0
2.((TYPE *)0)->MEMBER ----> p->MEMBER 访问MEMBER成员变量
3.&((TYPE *)0)->MEMBER 取出MEMBER成员变量的地址
4.(size_t)&((TYPE *)0)->MEMBER 将MEMBER成员变量的地址转换程size_t类型的数据
总结:求MEMBER成员变量在TYPE 中的偏移量。
七、64和32位Liunx系统字节对齐问题
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
typedef struct s
{
union //联合体公用一块内存
{
int a; //4个字节
char str[10]; //10个字节
}
struct s* next;
}S;
int main(void)
{
printf("%ld\n",offsetof(S,next)); //%ld size_t
return 0;
}
理论输出结果为10,但是实际结果为16.
64位操作系统,最小操作内存空间大小位8字节,所以至少16个字节才可以存放下此联合体。
32位操作系统,最小可操作内存空间大小为4字节,所以至少需要12字节才可存放此联合体。
#pragma pack(2) //当前两字节对齐
八、const指针与指向const的指针
const 关键字的作用:
1.const定义一个常量,这个常量在定义是必须初始化,否则就没有机会了
2.const定义的常量,并不是真正意义上的常量,本质上还是变量,可以用指针的方式修改他的值。
3.const和指针的用法
const int *pt;
int const *pt; //两者相同,都表示pt可以指向任意对象,但是不能通过pt修改指针修改指
向的对象。
int *const pt; //pt指针不能指向其他位置,但是可以通过pt指针修改内容。
4.const 修饰形参
#include <stdio.h>
int main(void)
{
const int i = 10;
const int *pt = &i;
*pt = 20; //会报错
printf("i = %d\n",i);
}