# 概述

# 内存

  • 存储器:计算机的组成中,用来存储数据和程序的部件
  • 内存:内部存储器,暂存程序和数据
  • 外村:外部存储器,用来长期存储程序和数据

内存是沟通CPU和外部存储器的桥梁,CPU通过内存来访问外部存储器

  • 暂存放CPU要处理的数据和程序
  • 暂存与硬盘等外部存储器交换的数据

# 物理存储器和存储地址空间

  • 物理存储器:内存条,内存条上的每一个存储单元都有一个唯一的编号,称为物理地址

  • 存储地址空间:内存条上所有存储单元的集合,每一个存储单元都有一个唯一的编号,称为存储地址

# 内存地址

  • 将内存抽象成一个很大的数组,每个元素都有一个唯一的编号,称为内存地址
  • 内存地址是一个无符号整数,用16进制表示
  • 内存地址的范围:0x00000000 ~ 0xFFFFFFFF

# 指针和指针变量

  • 指针:内存地址的别名
  • 指针变量:存储内存地址的变量

# 指针的定义和使用

# 指针的定义

  • 指针变量的定义:数据类型 *指针变量名;
  • 指针变量的初始化:指针变量名 = &变量名;
  • 指针变量的使用:*指针变量名
  • *操作符操作的是指针变量指向的内存空间
include <stdio.h>

int main() {

    int a = 10; //定义一个变量a,值为10

    int *p; //定义一个指针变量p

    p = &a; //将变量a的地址赋值给指针变量p

    printf("a的值为:%d\n", a); //打印变量a的值

    printf("a的地址为:%p\n", &a); //打印变量a的地址

    printf("p的值为:%p\n", p); //打印指针变量p的值

    printf("p的地址为:%p\n", &p); //打印指针变量p的地址

    printf("*p的值为:%d\n", *p); //打印指针变量p指向的内存空间的值

    return 0;
}

# 指针间接修改变量的值

#include <stdio.h>

int main() {

    int a = 10; //定义一个变量a,值为10

    int *p; //定义一个指针变量p

    p = &a; //将变量a的地址赋值给指针变量p

    *p = 20; //通过指针间接修改变量a的值

    printf("a的值为:%d\n", a); //打印变量a的值

    return 0;
}

# 指针的大小

  • 指针变量的大小与操作系统有关

    • 32位操作系统:4个字节
    • 64位操作系统:8个字节
  • sizeof(指针变量名)可以获取指针变量的大小

#include <stdio.h>

int main() {

    int a = 10; //定义一个变量a,值为10

    int *p; //定义一个指针变量p

    p = &a; //将变量a的地址赋值给指针变量p

    printf("指针变量p的大小为:%lu\n", sizeof(p)); //打印指针变量p的大小

    return 0;
}

# 野指针和空指针

  • 野指针:指针变量指向非法的内存空间
  • 空指针:指针变量指向内存编号为0的空间,空指针不指向任何合法的内存空间

WARNING

  • 空指针不是野指针
  • 空指针指向的内存空间是不可以访问的
  • 空指针不指向任何合法的内存空间
#include <stdio.h>

int main() {

    int *p = NULL; //定义一个空指针

    printf("空指针p的值为:%p\n", p); //打印空指针p的值

    printf("空指针p的大小为:%lu\n", sizeof(p)); //打印空指针p的大小

    return 0;
}

# 万能指针 void *

void * 是一种特殊的指针类型,可以用来存放任意类型的地址

#include <stdio.h>

int main() {

    int a = 10; //定义一个变量a,值为10

    int *p = &a; //定义一个指针变量p,指向变量a

    void *p1 = &a; //定义一个万能指针p1,指向变量a

    printf("指针变量p的值为:%p\n", p); //打印指针变量p的值

    printf("万能指针p1的值为:%p\n", p1); //打印万能指针p1的值

    return 0;
}

# const 修饰指针变量

  • const 修饰指针变量,表示指针变量指向的内存空间的值不能修改

#include <stdio.h>

