宏函数
#define MYADD(x,y) ((x) + (y)
将一些频繁短小的函数 写成宏函数
宏函数优点:以空间换时间
示例
void test()
{
printf("%d\n", MYADD(10, 20) * 20);
}
int main(void)
{
system("pause");
return EXIT_SUCCESS;
}
普通函数有入栈、出栈时间开销
函数调用流程
局部变量、函数形参、函数返回地址.. 入栈 和 出栈

调用惯例
主调函数和被调函数必须要有一致约定,才能正确的调用函数,这个约定我们称为调用惯例
调用惯例 包含内容: 出栈方、参数传递顺序、函数名称修饰
C/C++下默认调用惯例:
cdecl:从右到左;函数调用方管理出栈,主调函数管理出栈;名字修饰为下划线+函数名。
函数变量传递分析
main函数在栈区开辟的内存所有子函数均可以使用;
main函数在堆区开辟的内存,所有子函数均可以使用;
子函数1在栈区开辟的内存,子函数1和子函数的子函数均可以使用;
子函数的子函数在全局区开辟的内存,子函数1和main均可以使用。




示例
void func()
{
char* p = malloc(10);
int c = 10;
return p;
}
void test01()
{
int b = 10;
func();
}
int main(void)
{
int a = 10;
system("pause");
return EXIT_SUCCESS;
}
栈的生长方向和内存存放方向
栈生长方向
栈底
栈顶
内存存放方向
高位字节数据
低位字节数据
这种方式称为小端对齐方式

示例
void test01()
{
int a = 10;
int b = 10;
int c = 10;
int d = 10;
printf("%d\n", &a);
printf("%d\n", &b);
printf("%d\n", &c);
printf("%d\n", &d);
}
void test02()
{
int a = 0x11223344;
char* p = &a;
printf("%x\n", *p);
printf("%x\n", *(p + 1));
}
int main(void)
{
test01();
system("pause");
return EXIT_SUCCESS;
}
空指针和野指针
空指针
不能向NULL或者非法内存拷贝数据
野指针
1. 指针变量未初始化
2. 指针释放后未置空
free(p);
后面没有跟 p=NULL;
3. 指针操作超越变量作用域
在函数里指针指向的局部变量已经被销毁
4.空指针可以重复释放、野指针不可以重复释放
示例
void test()
{
char* p = NULL;
strcpy(p, '1111');
char* q = 0x122;
strcpy(q, '2222');
}
int* doWork()
{
int a = 10;
int* p = &a;
return p;
}
void test02()
{
int* p;
printf("%d\n", *p);
char* str = malloc(100);
free(str);
str = NULL;
free(str);
int* p1 = doWork();
printf("%d\n", *p1);
}
int main(void)
{
system("pause");
return EXIT_SUCCESS;
}
指针的步长
1. +1之后跳跃的字节数
2. 解引用 解出的字节数
3. 自定义结构体做步长练习
4. 通过 offsetof( 结构体名称, 属性) 找到属性对应的偏移量
offsetof 引入头文件 #include<stddef.h>
示例
void test01()
{
char* p = NULL;
printf("%d\n", p);
printf("%d\n", p+1);
double* p1 = NULL;
printf("%d\n", p1);
printf("%d\n", p1 + 1);
}
void test02()
{
char buf[1024] = { 0 };
int a = 1000;
memcpy(buf+1, &a, sizeof(int));
char* p = buf;
printf("%d\n",*(int *)(p+1));
}
struct Person
{
char a;
int b;
char buf[64];
int d;
};
void test03()
{
struct Person p = { 'a',10,"hello world",20 };
printf("d属性的偏移量:%d\n", offsetof(struct Person, d));
char* pp = &p;
printf("%d\n", *(int*)(pp + 72));
}
int main(void)
{
test03();
system("pause");
return EXIT_SUCCESS;
}
指针的间接赋值
需要满足三大条件
1. 一个普通变量+指针变量( 实参+形参)
2. 建立关系
3. 通过* 操作内存
利用Qt实现 操作地址 修改内存

