EIE111 Lecture 2 指针和结构 Pointer and Structure

sh06y 发布于 1 天前 82 次阅读


目录

什么是地址?

在内存中,最小的存储单位(可寻址)是字节(Byte)。为了高效地操作内存,我们为每个字节都引入了唯一的地址。通常以十六进制表示,如0x000001(十六进制通常使用0x开头,用于区分,详见https://stackoverflow.com/questions/2670639/why-are-hexadecimal-numbers-prefixed-with-0x

(由于安全原因,大部分情况下,地址并不是物理内存中的真实地址,而是由操作系统分配的虚拟地址)

比如说你有一条4GiB的内存,其中共有232字节(4 × 210 × 210 × 210),因此其地址就从0开始,一直到232-1。32位的电脑使用32位来寻址,因此,一台32位的电脑最多只支持4GiB的内存;而对于64位的电脑,其使用64位地址,因此其最大内存为264Byte = 224TiB = 214PiB = 24EiB = 17179869184 GiB

对储存单位的说明:我们使用两种不同的计量方法,一种是基于十进制的,如GB(Gigabyte)MB(Megabyte)其换算方法为1GB=1000MB;另一种是基于二进制的,如GiB(Gibibyte)MiB(Mebibyte)其换算方法为1GiB = 210MiB = 1024MiB。在Windows中显示的GB实际为GiB。详见https://en.wikipedia.org/wiki/Gigabyte

指针

指针类似与变量,但存储的是地址,而非具体的数值

操作符

&引用(reference):用于取地址。

*解引用(dereference):取其中的内容。

比如:

int myAge = 43;     // int变量
int* ptr = &myAge; // ptr是一个指针变量,存储了myAge的地址

// 输出age的值 (43)
printf("%d\n", myAge);

// 输出myage的地址 (0x7ffe5367e044)
printf("%p\n", &myAge);

// 输出ptr存储的地址 (0x7ffe5367e044)
printf("%p\n", ptr);

指针如何工作?

int main(){
    int num = 10;
    int *p1, *p2;
    p1 = #
    p2 = #
    *p1 = 20;
    *p2 = 30;
    cout << &num << p1 << p2 << &p1 << &p2;
}

输出:0x61fe1c,0x61fe1c,0x61fe10,0x61fe08

由此可见,指针也是变量,指针也有他自己的地址。

数组中的指针

之前我们使用[]来访问数组中的元素,实际上,一个数组变量就是一个指向数组首位的指针

int a[5] = {1, 2, 3, 4, 5};
cout << a << endl; // 输出:0x31e6fffc80(数组首位的地址)
cout << *a << endl; // 输出:1(数组首位的值)
cout << a + 1 << endl; // 输出:0x31e6fffc84
cout << *(a + 1) << endl; // 输出:2

指针也能进行运算,比如说a+1表示a的第二个元素,如上

而指针运算所带来的偏移量(如上,a+1后地址+4)即为数据类型的大小。在这里a为int类型,因此每个数组元素占用4Byte空间,偏移量也是4Byte。

指针和数组的区别

数组是一个常量指针(constant pointer),意味着他不能被修改(reassigned)

int numbers[4] = {0, 1, 2, 3};
int *p = numbers; // 合法
p = p + 1; // 合法

numbers = p; // 错误: array names cannot be reassigned

使用sizeof()函数返回数组的大小(占用空间)单位Byte

int a[5] = {1, 2, 3, 4, 5};
cout << sizeof(a) << endl; // 输出: 20 (4Byte × 5)

内存操作 Memory

在之前的c中,我们使用malloc来开辟内存空间,使用free来释放内存空间。c++中也可以沿用这种方法。

在c++中,有两种新的operator:new和delete,其具有一些新特性

int * p1 = new int; //allocate an int, default initializer (do nothing)
int * p2 = new int(); //allocate an int, initialized to 0
int * p3 = new int(5); //allocate an int, initialized to 5
int * p4 = new int{};//C++11 allocate an int, initialized to 0
int * p5 = new int {5};//C++11 allocate an int, initialized to 5
Student * ps1 = new Student; //allocate a Student object, default initializer
//allocate a Student object, initialize the members
Student * ps2 = new Student {"Yu", 2020, 1}; //C++11

对于数组:

//allocate 16 int, default initializer (do nothing) 
int * pa1 = new int[16];
//allocate 16 int, zero initialized 
int * pa2 = new int[16]();
//allocate 16 int, zero initialized 
int * pa3 = new int[16]{}; //C++11
//allocate 16 int, the first 3 element are initialized to 1,2,3, the rest 0
int * pa4 = new int[16]{1,2,3}; //C++11

//allocate memory for 16 Student objects, default initializer
Student * psa1 = new Student[16];
//allocate memory for 16 Student objects, the first two are explicitly initialized
Student * psa2 = new Student[16]{{"Li", 2000,1}, {"Yu", 2001,1}}; //C++11

使用delete关键字来释放内存。对于单个变量,使用delete;对于数组,使用delete[]

//deallocate memory
delete p1;
//deallocate memory
delete ps1;
//deallocate the memory of the array
delete []pa2;
//deallocate the memory of the array, and call the destructors of all the elements
delete []psa2;

二维数组

由于内存是线性的,c++中我们使用下面的方式(行主序Row-major layout),来存储一个二维数组。

行主序意味着多维数组(例如二维数组)在内存中是按行优先存储的。也就是说,数组的元素会按照行的顺序依次存储:先存储第一行的所有元素,然后是第二行的所有元素,依此类推。

定义方式:int A[3][3];

如果d有N行M列,那么,d[i][j] 与 ptr[i*M+j] 同义

我们也可以使用A[1]来访问每一行第一个元素的地址,当成一维数组来用

int a[2][2] = {{1,2},{3,4}};
cout << a[0] << endl; // 输出0x5f33dff750
cout << a[1] << endl; // 输出0x5f33dff758

命令行参数

在实际使用中,有些时候,我们需要向程序内传递参数。比如,在使用mingw编译器时,我们会加上-o参数来指定可执行文件的名字。

int main(int argc, char *argv[]) {
    cout << argc << endl;
    for (int i = 0; i < argc; i++) {
        cout << argv[i] << endl;
    }
    return 0;
}

编译完成后,在命令行内运行:

PS C:\Users\sy\Desktop\test> ./a.exe arg1 arg2 arg3
4
C:\Users\sy\Desktop\test\a.exe
arg1
arg2
arg3

可以看出,命令行内输入的参数会作为main函数的参数传入,第一个参数为数量,第二个参数是一个cstring数组,第一个元素是运行的位置,后面几个元素存储了传入的参数。


数据结构

todo