学习效果检验:(超详细)深度理解指针和数组

本章内容较多,主要是题目的讲解,文字表达啰嗦处请见谅,祝良好的观看体验

可以先在这里尝试所有的题目,如有不懂或不确定的,可按照顺序往下找答案
建议拿个纸笔,先把自己的答案写下来,之后再对答案捏
↓↓↓↓↓↓↓↓↓↓


    一维数组
    
    int a[] = { 1,2,3,4 };
    
    printf("%d\n", sizeof(a));
    
    printf("%d\n", sizeof(a + 0));
    
    printf("%d\n", sizeof(*a));

    printf("%d\n", sizeof(a + 1));
    
    printf("%d\n", sizeof(a[1]));
    
    printf("%d\n", sizeof(&a));

    printf("%d\n", sizeof(*&a));
    
    printf("%d\n", sizeof(&a + 1));

    printf("%d\n", sizeof(&a[0]));
    
    printf("%d\n", sizeof(&a[0] + 1));

    字符数组{ 'a','b','c','d','e','f' }->sizeof和strlen
    
    char arr[] = { 'a','b','c','d','e','f' };

    printf("%d\n", strlen(arr));
    
    printf("%d\n", strlen(arr + 0));
    
    printf("%d\n", strlen(*arr));
    
    printf("%d\n", strlen(arr[1]));
    
    printf("%d\n", strlen(&arr));
    
    printf("%d\n", strlen(&arr + 1));
    
    printf("%d\n", strlen(&arr[0] + 1));
    
    printf("%d\n", sizeof(arr));
    
    printf("%d\n", sizeof(arr + 0));
    
    printf("%d\n", sizeof(*arr));
    
    printf("%d\n", sizeof(arr[1]));
    
    printf("%d\n", sizeof(&arr));
    
    printf("%d\n", sizeof(&arr + 1));
    
    printf("%d\n", sizeof(&arr[0] + 1));

    字符数组"abcdef"
    char arr[] = "abcdef";
    
    printf("%d\n", strlen(arr));
    
    printf("%d\n", strlen(arr + 0));
    
    printf("%d\n", strlen(*arr));
    
    printf("%d\n", strlen(arr[1]));
    
    printf("%d\n", strlen(&arr));
    
    printf("%d\n", strlen(&arr + 1));
    
    printf("%d\n", strlen(&arr[0] + 1));

    printf("%d\n", sizeof(arr));
    
    printf("%d\n", sizeof(arr + 0));
    
    printf("%d\n", sizeof(*arr));
    
    printf("%d\n", sizeof(arr[1]));
    
    printf("%d\n", sizeof(&arr));
    
    printf("%d\n", sizeof(&arr + 1));
    
    printf("%d\n", sizeof(&arr[0] + 1));

    指针接收字符串
    char* p = "abcdef";

    printf("%d\n", strlen(p));
    
    printf("%d\n", strlen(p + 1));
    
    printf("%d\n", strlen(*p));
    
    printf("%d\n", strlen(p[0]));
    
    printf("%d\n", strlen(&p));
    
    printf("%d\n", strlen(&p + 1));
    
    printf("%d\n", strlen(&p[0] + 1));

    printf("%d\n", sizeof(p));
    
    printf("%d\n", sizeof(p + 1));
    
    printf("%d\n", sizeof(*p));
    
    printf("%d\n", sizeof(p[0]));
    
    printf("%d\n", sizeof(&p));
    
    printf("%d\n", sizeof(&p + 1));
    
    printf("%d\n", sizeof(&p[0] + 1));



    int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

    printf("%d\n", sizeof(a));
    
    printf("%d\n", sizeof(a[0][0]));
    
    printf("%d\n", sizeof(a[0]));
    
    printf("%d\n", sizeof(a[0] + 1));
    
    printf("%d\n", sizeof(*(a[0] + 1)));
    
    printf("%d\n", sizeof(*(a + 1)));
    
    printf("%d\n", sizeof(&a[0] + 1));
    
    printf("%d\n", sizeof(*(&a[0] + 1)));
    
    printf("%d\n", sizeof(*a));
    
    printf("%d\n", sizeof(a[3]));
    


原理分析(前提)

