📔 Lecture04 C Intro: Pointers, Arrays, Strings
更新日期:2023.11.02
1. Pointers and Bugs¶
1.1 Bugs来源¶
-
变量的声明
- 创建一个变量,然后在使用之前要对其进行初始化。
- 基本上都位于块的开头声明。
- 如果未对变量进行声明初始化,可能会出现代码问题。
-
未定义的行为
- 会出现很多未知的行为。
- 换个机器执行时,每次现象都不一致。
- 也可能出现随机却难以复现的错误。
- 也有可能在调试时消失或者改变。
- 会出现很多未知的行为。
-
地址和值的问题
- 内存可以看作是一个巨大的数组。
- 对于数组的每个单元都有一个与其关联的地址。
- 每个单元都存着一些值。
- 从0开始进行访问。
-
不要将地址和值混淆。
- 内存可以看作是一个巨大的数组。
1.2 指针¶
指针:指向一个特定的内存位置。是一个包含变量地址的变量。对于指针来说,就是直接指向了这个地址。
此时创建一个新变量,不知道其地址,但如果p指向x并可以访问x,那么x的地址在p的位置上。
1.3 指针的使用¶
对于p来说,无需去关注x的地址,因为可以通过p去获取到x的地址,然后获取到其值,并把x的值写入到p中。
👍 Notes:对于例子中, *
主要用于两种形式:
- 用来将p声明为指向整数的指针。
- 用来 “解引用运算符”。
对于变量指向的值,如何改变呢?使用 *
在等号左边
完成对值的修改。
1.4 指针和参数传递¶
在Java和C中,都是通过 值传递
的方式。
但是如果想要改变y的值,则可以使用指针的方式。
通过指针的方式传值,则子程序addOne可以直接访问y。也就可以修改子程序中的值,从而得到改变后的值。
1.5 C指针的陷阱¶
对于初始化来说,也会产生一些程序垃圾。例如指针。
对于指针来说,声明一个指针就是分配空间来保存指针。但是它并没有分配要指向的对象。
在C中,局部变量如果没有初始化,那么它可以包含任何内容,也就造成相应的程序垃圾。
对于指针来说,既有好处也有坏处:
-
益处:
- 传递一个大的结构体或者数组时,传递指针比传递整个结构体和数组的过程更容易更快,否则就需要使用大量的复制操作数据。
- 代码紧凑、简洁。
-
坏处:
- 指针是C语言中最大的Bug来源。所以使用时需要时刻小心。
- 特别是在动态内存管理时,需要注意其中的问题。例如:
- 悬空引用
- 内存泄漏
2. Using Pointers Effectively¶
可以声明一个泛型类型的指针。因为指针可以指向 任何数据类型的(如int、char、struct等)
。但指针通常只能指向其中之一的一种类型。
void*
可以指向任何值的类型,另外 void*
也被称为 通用指针
。
对于指针需要谨慎使用,有助于避免很多程序错误、安全问题、以及不利于程序的事情。
也可以使用函数指针(指向函数的指针)。
2.1 指针和结构体¶
2.2 NULL指针¶
null比较特殊,NULL指针意味着不指向任何东西。但是声明NULL指针式相对比较安全的方式。
NULL类似于指针的哨兵值。可以声明为NULL,不代表在任何时候将任何内容都存储为0。
对于NULL指针,需要注意点:禁止写入或者读取空指针,会让程序直接奔溃。
2.3 指向不同大小的对象¶
现代机器都是字节可寻址的方式可以访问每一个字节。C指针是抽象内存地址的一种思考方式。
对于硬件来说,存储器都是由8位存储单元组成,每一个存储单元都有一个唯一的地址。
类型声明告诉编译器每次通过指针访问要获取多少字节。
对于一些字节需要对齐,如果使用的机器是32位,那么对应的就是4字节,那么就需要进行4字节值对齐。
2.4 sizeof()
操作符¶
sizeof(type)
返回一个对象的字节大小。
-
在C99标准定义中,
sizeof(char) == 1
。 -
可以直接使用
sizeof(arg)
或sizeof(structType)
。- 对于结构体来说,需要考虑到字节对齐,所以在32位系统中,都是通过4字节的方式进行对齐。
2.5 指针运算¶
如何更改指针?
3. Arrays¶
C中数组和Java中类似,通过声明类型并指定数量。例如:
- 数组是一个请求连续内存块的一种方式。
ar[0]
可以表示为*ar
- 数组变量是指向第一个元素的指针。
ar[2]
表示为*(ar+2)
,- 这也是通过指针运算进行访问数组的一种方式。
在大多数情况下,数组和指针实际上绝大部分是相同的。
- 字符串是指向连续字符块的指针。 - 字符串就是一个字符数组。如果数组的大小为n
,要去访问 0 到 n-1
,也就是数组可以访问的范围
。
对于数组来说,也存在一定的陷。一些错误方式:
-
在C语言中数组是无法清楚何时会超出边界,因为C语言不会检查任何健全性检查相关的问题(包括数组的边界问题)。
-
分段错误 和 总线错误。
- 分段错误:访问内存异常(一般是访问了一些无权访问的区域)。
- 总线错误:对齐方式错误。