0

【C2】指针

 2 years ago
source link: https://www.guofei.site/2021/11/21/c2.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【C2】指针

2021年11月21日    Author:Guofei

文章归类: 语言    文章编号: 12001


版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2021/11/21/c2.html

Edit

指针

  • 指针是一种数据类型,它指向内存
  • 每个地址对应一个 Byte,
  • 地址编号本身是 8 个字节(64位系统)or 4个字节(32位系统)的 无符号整数
int *p;
// 1. 指针变量是 p
// 2. void *p 可以指向任何类型

int a;
p = &a; // 把 a 的内存赋值给 p
// 或者 int *p = &a;
a = 1;
printf("%p\n",p); // 打印内存编号

*p = 10; // 给指针指向的位置赋值,*p 就代表a

// 1.
  1. register int a; 定义的变量是无法用 & 取地址的,因为它一直在寄存器中。
  2. 保证类型一致

空指针和野指针

// 野指针
int *p ;
*p = 100;
// 没有指向哪个变量,编译器不报错,但规范不允许

// 空指针
int *p = NULL;
// 空指针是允许的
// NULL 其实是0

指向常量的指针,指针常量

// 指针常量
int a = 0;
const int *p = &a; // p 是一个变量,但指向一个常量
// 可以通过 *p 读取,但不能通过 *p 写。可以通过 a 写。
// *p = 1; // 这个是不允许的
// a = 1; // 这个是允许的
// 可以这么理解把,获取了 a 的只读权限


// 常量指针
int *const p = &a;
// 这个 p 是常量指针,不能再指向其它变量,例如 *p = &a2 是错的


// 错误用法
const int a = 100;
int *p = &a;
*p = 1;
printf("%d != %d\n", a, *p); // 不相同,好像和编译器有关,不知道为啥
printf("%p == %p", p, &a); // 相同
// 上面这个用法是不符合规范的。

指针和数组

int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = a; // 数组存入的就是内存地址,因此不需要 *p = &a;
int *p = &a[1]; // 也可以

p[3] = 100; // 可以直接当数组名用
printf("%ld != %ld ", sizeof(a), sizeof(p)); //但又不完全一样
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = &a[1];

p[3] = 100; // 其实是 a[4]
p + 1; // 指向下一个 “数据单元”,而不是16进制地址加1
p - 1;
// 实际移动多少 16 进制大小呢?取决于定义指针时的指定类型。例如 int 是 4,double 是 8
// 如果指针类型和变量类型不一致(虽然不符合规范),以指针类型为准


// 同样,也可以有这个:
p += 5;
p -= 3;
p++;
p--;

*(p + 3) = 100;
p[3] = 100; // 等价写法,即使不是数组,也能这么写,位移3个单位

有趣的例子1

int a = 0x12345678;
char *p = &a; // 也可以 char *p = (char *) &a;

printf("%x\n", *p);
printf("%x\n", *(p + 1));
printf("%x\n", *(p + 2));
printf("%x\n", p[3]);
// 返回:
// 78
// 56
// 34
// 12
// 1. 颠倒过来是因为整数是“正向对其”的
// 2. 也可以写入

有趣的例子2:ip 其实对应一个 int

unsigned int a = 987654321;
unsigned char *p = (unsigned char *)&a;
for(int i=3;i>=0;i--){
    printf("%u.",p[i]);
}

思考: ip 如何转回为 int

unsigned char a[4] = {58, 222, 104, 177};
unsigned char tmp[4];
for (int i = 0; i < 4; i++) {
    tmp[i] = a[3 - i];
}
unsigned int *p = &tmp;
printf("%d",*p);

指针数组

int *p[5]; // 5个指针组成的数组(叫做指针数组)
p[2]; // 这是一个指针
*p[2]; // 这是那个指针指向的值

// 你需要先把指针的指向赋值好,才能用这个指针
int a = 2;
p[3] = &a;
*p[3] = 5;

二级指针

指的是指向指针的指针

int a = 0;
int *p = &a;
int **pp;//二级指针,指向指针的指针
pp = &p;
// 或者 int **p = &p;


**pp = 10; // 通过二级指针访问 a

如果想用一个指针指向指针数组,必须用一个二级指针指向它

int value = 10;
int *arr[10];//一个指针数组
arr[2] = &value;//指针数组的第2个,指向一个 int

int **p = arr; //指向指针数组的指针,必须用二级指针

p[2]; // 其实是 arr[2] ,存放的是一个指针
*p[2] = 5; // 那么,这个指针其实指向 value

类似的,还有多级指针

指针的应用

如果想用函数改变某个变量的值,必须通过指针

int swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
    return 0;
}


int main() {
    int a = 1;
    int b = 2;
    swap(&a, &b);
    printf("%d%d", a, b);
    return 0;
}

当一个数组名作为入参时,自动作为指针变量。

int tst(int a[10]);
int tst(int a[]);
int tst(int *a);
// 以上三个是等价的
// 编译的时候,3个都会编译成最后一个
// 规范:通常使用最后一个


int tst(const int *a); // 这样,函数内部就无法修改数组 a 了
// 其实做个强转后,还是能改的 int *p = (int *)a; p[5] = 999;
// C++ 没这漏洞了
// 一般约定,函数只要不改数组,都加 const

// 如果数组作为入参,数组的大小在函数中是不可见的
int tst(const int *a) {
    printf("%lu\n", sizeof(a)); // 是指针的size,必然为 8
    return 0;
}
// 因此,往往需要额外传入一个数组长度。不过字符串不需要,因为字符串是 0 结尾的

一个函数也可以返回一个指针

int *tst(); // 函数返回指针

main函数的参数

int main(int argc, char **args) {
    for (int i = 0; i < argc; i++) {
        printf("%s, ", args[i]);
    }
    printf("\n");
    return 0;
}

// 如何使用?
// 编译后:./main -l -s 0
// 打印:./main, -l, -s, 0,
// !注意,在linux下,* 指的是通配符,被目录下的全部文件替换
// 要想传入星号,要传入 \*

标准库-内存操作

string.h

memset:内存置空

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
memset(a, 0, sizeof(a));
// 入参:置空的区域的首地址,0,这块内存大小(单位 Byte)

memcopy:内存拷贝。要确保没有内存重叠区域

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int b[10] = {0};
memcpy(b, a, sizeof(a));
// 入参:目标地址,源地址,拷贝的大小(单位 Byte)

您的支持将鼓励我继续创作!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK