char * p1,p2:p1是字符指针、p2是字符变量;解决方法:typedef char CHAR,这样就不会出现上述情况typedef的内容即可void a = 10;错误void sum(void)相当于告诉用户这里不需要传入参数,当在main函数里面传参的时候会警告,函数返回值同理void *
→
\rightarrow
→ 万能指针
→
\rightarrow
→ 不需要强制类型转换即可给其他指针赋值sizeof a这么写也可以unsigned intvoid test01()
{
int a = 10;
//直接修改
a = 20;
printf("a = %d\n", a);
//间接修改
int * p = &a;
*p = 100;
printf("a = %d\n", a);
}
//对于自定义数据类型
struct Person
{
char a;
int b;
char c;
int d;
};
void test02()
{
struct Person p1 = { 'a', 10, 'b', 20 };
//直接修改,用点域
p1.d = 1000;
//间接修改,用“箭头”
struct Person * p = &p1;
p->d = 2000;
}
struct Person * p;时候,p++表示访问下一个同类结构体char *p = &p1,获得结构体的首地址,然后按char型进行遍历访问,当访问到int型成员时,再强制转换。void test02()
{
struct Person p1 = { 'a', 10, 'b', 20 };
struct Person * p = &p1;
char * pPerson = p;
// 用char指针遍历,遇到需要的数组直接强制转换
printf("d = %d\n", *(int*)(pPerson + 12));
// 先强制转换成int型指针,然后再将指针指向的数据强制int
printf("d = %d\n", *(int*)((int*)pPerson +3) );
}

malloc)和释放(free),若程序员不释放,程序结束时由操作系统回收。不要返回局部变量的地址;
局部变量在函数执行之后就释放了,我们没有权限取操作释放后的内存
//栈,不要返回局部变量的地址
int * func()
{
int a = 10;
return &a;
}
void test01()
{
int * p = func();
//a的内存已经被释放了,我们没有权限去操作这块内存
//这两个打印的结果虽然是对的,但是原理不准确
printf("a = %d\n", *p);
printf("a = %d\n", *p);
}
char * getString()
{
char str[] = "hello world";
// 返回字符串名字相当于返回字符串的首地址
// 同理,在程序释放后就消失了
return str;
}
void test02()
{
char * p = NULL;
// 会受到无效的指针
p = getString();
printf("%s\n", p);
}
int * getSpace()
{
// 实际上开辟的是一个具有5个元素的数组
int * p = malloc(sizeof(int)* 5);
if (p == NULL)
{
return NULL;
}
for (int i = 0; i < 5;i++)
{
p[i] = i + 100;
}
// 将堆区的地址指针返回给main函数
// 自身p在getSpace()结束后消失
return p;
}
void test01()
{
int * p = getSpace();
for (int i = 0; i < 5;i++)
{
printf("%d\n", p[i]);
}
// 手动在堆区创建的数据,记得手动释放
free(p);
// free(p)后指针p指向“无依靠”,因此设置NULL承接p规范一些
p = NULL;
}
// 比主调函数高一级的指针承接
void allocateSpace2(char ** pp)
{
char * temp = malloc(100);
memset(temp, 0, 100);
strcpy(temp, "hello world");
*pp = temp;
}
void test03()
{
char * p = NULL;
allocateSpace2(&p);
// 这样才能打印出字符串
printf("%s\n", p);
free(p);
p = NULL;
}
编译阶段分配内存,只能在当前文件内使用,只初始化一次
全局变量,C语言下默认的全局变量前都隐藏的加了该关键字
//1、静态变量
static int a = 10;
//特点:只初始化一次,在编译阶段就分配内存,属于内部链接属性,只能在当前文件中使用
void test01()
{
static int b = 20;
//局部静态变量,作用域只能在当前test01中
}
//2、全局变量
extern int g_a = 100;
//在C语言下 全局变量前都隐藏加了关键字 extern,属于外部链接属性
void test02()
{
extern int g_b;
//告诉编译器 g_b是外部链接属性变量,下面在使用这个变量时候不要报错
printf("g_b = %d\n", g_b);
}
| const修饰全局变量 | const修饰局部变量 | |
|---|---|---|
| 直接修改 | 失败 | 失败 |
| 间接修改 | 失败 → \rightarrow → 常量区,受到保护 | 成功 → \rightarrow → 放在栈区,本身是伪常量 → \rightarrow → 伪常量不能用来定义数组 |
# define sum(x,y) x+y
void test()
{
int res = 0;
int a = 10;
int b = 20;
res = sum(a,b)*20;
// 结果显示是410
// 这是因为:10+20*20 = 10+400 = 410
// 宏定义会出现这样的简单替换
// 解决方法:增加括号
printf("The result is %d.\n",res)
}
一个函数调用过程所需要的信息一般包括以下几个方面:
主调函数和被调函数都必须有一致的约定 → \rightarrow → 正确的调用函数 → \rightarrow → 调用惯例
| 调用惯例 | 出栈方(对函数而言) | 参数传递 | 名字修饰 |
|---|---|---|---|
| cdecl(默认) | 调用方 | 从右至左参数入栈 | 下划线+函数名 |
| stdcall | 本身 | 从右至左参数入栈 | 下划线+函数名+@+参数字节数 |
| fastcall | 本身 | 前两个参数由寄存器传递,其余参数通过堆栈传递 | @+函数名+@+参数的字节数 |
| pascal | 函数本身 | 从左至右参数入栈 | 较为复杂,参见相关文档 |
要根据程序的生命周期决定