指针做函数参数的输入输出特性
输入特性
在主调函数中分配内存,被调函数使用
分配在栈上和堆区
输出特性
在被调函数中分配内存,主调函数使用
示例
void func(char* p)
{
strcpy(p, "hello world");
}
void test01()
{
char buf[1024] = { 0 };
func(buf);
printf("%s\n", buf);
}
void printString(char* str)
{
printf("%s\n", str+6);
}
void test02()
{
char* p = malloc(sizeof(char) * 64);
memset(p, 0, 64);
strcpy(p, "helloworld");
printString(p);
if (p != NULL)
{
free(p);
p = NULL;
}
}
void allocateSpace(char **pp)
{
char* str = malloc(sizeof(char) * 64);
memset(str, 0, 64);
strcpy(str, "hello world");
*pp = str;
}
void test03()
{
char* p = NULL;
allocateSpace(&p);
printf("%s\n", p);
}
int main(void)
{
test03();
system("pause");
return EXIT_SUCCESS;
}
字符串强化训练
字符串结束标志 \0
sizeof 和 strlen统计的区别,sizeof统计整个数组长度。
拷贝字符串 利用三种方式
1. 利用[]
2. 利用指针
3.while (*dest++ = *src++){}
示例
void test01()
{
char str1[] = { 'h','e' };
printf("%s\n", str1);
char str2[100] = { 'g','g' };
printf("%s\n", str2);
char str3[] = "hello";
printf("%s\n", str3);
printf("%d\n", sizeof(str3));
printf("%d\n", strlen(str3));
char str4[100] = "hello";
printf("%s\n", str4);
printf("%d\n", sizeof(str4));
printf("%d\n", strlen(str4));
char str5[] = "hello\0world";
printf("%s\n", str5);
printf("%d\n", sizeof(str5));
printf("%d\n", strlen(str5));
char str6[] = "hello\012world";
printf("%s\n", str6);
printf("%d\n", sizeof(str6));
printf("%d\n", strlen(str6));
}
void cpStr01(char * dest,char *src)
{
int len = strlen(src);
for (size_t i = 0; i < len; i++)
{
dest[i] = src[i];
}
dest[len] = '\0';
}
void cpStr02(char* dest, char* src)
{
while (*src !='\0')
{
*dest = *src;
dest++;
src++;
}
*dest = '\0';
}
void cpStr03(char* dest, char* src)
{
while (*dest++ =*src++)
{
}
}
void test02()
{
char* str = "hello world";
char buf[1024];
cpStr03(buf, str);
printf("%s\n", buf);
}
int main(void)
{
test02();
system("pause");
return EXIT_SUCCESS;
}
翻转字符串
利用[ ]
利用指针
示例
//字符串翻转
//1. 利用[]
void revertStr01(char *str)
{
int len = strlen(str)
int start = 0
int end = len - 1
while (start < end)
{
char temp = str[start]
str[start] = str[end]
str[end] = temp
start++
end--
}
}
//2. 利用指针
void revertStr02(char* str)
{
int len = strlen(str)
char* start = str
char* end = str + len - 1
while (start < end)
{
char temp = *start
*start = *end
*end = temp
start++
end--
}
}
void test03()
{
char str[] = "abcdefg"
revertStr02(str)
printf("%s\n", str)
}
int main(void)
{
test03()
system("pause")
return EXIT_SUCCESS
}
字符串的格式化:sprintf使用
格式化字符串
sprintf(目标字符串,格式化内容,占位参数…)
返回值 有效字符串长度
示例
void test01()
{
char buf[1024] = { 0 };
sprintf(buf, "今天%d年 %d月 %d日\n", 2018, 6, 30);
printf("%s", buf);
memset(buf, 0, 1024);
char str1[] = "hello";
char str2[] = "world";
int ret = sprintf(buf, "%s%s", str1, str2);
printf("ret =%d\n", ret);
memset(buf, 0, 1024);
int num = 100;
sprintf(buf, "%d", num);
printf("buf =%s\n", buf);
memset(buf, 0, 1024);
sprintf(buf, "%8d", num);
printf("buf:%s\n", buf);
memset(buf, 0, 1024);
sprintf(buf, "%-8d", num);
printf("buf:%s\n", buf);
memset(buf, 0, 1024);
sprintf(buf, "0x%x", num);
printf("buf:%s\n", buf);
memset(buf, 0, 1024);
sprintf(buf, "0%o", num);
printf("buf:%s\n", buf);
}
int main(void)
{
test01();
system("pause");
return EXIT_SUCCESS;
}