int main() {

    int a = 10; //定义一个变量a,值为10

    int b = 20; //定义一个变量b,值为20

    int *const p = &a; //定义一个指针变量p,指向变量a

    printf("指针变量p的值为:%p\n", p); //打印指针变量p的值

    printf("指针变量p指向的值为:%d\n", *p); //打印指针变量p指向的值

    //p = &b; //err, 指针变量p的值不能修改

    *p = 30; //ok, 指针变量p指向的值可以修改

    printf("指针变量p指向的值为:%d\n", *p); //打印指针变量p指向的值

    return 0;
}

# 指针和数组

# 数组名和指针

  • 数组名:数组首元素的地址
  • 指针变量:指向数组首元素的指针变量
#include <stdio.h>

int main() {

    int arr[5] = {1, 2, 3, 4, 5}; //定义一个数组arr

    printf("数组arr的地址为:%p\n", arr); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", &arr[0]); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", &arr); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", &arr + 1); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", arr + 1); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", &arr[0] + 1); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", &arr[1]); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", &arr[1] + 1); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", arr[1] + 1); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", *(arr + 1)); //打印数组arr的地址

    printf("数组arr的地址为:%p\n", *arr + 1); //打印数组arr的地址

    return 0;
}

# 指针操作数组

#include <stdio.h>

int main() {

    int arr[5] = {1, 2, 3, 4, 5}; //定义一个数组arr
    int i = 0;
    for (i = 0; i < 5; i++) {
        printf("%d\n", *(arr + i));
    }
    printf("arr[0]的地址为:%p\n", &arr[0]); //打印数组arr的地址

    int *p = arr; //定义一个指针变量p,指向数组arr
    for (i = 0; i < 5; i++) {
        printf("%d\n", *(p + i));
    }

    for (i = 0; i < 5; i++) {
        p[i] = 2 * i;
    }

}

# 指针加减运算

  • 指针加减整数:指针向前或向后移动指定的元素个数
  • 指针加减指针:两个指针相减,得到的是两个指针之间的元素个数
#include <stdio.h>

int main(){

    int a;
    int *p = &a;
    int *p1 = p + 1;
    printf("p的值为:%p\n", p);
    printf("p1的值为:%p\n", p1);
    printf("p1 - p的值为:%ld\n", p1 - p);

    char b=0;
    char *p2 = &b;
    char *p3 = p2 + 1;
    printf("p2的值为:%p\n", p2);
    printf("p3的值为:%p\n", p3);
    printf("p3 - p2的值为:%ld\n", p3 - p2);

}

通过改变指针指向操作数组元素:

#include <stdio.h>

int main(){

    
        int arr[5] = {1, 2, 3, 4, 5}; //定义一个数组arr
        int *p = arr; //定义一个指针变量p,指向数组arr
        int i = 0;
        for (i = 0; i < 5; i++) {
            printf("%d\n", *(p + i));
        }
    
        p = &arr[4];
        for (i = 0; i < 5; i++) {
            printf("%d\n", *(p - i));
        }
    
}

# 指针数组

指针数组:数组中存放的是指针变量

#include <stdio.h>

int main() {

    int a = 10; //定义一个变量a,值为10
    int b = 20; //定义一个变量b,值为20
    int c = 30; //定义一个变量c,值为30

    int *arr[3] = {&a, &b, &c}; //定义一个指针数组arr,存放指针变量

    int i = 0;
    for (i = 0; i < 3; i++) {
        printf("%d\n", *(arr[i]));
    }

    return 0;
}

# 多级指针

  • C语言允许有多级指针存在, 在实际的程序中一级指针最常用,其次是二级指针。
  • 一级指针:指向变量的指针
  • 二级指针:指向一级指针的指针
  • 三级指针:指向二级指针的指针
#include <stdio.h>

int main(){

    int a = 10; //定义一个变量a,值为10
    int *p = &a; //定义一个指针变量p,指向变量a
    int **p1 = &p; //定义一个二级指针变量p1,指向指针变量p
    int ***p2 = &p1; //定义一个三级指针变量p2,指向二级指针变量p1

    printf("a的值为:%d\n", a); //打印变量a的值
    printf("a的地址为:%p\n", &a); //打印变量a的地址
    printf("p的值为:%p\n", p); //打印指针变量p的值
    printf("p的地址为:%p\n", &p); //打印指针变量p的地址
    printf("p1的值为:%p\n", p1); //打印二级指针变量p1的值
    printf("p1的地址为:%p\n", &p1); //打印二级指针变量p1的地址
    printf("p2的值为:%p\n", p2); //打印三级指针变量p2的值
    printf("p2的地址为:%p\n", &p2); //打印三级指针变量p2的地址
    
    return 0;
}

