C 语言答疑-04

​ By Von Brank | 2020/11/23

目录

  • 网站推荐
  • 近期知识梳理
  • 几道题
  • 答疑

DJSO81.md.jpg

网站推荐

知识点梳理

指针

&:取地址的运算

  • scanf("%d", &n);里的&是什么

  • %p用来输出地址

    1
    2
    int i = 0;
    printf("0x%x\n", &i);
    1
    2
    3
    4
    5
    int i = 0;
    int p;
    int p = (int)&i;
    printf("0x%x\n", p);
    printf("%p\n", &i);
  • 32 位与 64 位的区别

    1
    2
    3
    int i = 0;
    printf("%lu\n", sizeof(int));
    printf("%lu\n", sizeof(&i));
  • &可以用于获得变量的地址,它的操作数必须是变量

  • 不能对没有地址的东西取地址

    以下写法是正确的

    1
    2
    int i = 0;
    printf("%p\n", &i);

    以下写法都是错误的

    1
    2
    3
    4
    int a = 0, b = 1;
    printf("%p\n", &(a++));
    printf("%p\n", &(++a));
    printf("%p\n", &(a + b));

&些别的

  • 相邻变量的地址

    1
    2
    int a = 0, b = 1;
    printf("%p\n%p\n", &a, &b);

    C 语言的变量存储在“栈”中

  • sizeof一个&的结果

    1
    2
    int i = 0;
    printf("%d\n", sizeof(&a));
  • 数组的地址,首个数组单元的地址,相邻元素的地址

    1
    2
    3
    4
    5
    int a[100];
    printf("%p\n", a);
    printf("%p\n", &a[0]);
    printf("%p\n", &a[1]);
    printf("%p\n", &a[2]);

指针变量

  • scanf("%d", &n)是什么

    • scanf函数需要传入一个变量的地址,便于赋值
    • scanf函数需要对控制串进行处理
    • scanf函数的内部实现也仅仅是基于 C 语言的基本语法
  • 指针变量的定义

    以下代码将i的地址赋给p

    1
    2
    int i;
    int *p = &i;

    以下两种写法意思相同

    1
    2
    int *p;
    int* p;

    以下写法定义了一个指针变量p和一个int类型的变量q

    1
    int *p, q;

    以下写法定义了两个指针变量pq

    1
    int *p, *q;
  • 访问地址上的变量*

    1
    2
    3
    4
    5
    6
    7
    int i = 10;
    int *p = &i;
    printf("%d\n", i);
    printf("%d\n", *p);
    *p = 20;
    printf("%d\n", i);
    printf("%d\n", *p);
  • 指针作为函数参数传入

    比较函数fg的输出差异

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio>
    void f(int *p)
    {
    printf("%d\n", *p);
    printf("%p\n", p);
    }
    void g(int k)
    {
    printf("%d\n", k);
    printf("%p\n", &k);
    }
    int main()
    {
    int i = 10;
    f(&i);
    g(i);
    return 0;
    }
  • &*互为反作用

    1
    2
    3
    4
    5
    6
    int i = 10;
    int *p = &i;
    printf("%d\n", i);
    printf("%d\n", *&i);
    printf("%p\n", p);
    printf("%p\n", &*p);
  • scanf("%d", i);出错的原因

