Pointer
1. You need to know.
We use type *variables
to declare a pointer.
Type of pointer is important, it not only hint the data- type it point, it also means how long of one step if you use pointer to operate .
For example, memory address between char *p1 = sth
and p1 + 1
is 1 byte.
But, memory address between int *p1 = sth
and p1 + 1
is 4 bytes.
Just because int takes 4bytes, char takes 1byte.
*
解引用操作符,是指针操作中非常重要的工具。相对于上面声明指针时的 int *
里的*用作声明,
运算符中的 *
将指定内存地址中所存储的值,取出来。
2. 指针操作实例
这里有一个一维数组
x[0] | x[1] | x[2] | x[3] | x[4] |
---|---|---|---|---|
1 | 2 | 3 | 4 | 5 |
我们让int *ptr1 = x;
那么编译器便在stack区中画出了一些地址,用于存储ptr所指向的内存地址,并且指出这是一个int型的指针。
显然,打印 ptr1 的结果,将会是数组的首地址,即 &x[0]
而如果你打印 ptr+1
则会是 &x[1], 很简单,因为你是int*
型指针,一步走4bytes。举个例子
#include <cstdio>
int main() {
char a[] = "abcdefgh";
char *p1 = a;
int *p2 = (int*)a;
printf("%c\n", *(p1+1));
printf("%c", *(p2+1));
return 0;
}
Results:
b
e
是不是char类型的走了一步读到b,int类型走了一步读到e?
如果打印 *(ptr+1)
结果是 2 ,这也是显然的因为 取出 &x[1
] 里存放的值,便是2
We assume there is an int array of two dimension.
x[0][0] | x[0][1] | x[0][2] | x[0][3] | x[0][4] |
---|---|---|---|---|
1 | 2 | 3 | 4 | 5 |
x[1][0] | x[1][1] | x[1][2] | x[1][3] | x[1][4] |
6 | 7 | 8 | 9 | 10 |
要定义一个多维数组的指针,我们首先要指出这个指针是什么类型的,或者说一步走多远:
int (*p)[5] = x
这个的意思是,声明一个指针p,但类型却是 int[5]
,或者说,这是一个 一步走5*4bytes的int指针,
你想想,既然 &a &a[0] &a[0][0]
他们都是 一个地址,为什么打印他们的时候前两个出来的是地址,而最后一个才是值?因为类型不同。
std::cout << "a: type is " << typeid(a).name() << std::endl;
std::cout << "a[0]: type is " << typeid(a[0]).name() << std::endl;
std::cout << "a[0][0]: type is " << typeid(a[0][0]).name() << std::endl;
std::cout << "p: type is " << typeid(p).name() << std::endl;
std::cout << "*p: type is " << typeid(*p).name() << std::endl;
std::cout << "**p: type is " << typeid(*(*p)).name() << std::endl;
results:
a: type is A2_A5_i
a[0]: type is A5_i
a[0][0]: type is i
p: type is PA5_i
*p: type is A5_i
**p: type is i
我们先别纠结这些类型是啥 因为我也不知道,你看 int (*p)[5] = x
是不是declare了p是一个int[5]*
类型,而不是int*
,结果也是如此吧!
*p
取出了 p中的 内存地址 中的 值。显然 p中的内存地址是 &a[0], 而*(&a[0])
便是 &a[0][0]
。
正同上面的结果,a[0] 和 *p 的type是一样的,他们打印出来也是一样的东西。
而只有取到最后的 *(*p) and a[0][0]
才是不属于&p
占位符的数据类型,got it ?
以下等号寓意他们等效
p = a[0]
*p = &a[0][0]
*(*p) = a[0][0]
p+1 = a[1]
*p + 1 = &a[0][1]
*(p+1) = &a[1][0]
*(*(p+1)+3) = a[1][3]
所以要怎么才能声明一个指向多维数组的指针呢?
在声明时,指出其低维的长度,或者说是,进行指针运算时的步长。
你想,对于一个指向高维数组的指针 *ptr2
,如果我们进行运算 ptr2 += 1
那么这个内存地址的跨度是多少字节呢?
只有神知道 绝大多数情况下,内存中数组都是顺序排序的,不存在所谓 高维数组,只有所谓 数组的数组
x[0][0] | x[0][1] | x[1][0] | x[1][1] | x[2][0] | x[2][1] | ...etc |
---|
です,你需要告诉指针,它加一的时候,需要跨越, 什么数据类型的,多少个内存地址。比如下面的
int a[10][10];
int (*ptr1)[10] = a
// 那么 ptr1+1 的结果就是 a[1]
int (*ptr2) = a[0]
// 那么ptr2+1 的结果就是 &a[0][1]
为什么要用 ()
把指针包起来呢?因为这样的话,声明指针这一操作的优先度会比较高,如果不包起来的话,就先运行后面的[10],显然不能这样操作,会编译错误。
来康康更高维的。
int a[2][5][8][9];
int (*ptr1)[5][8][9] = a;
// ptr1 + 1 结果是 a[1]
int (*ptr2)[8][9] = a[1];
// ptr2 + 2 结果是 a[1][2]
3. 向函数中传入高维数组的指针。
实际上,对于一维数组而言
现在有 int a[10];
那么:
void func(int *b)
func(a)
和
void func(int b[]) // 这其实是语法糖,可以更清晰的告诉我们,传进去的是一个数组的指针。
func(a)
是等价的
数组在被传入函数以后,会退化为指针。
实际上对于数组 int a[10]
而言, a其实是 int* const a
,但传这个const a进函数后,接受值的b会退化成普通指针,既可改值,又可以改地址。
那么高维数组的呢?
int a[10][10][10];
那么
void func(int (*b)[10][10])
和
void func(int b[][10][10]) //语法糖,指出这是一个三维数组
也是等价的。
两者都可以直接
func(a)
4. String
我刚刚知道 众所周知,C语言中其实没有String字符串这种结构,但是可以用字符数组来模拟。
这里面有三种类型: 字符串(fake) and 字符数组 and 字符常量。
前两者的区别就是结尾有没有'\0' 作为字符串的结尾。在一些比较老的或者我没用过的IDE上(现在只用Clion)
你用 char a[5] = {'a', 'b', 'c', 'd', 'e'}
这种全填满数组,不留地方给 '\0' 的方法可以整出字符数组。
不过不知道为什么我在Clion里,不管怎么写都会补个 '\0' 在最后,放弃了。。。
字符常量
那就比较有意思了,字符常量在编译时,不会被放在stack或者heap中,而是会被放在静态层,是不能被修改的。
比如 printf("hello world")
里面的hello world就是字符常量,不可以被修改。
char *ptr = "hello world";
char b[] = "hello world";
这两个整出来的字符常量,我也不知道指针为什么可以这么赋值为字符串,历史遗留问题?
5. 动态分配内存 malloc calloc realloc
一定要记得free喔,动态分配的内存均在heap区域,不会自己释放的,只能通过free(接受内存地址的那个指针)
malloc()
就是在heap里开一块内存,然后返回 所开辟区域的内存首地址,这个首地址是 void*
的指针。因为编译器不知道你(赛博精神病)要怎么对待这个数据
malloc(size_t*size) // 括号里面是,要返回多少byte的heap内存
int *a = (int*)malloc(4*sizeof(int)); //放回 4*4 bytes
当然你也可以在括号里面直接放多少bytes就是了。
calloc()
跟malloc()
的区别就是,会初始化heap里面分到的区域。但是据说性能不如malloc,所以还是用malloc()吧
realloc()
重新划分已经被分配的heap内存区域
7 条评论
哈哈哈,写的太好了https://www.cscnn.com/
想想你的文章写的特别好www.jiwenlaw.com
想想你的文章写的特别好https://www.ea55.com/
想想你的文章写的特别好https://www.237fa.com/
不错不错,我喜欢看 https://www.237fa.com/
怎么收藏这篇文章?
表评论6256