指针和数组的概念及补充
  • 指针:为了更好的管理内存,给每一块空间都建立了独立的地址,这些地址也被称作为指针;指针 == 地址;
  • 数组:能够存放相同类型元素的集合,数组的大小取决于元素个数和元素类型
  • 数组名:数组名一般情况下是首元素地址,
    只有两种特例:
    1、sizeof(数组名):sizeof里面单独放一个数组名是代表的是整个数组
    2、&数组名:取地址符取出的是整个数组的地址
  • int占用内存的大小是4个字节
  • char占用内存的大小是1个字节
  • 指针的大小取决于环境,不取决于指针类型,在x86环境下,指针的大小为4个字节,x64环境下指针的大小为8个字节

题目实践(自我检测)

strlen是一个函数,需要你给一个字符指针,然后函数就会以这个指针为起点,不断地向后找’\0’,并且给你返回一个从这个指针到’\0’的整形数,记作字符串的长度


sizeof是一个操作符,计算的是操作数在内存中占用的字节大小,根据操作数的类型属性决定


答案本身不重要,重要的是理解数组及指针变化的原理

一维数组

题目:

    int a[] = { 1,2,3,4 };
    
    printf("%d\n", sizeof(a));
    
    数组名a单独放进sizeof中,数组名a表示整个数组
    sizeof得到的值是 数组a的总大小 在内存中占用字节的大小
    :4int*4(四个元素) = 16
    
    printf("%d\n", sizeof(a + 0));
    
    虽然看似a+0没有什么变化,但是如果数组名a不是单独放到sizeof中
    则数组名a表示的是数组首元素的地址(1的地址)
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(*a));
    
    数组名a没有单独放到sizeof中,所以表示的是数组首元素地址
    数组首元素地址进行解引用,得到的是数组首元素((int1sizeof得到的值是(int1在内存中占用字节的大小
    := 4
    
    printf("%d\n", sizeof(a + 1));
    
    数组名a没有单独放到sizeof中,所以表示的是数组首元素地址
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为int*,所以跳过一个整形空间
    指针先前指向的是数组首元素地址((int*1),跳过后,指向数组第二个元素的地址((int *2sizeof得到的值是((int*2)在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8

    printf("%d\n", sizeof(a[1]));

    a[1]表示的是数组下标为1的元素((int2sizeof得到的值是((int2)在内存中占用字节的大小
    := 4
    
    printf("%d\n", sizeof(&a));
    
    &a表示取出了整个数组a的指针(int (*)[4]sizeof得到的值是(int (*)[4])在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(*&a));

    &a表示取出了整个数组a的指针(int (*)[4]*&a)表示通过&a解引用找到了a数组
    (以上两步亦可理解为*&可以互相抵消)
    以上两步进行后,此时sizeof中的操作数为a
    sizeof得到的值是 数组a的总大小 在内存中占用字节的大小
    :4int*4(四个元素) = 16
    
    printf("%d\n", sizeof(&a + 1));
    
    &a表示取出了整个数组a的指针(int (*)[4])
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为(int (*)[4]),所以跳过一个整形空间
    指针先前指向的是数组首元素地址((int*1),跳过后,指向数组第二个元素的地址((int *2sizeof得到的值是((int *2)在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8

    printf("%d\n", sizeof(&a[0]));

    &a[0]表示的是取出a数组中下标为0的元素((int1sizeof得到的值是((int1)在内存中占用字节的大小
    :4
    
    printf("%d\n", sizeof(&a[0] + 1));
    &a[0]得到的是a数组首元素的地址((int *)a[0])
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为int*,所以跳过一个整形空间
    指针先前指向的是数组首元素地址((int*1),跳过后,指向数组第二个元素的地址((int *2sizeof得到的值是((int *2)在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
字符数组
char arr[] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };

题目:

strlen
    char arr[] = { 'a','b','c','d','e','f' };
    (该方式赋值不会自动在字符串末尾添加'\0')
    (需要你给一个字符指针,然后函数就会以这个指针为起点,不断地向后找'\0',并且给你返回一个从这个指针到'\0'的整形数,记作字符串的长度)

    printf("%d\n", strlen(arr));
    
    arr不是&数组名,也不是sizeof里面单独放一个数组名,所以arr是首元素地址((char*)a)
    因为arr数组中没有'\0'作为终止位置,所以strlen将从地址((char *'a')不断往后寻找,直到找到了'\0',因此返回值为随机值
    := 随机值
    
    printf("%d\n", strlen(arr + 0));
    
    arr不是&数组名,也不是sizeof里面单独放一个数组名,所以arr是首元素地址((char*'a')
    地址进行+0的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过零个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char *'a')
    因为arr数组中没有'\0'作为终止位置,所以strlen将从地址((char *'a')不断往后寻找,直到找到了'\0',因此返回值为随机值
    := 随机值
    
    printf("%d\n", strlen(*arr));

    arr不是&数组名,也不是sizeof里面单独放一个数组名,所以arr是首元素地址((char*'a'*arr表示通过((char*'a')解引用找到了字符((char'a')
    strlen的操作数要求的参数类型为(char*),和现在的操作数((char'a')不匹配
    为了运行下去,strlen函数会将操作数的类型强制转化成(char*)
    ((char'a')强制类型转化为((char*97)(因为'a'的ascll码值为97,地址的形式是以数字的方式呈现的)
    strlen将从地址((char*97)不断往后寻找,直到找到了'\0',但是因为((char*97)不属于我们创造的地址,访问方式为非法访问
    编译器将会报错
    
    printf("%d\n", strlen(arr[1]));
    
    arr[1]表示为arr数组里下标为1的元素(((char*'b'))
    strlen的操作数要求的参数类型为(char*),和现在的操作数((char'b')不匹配
    为了运行下去,strlen函数会将操作数的类型强制转化成(char*)
    ((char'b')强制类型转化为((char*98)(因为'a'的ascll码值为98,地址的形式是以数字的方式呈现的)
    strlen将从地址((char*98)不断往后寻找,直到找到了'\0',但是因为((char*98)不属于我们创造的地址,访问方式为非法访问
    编译器将会报错
    
    printf("%d\n", strlen(&arr));
     
     &arr得到的是整个arr数组的地址
     strlen的操作数要求的参数类型为(char*),和现在的操作数(char (*)[6])不匹配    
    为了运行下去,strlen函数会将操作数的类型强制转化成(char*)
    在地址的角度看,整个数组的地址和数组首元素地址在数值上是一致的
     所以,strlen的操作数可看作为数组首元素地址((char*'a')
     因为arr数组中没有'\0'作为终止位置,所以strlen将从地址((char *'a')不断往后寻找,直到找到了'\0',因此返回值为随机值
    := 随机值
    
    printf("%d\n", strlen(&arr[0] + 1));
     
     &arr[0]得到的是arr[0]的地址((char*'a')
     地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b')
    因为arr数组中没有'\0'作为终止位置,所以strlen将从地址((char *'b')不断往后寻找,直到找到了'\0',因此返回值为随机值
    := 随机值

sizeof
    char arr[] = { 'a','b','c','d','e','f' };
    (该方式赋值不会自动在字符串末尾添加'\0')
    (计算的是操作数在内存中占用的字节大小,根据操作数的类型属性决定)
    
    printf("%d\n", sizeof(arr));
    
    函数名arr单独放到sizeof,表示的是整个数组
    sizeof得到的值是 数组a的总大小 在内存中占用字节的大小
    :1char* 6(六个元素) = 6
    
    printf("%d\n", sizeof(arr + 0));
    
    数组名arr没有单独放到sizeof,也没有&arr,所以arr表示的是数组首元素地址((char*'a')
    地址进行+0的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'a'sizeof得到的值是((char*'a')在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(*arr));
    
    数组名arr没有单独放到sizeof,也没有&arr,所以arr表示的是数组首元素地址((char*'a'*arr表示通过数组首元素地址((char*'a')访问到((char'a'sizeof得到的值是((char'a')在内存中占用字节的大小
    := 1char类型占一个字节)
    
    printf("%d\n", sizeof(arr[1]));//1
    
    arr[1]表示为数组arr的下标为1的元素((char)'b'sizeof得到的值是((char'b')在内存中占用字节的大小
    := 1char类型占一个字节)
    
    printf("%d\n", sizeof(&arr));
    
    &arr表示取出的是整个数组的地址
    sizeof得到的值是(char(*)[6])在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(&arr[0] + 1));
    
    &arr[0]得到的是数组arr中下表为0的元素的地址((char*'a')
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b'sizeof得到的值是((char*'b')在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8

printf("%d\n", sizeof(&arr + 1));
  • &arr得到的是整个数组的地址(char(*)[6])

  • 地址进行+1的操作,跳过多少个字节取决于指针类型

  • 当前指针类型为(char(*)[6]),所以跳过一个字符数组的空间(char [6])
    在这里插入图片描述

  • 指针先前指向的是整个数组的地址p1(char(*)[6]),跳过一个字符数组空间后,指向p2

  • sizeof得到的值是((char(*)[6]))在内存中占用字节的大小

  • 地址的大小是4/8个字节

  • := 4/8

char arr[] = “abcdef”;

题目:

strlen
    char arr[] = "abcdef";
   (该方式赋值会自动在字符串末尾添加'\0')
   (相当于{'a',  'b',  'c',  'd',  'e',  'f',  '\0'})
   (需要你给一个字符指针,然后函数就会以这个指针为起点,不断地向后找'\0',并且给你返回一个从这个指针到'\0'的整形数,记作字符串的长度)
   
    printf("%d\n", strlen(arr));
    
    arr不是单独放在sizeof中,也不是&arr,表示的是首元素的地址((char *'a')
    strlen将从地址((char *'a')不断往后寻找,直到找到了'\0',因此返回值为6= 6
    
    printf("%d\n", strlen(arr + 0));
    arr不是单独放在sizeof中,也不是&arr,表示的是首元素的地址((char *'a')
    地址进行+0的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'a')
    strlen将从地址((char *'a')不断往后寻找,直到找到了'\0',因此返回值为6= 6
    
    printf("%d\n", strlen(*arr));

    arr不是单独放在sizeof中,也不是&arr,表示的是首元素的地址((char *'a'*arr表示通过数组首元素地址((char*'a')访问到((char'a')
    strlen的操作数要求的参数类型为(char*),和现在的操作数((char'a')不匹配
    为了运行下去,strlen函数会将操作数的类型强制转化成(char*)
    ((char'a')强制类型转化为((char*97)(因为'a'的ascll码值为97,地址的形式是以数字的方式呈现的)
    strlen将从地址((char*97)不断往后寻找,直到找到了'\0',但是因为((char*97)不属于我们创造的地址,访问方式为非法访问
    编译器将会报错
    
    printf("%d\n", strlen(arr[1]));
    
    arr[1]表示的是数组arr中下标为1的元素((char'b')
    strlen的操作数要求的参数类型为(char*),和现在的操作数((char'b')不匹配
    为了运行下去,strlen函数会将操作数的类型强制转化成(char*)
    ((char'b')强制类型转化为((char*98)(因为'b'的ascll码值为98,地址的形式是以数字的方式呈现的)
    strlen将从地址((char*98)不断往后寻找,直到找到了'\0',但是因为((char*98)不属于我们创造的地址,访问方式为非法访问
    编译器将会报错
    
    printf("%d\n", strlen(&arr[0] + 1));

    arr[0]表示的是数组arr中下标为1的元素((char'a'&arr[0]得到的是数组arr中下标为1的元素((char'a')的地址((char*'a')
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b')
    strlen将从地址((char *'b')不断往后寻找,直到找到了'\0',因此返回值为5= 5

printf("%d\n", strlen(&arr));
  • &arr表示的是取出整个数组的地址(char(*)[7])

  • (↑↑↑相比于{ ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ }的形式, "abcdef"形式因为会自动补’\0’而{ ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ }的形式不会自动补’\0’,所以数组大小要比第一种大一)

  • 在这里插入图片描述

  • strlen的操作数要求的参数类型为(char*),和现在的操作数(char()[7])不匹配
    为了运行下去,strlen函数会将操作数的类型强制转化成(char

  • 由于整个数组的地址在数值上与数组首元素地址一致,且strlen只需要得到(char*)的地址,所以可以将&arr的地址值看做首元素地址((char *)‘a’)

  • strlen将从地址((char *)‘a’)不断往后寻找,直到找到了’\0’,因此返回值为6


printf("%d\n", strlen(&arr + 1));
  • &arr得到的是整个数组的地址(char(*)[7])

  • 地址进行+1的操作,跳过多少个字节取决于指针类型

  • 当前指针类型为(char(*)[7]),所以跳过一个字符数组空间
    在这里插入图片描述

  • 指针先前指向的是数组首元素地址p1,跳过后,指向数组第二个元素的地址p2

  • strlen将从地址p2不断往后寻找,直到找到了’\0’,由于在后面的内存中不知什么时候才能找到’\0’,因此返回值为随机值

  • := 随机值

sizeof
    char arr[] = "abcdef";
   (该方式赋值会自动在字符串末尾添加'\0')
   (相当于{'a',  'b',  'c',  'd',  'e',  'f',  '\0'})
   (计算的是操作数在内存中占用的字节大小,根据操作数的类型属性决定)
    p.s:'\0'也算一个字符

    printf("%d\n", sizeof(arr));

    arr单独放到sizeof中表示的是整个数组
    sizeof得到的值是 数组arr的总大小 在内存中占用字节的大小
    :1char* 7(七个元素) = 7
    
    printf("%d\n", sizeof(arr + 0));

    arr不是单独放到sizeof中,也不是&arr,所以表示的是数组首元素的地址((char*'a')
    地址进行+0的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过零个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'a'sizeof得到的值是(char*'a')在内存中占用字节的大小
    =4/8


    printf("%d\n", sizeof(*arr));
    *arr是数组首元素((char'a'sizeof得到的值是(char'a')在内存中占用字节的大小
    =1
    
    printf("%d\n", sizeof(arr[1]));
    
    arr[1]表示的是数组中下标为1的元素((char'b'sizeof得到的值是(char'b')在内存中占用字节的大小
    =1
    printf("%d\n", sizeof(&arr));
    &arr表示的是取出了整个数组的地址(char*[7]sizeof得到的值是(char*[7])在内存中占用字节的大小
    =4/8
    
    printf("%d\n", sizeof(&arr + 1));
    &arr表示的是取出了整个数组的地址(char*[7])
    地址进行+0的操作,跳过多少个字节取决于指针类型
    当前指针类型为(char*[7]),所以跳过七个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'a'+7sizeof得到的值是((char*'a'+7)在内存中占用字节的大小
    =4/8
    
    printf("%d\n", sizeof(&arr[0] + 1));
    &arr[0]取出的是数组首元素的地址(char*'a')
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为(char),所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b'sizeof得到的值是((char*'b')在内存中占用字节的大小
    =4/8
指针接收字符串
    char* p = "abcdef";
   (该方式赋值会自动在字符串末尾添加'\0')
   (相当于{'a',  'b',  'c',  'd',  'e',  'f',  '\0'}printf("%d\n", strlen(p));
    
    p没有单独放到sizeof内部,也没有&,表示的是数组首元素的地址((char*'a')
    strlen计算的是从地址处((char*'a')不断往后找,直到找到了'\0'后的长度
    = 6
    
    printf("%d\n", strlen(p + 1));
    
    p没有单独放到sizeof内部,也没有&,表示的是数组首元素的地址((char*'a')
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b')
    strlen计算的是从地址处((char*'b')不断往后找,直到找到了'\0'后的长度
    =5
    
    printf("%d\n", strlen(*p));
    
    p表示的是字符串首元素的地址((char*'a'*p表示向地址((char*'a')解引用得到((char'a')
    strlen的操作数要求的参数类型为(char*),和现在的操作数((char'a')不匹配
    为了运行下去,strlen函数会将操作数的类型强制转化成(char*)
    ((char'a')强制类型转化为((char*97)(因为'a'的ascll码值为97,地址的形式是以数字的方式呈现的)
    strlen将从地址((char*97)不断往后寻找,直到找到了'\0',但是因为((char*97)不属于我们创造的地址,访问方式为非法访问
    编译器将会报错
    
    printf("%d\n", strlen(p[0]));
    
    p[0]表示的是该字符串的第一个元素((char'a')
    strlen的操作数要求的参数类型为(char*),和现在的操作数((char'a')不匹配
    为了运行下去,strlen函数会将操作数的类型强制转化成(char*)
    ((char'a')强制类型转化为((char*97)(因为'a'的ascll码值为97,地址的形式是以数字的方式呈现的)
    strlen将从地址((char*97)不断往后寻找,直到找到了'\0',但是因为((char*97)不属于我们创造的地址,访问方式为非法访问
    编译器将会报错
    
    printf("%d\n", strlen(&p[0] + 1));
    
    p[0]表示的是该字符串的第一个元素((char'a'&p[0]表示的是取出字符串的第一个元素((char'a')的地址((char*'a')
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b')
    strlen计算的是从地址处((char*'b')不断往后找,直到找到了'\0'后的长度
    =5
    
    printf("%d\n", sizeof(p));
    p是一个字符指针
    sizeof得到的值是(char(*))在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(p + 1));
    
    p是一个字符指针
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b'sizeof得到的值是(char(*))在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(*p));
    
    p表示的是字符串首元素的地址((char*'a'*p表示向地址((char*'a')解引用得到((char'a'sizeof得到的值是(char)在内存中占用字节的大小
    =1
    
    printf("%d\n", sizeof(p[0]));
    
    p[0]表示的是该字符串的第一个元素((char'a'sizeof得到的值是(char)在内存中占用字节的大小
    =1
    
    printf("%d\n", sizeof(&p));
    
    p是一个字符指针
    &p表示的是取出这个字符指针的指针(应当用二级指针接收)
    sizeof得到的值是(char**)在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(&p + 1));
    
    p是一个字符指针
    &p表示的是取出这个字符指针的指针(二级指针)
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char**,所以跳过一个字符指针的空间(四个字节)
    指针先前指向的是数组首元素地址((char**&p),跳过后,指向数组第二个元素的地址((char**&p+4sizeof得到的值是(char**)在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(&p[0] + 1));
    p[0]表示的是该字符串的第一个元素((char'a'&p[0]表示的是取出字符串的第一个元素((char'a')的地址((char*'a')
    地址进行+1的操作,跳过多少个字节取决于指针类型
    当前指针类型为char*,所以跳过一个字符空间
    指针先前指向的是数组首元素地址((char*'a'),跳过后,指向数组第二个元素的地址((char*'b'sizeof得到的值是(char*)在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8

特殊案例

printf("%d\n", strlen(&p));
    
printf("%d\n", strlen(&p + 1));
  • &p表示的是取出这个字符指针的指针(应当用二级指针接收)
  • 地址进行+1的操作,跳过多少个字节取决于指针类型
  • 当前指针类型为char**,所以跳过一个字符指针的空间(四个字节)
    
  • 指针先前指向的是数组首元素地址((char**)&p),跳过后,指向数组第二个元素的地址((char**)&p+4)
    在这里插入图片描述

由上图,可得出以下结论

  • p是一个字符指针,&p得到的一个字符指针的地址,用二级指针来接收
  • 蓝色:p指向的类型是(char),解引用和+1或-1操作时,访问的是一个字节的空间
  • 蓝色:p指向的类型是(char),解引用和+1或-1操作时,访问的是一个字节的空间
  • 橙色:&p指向的类型是(char*),解引用和+1或-1操作时,访问的是四个字节的空间
  • 蓝色:strlen(p)和strlen(p+1)存在紧密联系,在值上strlen(p+1)比strlen(p)小1
  • 绿色:strlen(&p)和strlen(&p+1)在值上不存在紧密联系,原因如下

(strlen的操作数要求的参数类型为(char*),和现在的操作数((char)‘a’)不匹配
为了运行下去,strlen函数会将操作数的类型强制转化成(char*))
当前,(&p)和(&p+1)强制转化成char*,也就代表着它们每次只能访问1个字节
&p和&p+1之间存在四个字节的空间(绿色箭头指向的位置)
假设编号3的位置是’\0’,则strlen(&p)= 3,而strlen(&p+1)因为不确定’\0’的位置而为随机值
只有在&p和&p+1之间存在的四个字节空间内没有’\0’的情况下,strlen(&p)才等于strlen(&p+1)+4

二维数组

题目:
int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
做题的前提:

  1. 在内存中的存储:
    在这里插入图片描述

  2. 二维数组可以理解为是一维数组的数组,a[0]表示的是第一行的数组名。是数组a的第一个元素,a[1]表示的是第二行的数组名,是数组a的第二个元素;如果不是&a或者sizeof中单独放a,表示的是首元素的地址,即&a[0]

    int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
    
    printf("%d\n", sizeof(a)); 
    
    a这个二维数组的数组名单独放在sizeof内部,a表示的是整个数组
    sizeof得到的值是 数组a的总大小 在内存中占用字节的大小
    :4int* 12(十二个元素) = 48
    
    printf("%d\n", sizeof(a[0][0]));

    a[0][0]表示第一行第一个元素
    4个字节
    
    printf("%d\n", sizeof(a[0]));//16
    
    a[0] 第一行的数组名,这时数组名单独放在sizeof内部了
    计算的是数组(int [4])的大小,单位是字节,16
    
    printf("%d\n", sizeof(a[0] + 1));//4
    
    a[0]不是单独放在sizeof内部,a[0]表示的首元素的地址,即第一行第一个元素的地址,(可理解为&a[0][0])
    a[0] + 1 是第一行第2个元素的地址 &a[0][1]
    sizeof得到的值是(int*)在内存中占用字节的大小
    地址的大小是4/8个字节
    := 4/8
    
    printf("%d\n", sizeof(*(a[0] + 1)));
    a[0]不是单独放在sizeof内部,a[0]表示的首元素的地址,即第一行第一个元素的地址,(可理解为&a[0][0])
    a[0] + 1 是第一行第2个元素的地址 &a[0][1]
    *(a[0] + 1)表示通过(a[0] + 1)解引用找到a[0][1]
    
    printf("%d\n", sizeof(a + 1));
    a作为二维数组的数组名并非单独放在sizeof内部,所以表示首元素的地址
    二维数组的首元素是第一行,这里的a就是第一行的地址(int (*)[4])
    a+1是跳过第一行,指向了第二行
    sizeof得到的值是(int (*)[4])在内存中占用字节的大小
    地址的大小是4/8个字节

    printf("%d\n", sizeof(*(a + 1)));
    
    a作为二维数组的数组名并非单独放在sizeof内部,所以表示首元素的地址
    二维数组的首元素是第一行,这里的a就是第一行的地址(int (*)[4])
    a+1是跳过第一行,指向了第二行
    对第二行的地址进行解引用,得到的是第二行的数组
    sizeof计算的是数组(int [4])的大小,单位是字节,16
    
    printf("%d\n", sizeof(&a[0] + 1));
    
    &a[0]是第一行的地址
    &a[0]+1是第二行的地址
    sizeof得到的值是(int (*)[4])在内存中占用字节的大小
    地址的大小是4/8个字节
    
    printf("%d\n", sizeof(*(&a[0] + 1)));
          
    &a[0]+1是第二行的地址
    对第二行的地址进行解引用得到第二行的数组a[1]
    sizeof计算的是数组(int [4])的大小,单位是字节,16
    
    printf("%d\n", sizeof(*a));
    
    a不是单独放到sizeof中,表示的是数组首元素地址(int *[4])
    对数组首元素地址(int *[4])解引用得到的是第一行的数组
    sizeof计算的是数组(int [4])的大小,单位是字节,16
    
    

特殊案例

printf("%d\n", sizeof(a[3]));
  • a[3]得到的是数组a的第四行的数组名,类型还是(int [4])
  • 【因为这个a[3]是源于a数组的】
  • sizeof计算的是数组(int [4])的大小,单位是字节,16

这里看似是越界访问了,按道理来说应该会报警告

但是这个和sizeof的原理有关,sizeof内部的表达式不会真的去计算,只需要通过类型就可以得出结果,计算的步骤反而冗余了,不存在计算就不存在越界访问

sizeof科普:

  1. 任何一个值或者表达式都存在两个属性,一是值属性,二是类型属性

例:
int a = 7;
a+3
值属性:10
类型属性:int

  1. 代码写出来之后,要进行编译、链接、运行的过程,sizeof已经在编译过程中根据类型属性得到了值,所以值就不会再计算了
    int a = 5;
    short s = 11;
    printf("%d\n", sizeof(s = a + 2));//2
    printf("%d\n", s);//11

以上就是关于数组和指针运用理解的练习,如果有什么地方不足,欢迎评论区或私信
如果这篇文章对你有用的话,希望可以得到一个免费的赞,拜托了捏

  • 18
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CtrlZ大牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值