指针与数组

  • 以下两段代码效果相同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <stdio.h>

    void f(int a[], int start, int end)
    {
    for(int i=start; i<=end; i++)
    {
    printf("%d ", a[i]);
    }
    }

    int main()
    {
    int start, end;
    int a[100] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
    start = 1;
    end = 10;
    f(a, start, end);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <stdio.h>

    void f(int *a, int start, int end)
    {
    for(int i=start; i<=end; i++)
    {
    printf("%d ", a[i]);
    }
    }

    int main()
    {
    int start, end;
    int a[100] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
    start = 1;
    end = 10;
    f(a, start, end);
    return 0;
    }
  • 以下函数原型等价

    1
    2
    3
    4
    int sum(int *p, int i);
    int sum(int *, int i);
    int sum(int a[], int i);
    int sum(int [], int i);
  • 数组变量本身就是特殊的指针

    以下操作无需取地址符

    1
    2
    int a[100];
    int *p = a;
  • 数组的单元表达的是变量,需要取地址,如:

    1
    2
    3
    4
    if(a == &a[0])
    {
    printf("YES!!!");
    }
  • []运算符也可以用于指针,如

    1
    2
    3
    4
    int a = 10;
    int *p = &a;
    printf("%d\n", *p);
    printf("%d\n", p[0]);
  • *也可以用于数组

    1
    2
    3
    4
    5
    6
    7
    int a[100] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
    a[0] = 123;
    printf("%d\n", a[0]);
    printf("%d\n", *a);
    *a = 456;
    printf("%d\n", a[0]);
    printf("%d\n", *a);
  • 数组变量是常量(const),即int a[]等价于int * const a,因此:

    以下写法是错误的

    1
    2
    int a = {0, 1, 2, 3};
    int b[] = a;

    以下写法是正确的

    1
    2
    int a = {0, 1, 2, 3};
    int *p = a;

const 指针

  • int * const p = &i;的含义

    表示指针变量p只能指向i的地址

    1
    2
    3
    4
    int i, j;
    int * const p = &i;
    *p = 123; //可以
    p = &j; //不可以
  • const int *p = &i的含义

    表示不允许通过*p来修改变量的值

    1
    2
    3
    4
    5
    int i, j;
    const int *p = &i;
    *p = 123; //不可以
    i = 456; //可以
    p = &j; //可以
  • 分辨以下写法的区别

    • const int* p = &i;表示不能通过*p来修改变量的值
    • int const* p = &i;表示不能通过*p来修改变量的值
    • int *const p = &i;表示不能改变指针变量p指向的变量
  • 可以用于防止函数修改指针所指向变量的值或数组的值,保证安全性

    1
    2
    3
    4
    5
    6
    #include <stdio.h>
    void f(const int* a)
    {
    int b = *a; //可以
    *a = 123; //不可以
    }
  • 常量数组

    const类型的数组只能使用集成初始化

    1
    const int a[] = {0, 1, 2, 3, 4, 5};

指针的运算

  • 对于一个指针变量pp + 1的含义不是让指针的值+1,而是加上sizeof(<指针变量指向的变量类型>)

    1
    2
    3
    4
    5
    6
    7
    8
    char s[] = "hello_world";
    char *p = s;
    printf("%p\n", p);
    printf("%p\n", p + 1);
    int a[] = {0, 1, 2, 3, 4, 5, 6};
    int *q = a;
    printf("%p\n", q);
    printf("%p\n", q + 1);
    1
    2
    3
    4
    5
    6
    7
    8
    char s[] = "hello_world";
    char *p = s;
    printf("%c\n", *p);
    printf("%c\n", *(p + 1));
    int a[] = {0, 1, 2, 3, 4, 5, 6};
    int *q = a;
    printf("%d\n", *q);
    printf("%d\n", *(q + 1));
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int a[105];
    for(int i=1; i<=100; i++)
    {
    scanf("%d", a + i);
    }
    for(int i=1; i<=100; i++)
    {
    printf("%d", a + i);
    }

    a[i]*(a + i)完全等价

  • 指针变量可以进行加减,但指针的乘除没有意义

    1
    2
    3
    4
    5
    6
    int a[100];
    int *p1 = a;
    int *p2 = &a[7];
    printf("p1 = %p\n", p1);
    printf("p2 = %p\n", p2);
    printf("p2 - p1 = %d\n", p2 - p1);

    *p++表示取出p指向的那个数据,然后将p移动到下一个位置

    1
    2
    3
    4
    5
    6
    int a[] = {0, 1, 2, 3, 4, 5, 6, 7, -1};
    int *p = a;
    while(*p != -1)
    {
    printf("%d ", *p++);
    }

指针使用的其他注意事项

  • 所有指针的大小都是一样的,32 位程序的指针是 4 字节,64 位是 8 字节

  • 为了防止使用出错,指向不同数据类型的指针不能相互赋值,如:

    1
    2
    3
    int *p = &n;
    char *q = &c;
    p = q;
  • void*表示指向未知类型的指针,编译器不知道它指向的是什么数据类型

    1
    2
    3
    4
    5
    int *p1 = &n;
    char *p2 = &c;
    void *p3;
    p3 = p1;
    p3 = p2;
  • 指针也可以做强制类型转换

    1
    2
    3
    int *p = &n;
    char *q = &c;
    p = (void*)q;
  • 指针究竟有什么用

    • 便于向函数中传入大量数据,如数组作为参数传入
    • 将数组传入函数后对数组作操作
    • 函数需要返回一个数组
    • 用函数修改外部变量的值
    • 动态内存分配

几道题

关于指针的题没几道,关于链表的倒是有不少(bushi