# 指针和函数

# 函数形参改变实参的值

#include <stdio.h>

void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

void swap1(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {

    int a = 10;
    int b = 20;

    printf("a的值为:%d\n", a);
    printf("b的值为:%d\n", b);

    swap(a, b);

    printf("a的值为:%d\n", a);
    printf("b的值为:%d\n", b);

    swap1(&a, &b);

    printf("a的值为:%d\n", a);
    printf("b的值为:%d\n", b);

    return 0;
}

# 函数返回指针

#include <stdio.h>

int *getMax(int *a, int *b) {
    if (*a > *b) {
        return a;
    } else {
        return b;
    }
}

int main() {

    int a = 10;
    int b = 20;

    int *p = getMax(&a, &b);

    printf("p的值为:%p\n", p);
    printf("p的值为:%d\n", *p);

    return 0;
}

# 函数指针

  • 函数指针:指向函数的指针变量
#include <stdio.h>

int getMax(int a, int b) {
    return a > b ? a : b;
}

int main() {

    int a = 10;
    int b = 20;

    int (*p)(int, int) = getMax;

    printf("p的值为:%p\n", p);
    printf("p的值为:%d\n", p(a, b));

    return 0;
}

# 指针和字符串

# 字符指针

  • 字符指针:指向字符串的指针变量
#include <stdio.h>

int main() {

    char *p = "hello world";

    printf("p的值为:%p\n", p);
    printf("p的值为:%s\n", p);

    return 0;
}

# 字符指针做函数参数

#include <stdio.h>

void printStr(char *p) {
    printf("p的值为:%p\n", p);
    printf("p的值为:%s\n", p);
}

int main() {

    char *p = "hello world";

    printf("p的值为:%p\n", p);
    printf("p的值为:%s\n", p);

    printStr(p);

    return 0;
}

# const 修饰的指针变量

  • const 修饰的指针变量,指针变量指向的内存空间的值不能修改
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void){

    const char *p = "hello world";

    printf("p的值为:%p\n", p);

    printf("p的值为:%s\n", p);

    //p[0] = 'H'; //err, 指针变量p指向的内存空间的值不能修改

    p = "hello world"; //ok, 指针变量p指向的内存空间的值可以修改

    printf("p的值为:%p\n", p);
}

# 指针数组做为main函数的参数

main 函数是操作系统调用的,第一个参数是 argc ,第二个参数是 argvargv 是一个指针数组,数组中的每一个元素都是一个指针,指向一个字符串

#include <stdio.h>

int main(int argc, char *argv[]){

    char*a[]={ "hello", "world", "!" };
    int i;

    for (i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }

    return 0;

}

# 字符串处理函数

函数名 功能 参数 返回值 头文件
strlen 计算字符串的长度 const char *str size_t string.h
strcpy 复制字符串 char *dest, const char *src char * string.h
strcat 字符串拼接 char *dest, const char *src char * string.h
strcmp 字符串比较 const char *str1, const char *str2 int string.h
strchr 查找字符在字符串中第一次出现的位置 const char *str, int c char * string.h
strstr 查找字符串在字符串中第一次出现的位置 const char *haystack, const char *needle char * string.h
strtok 分割字符串 char *str, const char *delim char * string.h
strerror 根据错误编号获取错误信息 int errnum char * string.h
stmcpy 复制字符串 char *dest, const char *src, size_t n char * string.h
strncpy 复制字符串 char *dest, const char *src, size_t n char * string.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void){

    char *p = "hello world";

    printf("p的值为:%p\n", p);

    printf("p的值为:%s\n", p);

    //p[0] = 'H'; //err, 指针变量p指向的内存空间的值不能修改

    p = "hello world"; //ok, 指针变量p指向的内存空间的值可以修改

    printf("p的值为:%p\n", p);
}