字符字符串
字符字符串在c\c++中是由"\0"作为字符串结束标记。
字符的编码
编码格式只分为两种:ascii和unicode。Ascii只占1字节,由0-255之间的数字组成。
ASCII码对照表,ASCII码一览表(非常详细) - C语言中文网 (biancheng.net)
使用char定义ascii编码格式的字符,使用w_char定义Unicode编码格式的字符。
W_char中保存ascii编码的字符,不足位补0。
字符串的存储方式
- 保存总长度
优点:获取字符串长度不需要遍历
缺点:要保存字符串长度n(额外开销),字符串不能超过长度范围。
- 结束符
优点:没有长度作为开销,可以随时结束。
缺点:需要遍历,特殊情况下效率不高。
c\c++使用"\0"作为结束标志,ascii使用一个字节"\0",unicode使用两个字节"\0"。(注意程序会使用一个变量来存储字符串第一个字符的位置,以便于查找字符串)。
布尔类型
用于判断执行类型,分为0和非0.0为假,非0为真。
地址、指针和引用
c\c++中地址都以16进制表示,取一个变量的地址使用"&"符号,只有变量才能取地址。常量没有地址(不包括const定义的伪常量(其实是常量,但是设置为不可修改))。
指针定义使用"TYPE*"格式,type为数据类型,任何数据都可以定义为指针,指针本身也是一种数据类型,也用于保存各种数据类型在内存中的地址。指着变量也可以取出地址,所以有多级指针。
指针和地址的区别
不同点
|----------------|-------------|
| 指针 | 地址 |
| 变量,保存变量地址 | 常量,内存标号 |
| 可修改,再次保存其他变量地址 | 不可修改 |
| 可以对其执行取地址操作 | 不可执行取地址操作 |
| 包含对保存地址的解释信息 | 仅有地址值无法解释数据 |
共同点
|-------------|-------------|
| 指针 | 地址 |
| 取出指向地址内存的数据 | 取出地址对应内存的数据 |
| 对地址偏移后,取出数据 | 偏移后取数据,自身不变 |
| 求两个地址值的差 | 求两个地址的差 |
代码
不同指针访问同一个地址
代码
#include <stdio.h>
int main(){
int n = 0x12345678;
int *p1 = &n;
char *p2 = (char*)&n;
short *p3 = (short *)&n;
printf("%08x", *p1);
printf("%08x", *p2);
printf("%08x", *p3);
return 0;
}
反汇编
C
Wingw 64
main:
push %rbp
mov %rsp,%rbp
sub $0x40,%rsp
call 0x7ff6f3591877 ; <__main> movl $0x12345678,-0x1c(%rbp) ;n = 0x12345678
lea -0x1c(%rbp),%rax
mov %rax,-0x8(%rbp) ;int *p1=&n
lea -0x1c(%rbp),%rax
mov %rax,-0x10(%rbp) ;char *p2=&n
lea -0x1c(%rbp),%rax
mov %rax,-0x18(%rbp) ;short *p3 = &n
mov -0x8(%rbp),%rax ;*p1
mov (%rax),%eax ;读取p1
mov %eax,%edx ;参数2 p1
lea 0x8894(%rip),%rax ; 0x7ff6f359a000 参数1 %08 mov %rax,%rcx
call 0x7ff6f35916e0 ; <printf> mov -0x10(%rbp),%rax ;*p2
movzbl (%rax),%eax ;读取一个字节
movsbl %al,%eax ;扩展到32位
mov %eax,%edx
lea 0x8879(%rip),%rax ; 0x7ff6f359a000 参数1 %08x mov %rax,%rcx
call 0x7ff6f35916e0 ; <printf> mov -0x18(%rbp),%rax ;*p3
movzwl (%rax),%eax ;读取两个字节
cwtl ;ax扩展到eax
mov %eax,%edx
lea 0x8860(%rip),%rax ; 0x7ff6f359a000 参数1 %08x mov %rax,%rcx
call 0x7ff6f35916e0 ; <printf> mov $0x0,%eax
add $0x40,%rsp
pop %rbp
ret
c
msvc64->64
main(...):
pushq %rdi
subq $0x50, %rsp
leaq 0x20(%rsp), %rdi
movl $0xc, %ecx
movl $0xcccccccc, %eax ; imm = 0xCCCCCCCC 重置 rep stosl %eax, %es:(%rdi)
movl $0x12345678, 0x24(%rsp) ; imm = 0x12345678 leaq 0x24(%rsp), %rax
movq %rax, 0x38(%rsp) ;int *p1 = &n
leaq 0x24(%rsp), %rax
movq %rax, 0x40(%rsp) ;char *p2 = &n
leaq 0x24(%rsp), %rax
movq %rax, 0x48(%rsp) ;short *p3 = &n
movq 0x38(%rsp), %rax ;*p1
movl (%rax), %edx ;参数2 p1
leaq 0xaaf5(%rip), %rcx ;参数1 %08x
callq 0x140001195 ; printf movq 0x40(%rsp), %rax ;*p2
movsbl (%rax), %eax ;读取一个字节扩展到32位
movl %eax, %edx ;参数2 p2
leaq 0xaae7(%rip), %rcx ; 参数1 %08x
callq 0x140001195 ; printf movq 0x48(%rsp), %rax ;*p3
movswl (%rax), %eax ;读取两个字节扩展为32位
movl %eax, %edx ;参数2 p3
leaq 0xaad9(%rip), %rcx ;参数1 %08x
callq 0x140001195 ; printf xorl %eax, %eax
movl %eax, %edi
movq %rsp, %rcx
leaq 0x8736(%rip), %rdx
callq 0x140001046 ; _RTC_CheckStackVars movl %edi, %eax
addq $0x50, %rsp
popq %rdi
retq
msvc
x64->x86
main(...):
pushl %ebp
movl %esp, %ebp
subl $0x18, %esp
movl $0xcccccccc, %eax ; imm = 0xCCCCCCCC movl %eax, -0x18(%ebp)
movl %eax, -0x14(%ebp)
movl %eax, -0x10(%ebp)
movl %eax, -0xc(%ebp)
movl %eax, -0x8(%ebp)
movl %eax, -0x4(%ebp)
movl $0x12345678, -0x8(%ebp) ; imm = 0x12345678 leal -0x8(%ebp), %eax
movl %eax, -0x10(%ebp) ;int *p1 = &n
leal -0x8(%ebp), %ecx
movl %ecx, -0x14(%ebp) ;char *p2 = &n
leal -0x8(%ebp), %edx
movl %edx, -0x18(%ebp) ;short *p3 = &n
movl -0x10(%ebp), %eax ;*p1
movl (%eax), %ecx
pushl %ecx ;参数2 *p1
pushl 0x919000 *; imm = 0x919000* *(参数1 %08x)* calll 0x40119a *; _printf* addl 0x8, %esp ;平衡栈
movl -0x14(%ebp), %edx ;*p2
movsbl (%edx), %eax ;取出1字节扩展为32位
pushl %eax ;参数2 *p2
pushl 0x919008 *; imm = 0x919008* *(参数1 %08x)* calll 0x40119a *; _printf* addl 0x8, %esp ;平衡栈
movl -0x18(%ebp), %ecx ;*p3
movswl (%ecx), %edx ;取出2字节扩展为32位
pushl %edx ;参数2 *p3
pushl 0x919010 *; imm = 0x919010* *(参数1 %08x)* calll 0x40119a *; _printf* addl 0x8, %esp
xorl %eax, %eax
pushl %edx
movl %ebp, %ecx
pushl %eax
leal 0x911554, %edx
calll 0x401050 ; @_RTC_CheckStackVars@8 popl %eax
popl %edx
addl $0x18, %esp
cmpl %esp, %ebp
calll 0x4010ff ; __RTC_CheckEsp movl %ebp, %esp
popl %ebp
retl
nop
addl %eax, (%eax)
addb %al, (%eax)
popl %esp
adcl $0xfff80091, %eax ; imm = 0xFFF80091
各类型指针的寻址方式
代码
#include <stdio.h>
int main(int argc, char* argv\[\]){
char ary5 = {(char)0x01, (char)0x23, (char)0x45, (char)0x67, (char)0x89};
int *p1 = (int*)ary;
char *p2 = (char*)ary;
short *p3 = (short*)ary;
p1 += 1;
p2 += 1;
p3 += 1;
return 0;
}
反汇编
C
Wingw 64
main(int, char**):
push %rbp
mov %rsp,%rbp
sub $0x40,%rsp
mov %ecx,0x10(%rbp)
mov %rdx,0x18(%rbp)
call 0x7ff74fc41817 ; <__main> movl $0x67452301,-0x1d(%rbp)
movb $0x89,-0x19(%rbp) ;char ary5
lea -0x1d(%rbp),%rax
mov %rax,-0x8(%rbp) ;int *p1
lea -0x1d(%rbp),%rax
mov %rax,-0x10(%rbp) ;char *p2
lea -0x1d(%rbp),%rax
mov %rax,-0x18(%rbp) ;chort *p3
addq $0x4,-0x8(%rbp) ;p1 += 1
addq $0x1,-0x10(%rbp) ;p2 += 1
addq $0x2,-0x18(%rbp) ;p3 += 1
mov $0x0,%eax
add $0x40,%rsp
pop %rbp
ret
c
msvc64->64
main(int,char **):
movq %rdx, 0x10(%rsp)
movl %ecx, 0x8(%rsp)
pushq %rdi
subq $0x60, %rsp
leaq 0x20(%rsp), %rdi
movl $0x10, %ecx
movl $0xcccccccc, %eax ; imm = 0xCCCCCCCC rep stosl %eax, %es:(%rdi)
movl 0x70(%rsp), %ecx
movq 0xab26(%rip), %rax ; __security_cookie canary xorq %rsp, %rax
movq %rax, 0x58(%rsp) ;cookie ^ rsp 的结果保存在栈上
movb $0x1, 0x24(%rsp)
movb $0x23, 0x25(%rsp)
movb $0x45, 0x26(%rsp)
movb $0x67, 0x27(%rsp)
movb $-0x77, 0x28(%rsp) ;写入char ary5的数据
leaq 0x24(%rsp), %rax
movq %rax, 0x38(%rsp) ;int *p1
leaq 0x24(%rsp), %rax
movq %rax, 0x40(%rsp) ;char *p2
leaq 0x24(%rsp), %rax
movq %rax, 0x48(%rsp) ;short *p3
movq 0x38(%rsp), %rax
addq $0x4, %rax
movq %rax, 0x38(%rsp) ;*p1 += 1
movq 0x40(%rsp), %rax
incq %rax
movq %rax, 0x40(%rsp) ;*p2 += 1
movq 0x48(%rsp), %rax
addq $0x2, %rax
movq %rax, 0x48(%rsp) ;*p3 += 1
xorl %eax, %eax
movl %eax, %edi
movq %rsp, %rcx
leaq 0x86b0(%rip), %rdx
callq 0x140001046 ; _RTC_CheckStackVars 运行时栈检查 movl %edi, %eax
movq 0x58(%rsp), %rcx
xorq %rsp, %rcx
callq 0x140001055 ; __security_check_cookie cannry addq $0x60, %rsp
popq %rdi
retq
msvc
x64->x86
main(int,char **):
pushl %ebp
movl %esp, %ebp
subl $0x20, %esp
movl $0xcccccccc, %eax ; imm = 0xCCCCCCCC movl %eax, -0x20(%ebp)
movl %eax, -0x1c(%ebp)
movl %eax, -0x18(%ebp)
movl %eax, -0x14(%ebp)
movl %eax, -0x10(%ebp)
movl %eax, -0xc(%ebp)
movl %eax, -0x8(%ebp)
movl %eax, -0x4(%ebp)
movl 0x949000, %eax
xorl %ebp, %eax
movl %eax, -0x4(%ebp)
movb $0x1, -0x10(%ebp)
movb $0x23, -0xf(%ebp)
movb $0x45, -0xe(%ebp)
movb $0x67, -0xd(%ebp)
movb $-0x77, -0xc(%ebp) ;char ary5
leal -0x10(%ebp), %eax
movl %eax, -0x18(%ebp) ;int *p1
leal -0x10(%ebp), %ecx
movl %ecx, -0x1c(%ebp) ;char *p2
leal -0x10(%ebp), %edx
movl %edx, -0x20(%ebp) ;chort *p3
movl -0x18(%ebp), %eax
addl $0x4, %eax
movl %eax, -0x18(%ebp) ;p1 += 1
movl -0x1c(%ebp), %ecx
addl $0x1, %ecx
movl %ecx, -0x1c(%ebp) ;p2 += 1
movl -0x20(%ebp), %edx
addl $0x2, %edx
movl %edx, -0x20(%ebp) ;p3 += 1
xorl %eax, %eax
pushl %edx
movl %ebp, %ecx
pushl %eax
leal 0x941540, %edx
calll 0x401050 ; @_RTC_CheckStackVars@8 运行时栈检查 popl %eax
popl %edx
movl -0x4(%ebp), %ecx
xorl %ebp, %ecx
calll 0x401217 ; @__security_check_cookie@4 canary movl %ebp, %esp
popl %ebp
retl
nop
addl %eax, (%eax)
addb %al, (%eax)
decl %eax
adcl $0xfff00094, %eax ; imm = 0xFFF00094
以指针保存的地址作为寻址首地址,加上偏移量,最终得到目标地址。地址寻址公式如下:
type *p; //type指任意类型指针
p + n的目标地址 = 首地址 + sizeof(指针类型 type) * n
注意两个指针的值可以相加,但是没有意义(两个地址值相加会得到一个新地址,这个地址不可控往往无法访问所以无意义,不是不能相加)
减法对指针是有意义的,乘法和除法是没有意义的(原因和上面相同),减法操作必须是同类型指针(强转也可以),一般用于两个指针的大小比较,也可以用于计算数组元素个数:
type *p;//type指任意类型指针
p -- q = ((int)p -- (int)q) / sizeof(指针类型 type)
引用
C++为了简化操作,对指针操作进行了封装,产生了应用类型。
代码
#include <stdio.h>
void add(int &ref){
ref++;
}
int main(int argc, char argv\[\]){
int n = 0x12345678;
int &ref = n;
add(ref);
return 0;
}
汇编
C
Wingw 64
main(int, char*):
push %rbp
mov %rsp,%rbp
sub $0x30,%rsp
mov %ecx,0x10(%rbp)
mov %rdx,0x18(%rbp)
call 0x7ff75ebf1817 ; <__main> movl $0x12345678,-0xc(%rbp) ;int n = 0x12345678
lea -0xc(%rbp),%rax
mov %rax,-0x8(%rbp) ;int &ref = n
mov -0x8(%rbp),%rax ;参数1
mov %rax,%rcx ;传递n的地址
call 0x7ff75ebf16e0 ; <add(int&)> mov $0x0,%eax
add $0x30,%rsp ;平衡栈
pop %rbp
ret
c
msvc64->64
main(int,char *):
movq %rdx, 0x10(%rsp)
movl %ecx, 0x8(%rsp)
pushq %rdi
subq $0x40, %rsp
leaq 0x20(%rsp), %rdi
movl $0x8, %ecx
movl $0xcccccccc, %eax ; imm = 0xCCCCCCCC rep stosl %eax, %es:(%rdi)
movl 0x50(%rsp), %ecx
movl $0x12345678, 0x24(%rsp) ; int n= 0x12345678 leaq 0x24(%rsp), %rax
movq %rax, 0x38(%rsp)
movq 0x38(%rsp), %rcx ;int &ref = n
callq 0x140001244 ; add(int & __ptr64) xorl %eax, %eax
movl %eax, %edi
movq %rsp, %rcx
leaq 0x86d3(%rip), %rdx
callq 0x140001046 ; _RTC_CheckStackVars movl %edi, %eax
addq $0x40, %rsp
popq %rdi
retq
msvc
x64->x86
main(int,char *):
pushl %ebp
movl %esp, %ebp
subl $0x10, %esp
movl $0xcccccccc, %eax ; imm = 0xCCCCCCCC movl %eax, -0x10(%ebp)
movl %eax, -0xc(%ebp)
movl %eax, -0x8(%ebp)
movl %eax, -0x4(%ebp)
movl $0x12345678, -0x8(%ebp) ; int n = 0x12345678 leal -0x8(%ebp), %eax
movl %eax, -0x10(%ebp) ;int &ref = n
movl -0x10(%ebp), %ecx
pushl %ecx ;参数1传递n的地址
calll 0x4010eb ; add(int &) addl $0x4, %esp ;平衡栈
xorl %eax, %eax
pushl %edx
movl %ebp, %ecx
pushl %eax
leal 0x311534, %edx
calll 0x401050 ; @_RTC_CheckStackVars@8 popl %eax
popl %edx
addl $0x10, %esp
cmpl %esp, %ebp
calll 0x401104 ; __RTC_CheckEsp __RTC_CheckEsp 是另一个 MSVC Debug 独有的检查函数,用于验证栈平衡(esp == ebp)。如果栈指针被意外修改(如错误调用约定导致栈不平衡),该函数会触发断点或报错。 movl %ebp, %esp
popl %ebp
retl
nopl (%eax)
addl %eax, (%eax)
addb %al, (%eax)
cmpb $0x15, %al
xorl %eax, (%eax)
clc
除了引用是编译器实现寻址,而指针需要手动寻址外,引用和指针没有太大区别
引用类型作为函数参数
代码
void add(int &ref){
ref++;
}
汇编
C
Wingw 64
add(int&):
push %rbp
mov %rsp,%rbp
mov %rcx,0x10(%rbp) ;保护引用指针
mov 0x10(%rbp),%rax
mov (%rax),%eax ;取出参数ref的内容放入eax
lea 0x1(%rax),%edx ;+1
mov 0x10(%rbp),%rax
mov %edx,(%rax) ;写回去
nop
pop %rbp
ret
c
msvc64->64
add(int &):
movq %rcx, 0x8(%rsp) ;保护指针
pushq %rdi
movq 0x10(%rsp), %rax ;取出参数ref放入rax
movl (%rax), %eax
incl %eax ;+1
movq 0x10(%rsp), %rcx
movl %eax, (%rcx) ;写回去
popq %rdi
retq ;0xcc debug 对齐用
int3
int3
int3
int3
int3
int3
int3
int3
msvc
x64->x86
add(int &):
pushl %ebp
movl %esp, %ebp ;栈帧
movl 0x8(%ebp), %eax
movl (%eax), %ecx ;取出ref放入eax
addl $0x1, %ecx ;+1
movl 0x8(%ebp), %edx
movl %ecx, (%edx) ;写回去
popl %ebp
retl
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3