函数调用的流程
宏函数不是函数
他只是预处理器进行简单的文本替换,以空间换时间
#include<iostream>
using namespace std;
#define MYADD(x,y) ((x)+(y))
#define MAX 1024
int main() {
int a = 10;
int b = 20;
cout << "a+b=" << MYADD(a, b) << endl;
return 0;
}
宏的细节,参考这个:C++宏定义详解
栈
栈总是向下增长,压栈操作使得栈顶地址减小,出现操作使得栈顶地址增加。
栈保存一个函数调用中所需要维护的信息,这通常被称为活动记录,一个函数迪奥哟经过程所需要的信息一般包括以下几个方面:
- 函数的地址
- 函数的参数
- 临时变量
- 保存的上下文:包括在函数迪奥哟经前后需要保持不变的寄存器。
函数参数的传递顺序和方式
最常见的函数传递方式是栈传递。函数的调用方将参数压入栈中,函数自己再从栈中将参数取出。对于有多个参数的函数,调用管理要规定函数调用方将参数压栈的顺序:从左向右或者从右向左。有些调用惯例还允许使用寄存器传递参数,以提高性能。
栈的维护方式
在函数将参数压入栈中之后,函数体会被调用,此后需要将被压入栈中的参数全部弹出,以使得栈在函数调用前后保持一致。这个弹出的工作可以由函数的调用方完成或者函数自己完成。
栈的生长方向和内存存放方向
大小端
#include<stdio.h>
void test() {
int a = 0xaabbccdd;
unsigned char*p = &a;
printf("%x\n", *p);
printf("%x\n", *(p+1));
}
int main() {
test();
return 0;
}
运行结果:
说明高位字节被存放在了高地址,低位字节存放在了低地址——小端模式
指针
1、指针就是一个变量
2、指针不管什么类型,不管几级指针,占4字节
野指针
指向已经删除的对象或申请访问受限内存区域的指针。野指针无法通过简单的判断是否为空来进行避免,只能通过养成良好的变成习惯来尽量减少。对野指针进行操作很容易出现程序错误。
什么情况会产生野指针
1、指针变量未初始化
2、指针释放后未置空。
3、指针操作超过变量作用域
指针的步长:指针变量+1,要向后跳多少字节
指针的类型不仅仅决定指针的步长,还决定解引用的时候从给定地址开始取类型大小的字节数。
例子如下:
#include<stdio.h>
#include<cstring>
void test() {
char buf[1024] = { 0 };
int a = 100;
//将a中的内容拷贝到buf中,拷贝的字节大小为sizeof(int)
memcpy(buf , &a, sizeof(int));
char *p3 = buf;
//(int*)说明我要去的内容类型为int,因此会取四字节的内容
//*代表解引用,将p3指向的地址上的内容取出来
printf("*(int*)(p3)= %d\n", *(int*)(p3 ));
}
int main() {
test();
return 0;
}
运行结果:
字符串
字符串基础
1、不设置结尾标识符'\0',也不设置长度的结果
#include<stdio.h>
#include<cstring>
void test() {
char str[] = { 'h','e','l','l','0' };
printf("%s\n", str);
}
int main() {
test();
return 0;
}
运行结果:
2、设置长度之后,未赋值部分自动初始化为 0
#include<stdio.h>
#include<cstring>
void test() {
char str[100] = { 'h','e','l','l','0' };
printf("%s\n", str);
}
int main() {
test();
return 0;
}
运行结果:
3、以字符串初始化,那么编译器默认会在字符串尾部添加“\0”
#include<stdio.h>
#include<cstring>
void test() {
char str[] = "hello";
printf("%s\n", str);
}
int main() {
test();
return 0;
}
运行结果:
字符串拷贝
#include<stdio.h>
#include<cstring>
void copyStr(char * dst, const char *source) {
int len = strlen(source);
for (int i = 0; i < len; i++) {
dst[i] = source[i];
}
}
void test() {
const char *source = "hello world!";
char buf[1024] = { 0 };
copyStr(buf, source);
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果:
如果把上述代码中的char buf[1024] = { 0 }; 改成char buf[1024]; 则运行结果变为:
应对策略,咋copyStr函数中经加上 dst[len]='\0';
#include<stdio.h>
#include<cstring>
void copyStr(char * dst, const char *source) {
int len = strlen(source);
for (int i = 0; i < len; i++) {
dst[i] = source[i];
}
dst[len] = '\0';
}
void test() {
const char *source = "hello world!";
char buf[1024];
copyStr(buf, source);
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果
也可以通过指针进行字符串拷贝
#include<stdio.h>
#include<cstring>
void copyStr(char * dst, const char *source) {
while (*source )
*dst++ = *source++;
*dst = '\0';
}
void test() {
const char *source = "hello world!";
char buf[1024];
copyStr(buf, source);
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果
格式化字符串_sprintf
1、格式化字符串
#include<stdio.h>
void test() {
char buf[1024] = { 0 };
sprintf(buf, "hello %s!", "Obama");
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果:
2、拼接字符串
#include<stdio.h>
void test() {
char buf[1024] = { 0 };
char *s1 = "hello";
char* s2 = "world";
sprintf(buf, "%s %s!", s1,s2);
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果:
3、数字转成字符串格式
#include<stdio.h>
void test() {
char buf[1024] = { 0 };
int num = 666;
sprintf(buf, "%d!", num);
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果
4、格式化输出八进制、十六进制
#include<stdio.h>
void test() {
char buf[1024] = { 0 };
int num = 666;
sprintf(buf, "%o", num);
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果:
十进制数字格式化成16进制字符串
#include<stdio.h>
void test() {
char buf[1024] = { 0 };
int num = 666;
sprintf(buf, "%x", num);
printf("%s\n", buf);
}
int main() {
test();
return 0;
}
运行结果: