学C的第二十七天【指针的进阶(三)】-1

简介: 复习巩固:数组名:数组名是数组首元素的地址,但是有两个例外:

复习巩固:

数组名:

数组名数组首元素的地址

但是有两个例外

1 . sizeof(数组名) :这里的数组名表示整个数组计算的是整个数组的大小单位字节

               

2 . &数组名 :这里的数组名表示整个数组取出的是整个数组的地址

9. 指针和数组笔试题解析

补充(回顾):

  • sizeof() 是一个运算符,而 strlen() 是一个函数
  • sizeof() 计算的是变量或类型所占用的内存字节数,而 strlen() 计算的是字符串中字符的个数
  • sizeof()可以用于任何类型的数据,而strlen() 只能用于以空字符 '\0' 结尾的字符串
  • sizeof()计算字符串的长度包含末尾的 '\0'strlen() 计算字符串的长度不包含字符串末尾的 '\0'

                 

(1). 一维数组相关题:

image.png

对应代码:

#include <stdio.h>
int main()
{
  int a[] = { 1,2,3,4 };
  printf("%d\n", sizeof(a));
  //特殊情况,这里的a为整个数组,为4*4=16字节
  printf("%d\n", sizeof(a + 0));
  //数组名a是首元素地址,a+0还是首元素地址,地址的大小为4/8字节
  printf("%d\n", sizeof(*a));
  //数组名a为首元素地址,*a就是首元素,大小是4个字节
  printf("%d\n", sizeof(a + 1));
  //数组名是首元素地址。a+1是第二个元素地址,为4/8字节
  printf("%d\n", sizeof(a[1]));
  //a[1]是数组的第二个元素,为4个字节
  printf("%d\n", sizeof(&a));
  //&a是整个数组的地址,数组地址也只是个地址,为4/8字节
  printf("%d\n", sizeof(*&a));
  //解引用整个数组的地址,相当于sizeof(a),为16个字节
  printf("%d\n", sizeof(&a + 1));
  //相当于跳过了整个数组,虽然跳过了整个数组,&a+1依然是个地址,为4/8个字节
  printf("%d\n", sizeof(&a[0]));
  //取数组首元素的地址,为4/8个字节
  printf("%d\n", sizeof(&a[0] + 1));
  //&a[0]是首元素地址,&a[0]+1就是第二个元素的地址,是地址就为4/8个字节
  return 0;
}

(2). 字符数组和指针相关题:

(2.1). 明确赋值的字符数组(无结束符\0):

3825ed0cee8d4ef2a4e6615ed7389bda.png


66ce805074c1411480a6f51d0487ca82.png

对应代码:

char arr[] = { 'a','b','c','d','e','f' };
  //六个字符的字符数组,没有结束符\0
  printf("%d\n", sizeof(arr));
  //数组名单独放在sizeof中,这里的arr为整个数组,计算的是整个数组大小,为6个字节 -- char[6]
  printf("%d\n", sizeof(arr + 0));
  //arr表示数组首元素地址,arr+0还是数组首元素地址,是地址就为4/8个字节 -- char*
  printf("%d\n", sizeof(*arr));
  //arr表示数组首元素地址,*arr就是首元素,为1个字节 -- char
  printf("%d\n", sizeof(arr[1]));
  //arr[1]就是第二个元素,为1个字节 -- char
  printf("%d\n", sizeof(&arr));
  //&arr是整个数组的地址,数组地址也是地址,是地址就为4/8个字节
  printf("%d\n", sizeof(&arr + 1));
  //&arr+1是跳过整个数组后的地址,是地址就为4/8个字节
  printf("%d\n", sizeof(&arr[0] + 1));
  //为第二个元素的地址,为4/8个字节
  printf("%d\n", strlen(arr));
  //因为字符数组arr中没有\0,所以在求字符串长度时,
  //会一直往后数到未知的\0为止,是不确定的,所以会产生随机值
  printf("%d\n", strlen(arr + 0));
  //arr+0是首元素地址,所以和上面的一样会产生随机值
  printf("%d\n", strlen(*arr));
  //arr是数组首元素地址,*arr是数组的元素,是 'a' ,ACSII码为97,
  //所以strlen会从 '97' 的地址开始统计字符串长度,为非法访问内存
  printf("%d\n", strlen(arr[1]));
  //arr[1]为数组首元素,即 'a' ,所以和上面一样为非法访问内存
  printf("%d\n", strlen(&arr));
  //&arr是数组地址,数组地址和数组首元素的地址,两者的值是一样的,
  //数组地址类型char*[6],会强制转化为char*类型,所以依然从数组的首元素开始向后统计,结果为随机值
  printf("%d\n", strlen(&arr + 1));
  //&arr+1为跳过这个数组,所以从这个数组后的地址开始统计,结果为随机值
  printf("%d\n", strlen(&arr[0] + 1));
  //&arr[0]+1是第二个元素的地址,往后统计直到遇到\0,结果为随机值

(2.2). 未明确赋值的字符数组(有结束符\0):

9b7e0fb672d14e96a2893bdbfcf8a2a5.png

09f6fd9461cf476a93fe1d6994e419d2.png

对应代码:

  char arr[] = "abcdef";
  //未明确定义的字符数组,有结束符\0
  //    {a b c d e f \0}
  printf("%d\n", sizeof(arr)); 
  //sizeof(数组名):计算整个数组的大小,为7个字节,包含\0 -- char [7]
  printf("%d\n", sizeof(arr + 0));
  //arr+0是首元素的地址,是地址就是4/8个字节 -- char*
  printf("%d\n", sizeof(*arr));
  //*arr为数组首元素,为1个字节 -- char
  printf("%d\n", sizeof(arr[1]));
  //arr[1]为第二个元素,为1个字节 -- char
  printf("%d\n", sizeof(&arr));
  //&arr是数组的地址,是地址就是4/8个字节 -- char* [7]
  printf("%d\n", sizeof(&arr + 1));
  //&arr为跳过arr这个数组后的地址,是地址就是4/8个字节 -- 随机地址
  printf("%d\n", sizeof(&arr[0] + 1));
  //&arr[0]+1是第二个元素的地址,是地址就是4/8个字节 -- char*
  printf("%d\n", strlen(arr));
  //统计到\0为止,即 a b c d e f ,所以结果为6
  printf("%d\n", strlen(arr + 0));
  //和上题一样,都是从数组首元素开始,\0结束,所以结果为6
  printf("%d\n", strlen(*arr));
  //strlen应该接收一个地址,从这个地址向后计算长度,
  //这里接收的是数组首元素字符'a'(char),不是地址,所以会报错
  printf("%d\n", strlen(arr[1]));
  //这里和上面一样接收的也是数组首元素字符'a',不是地址,会报错
  printf("%d\n", strlen(&arr));
  //接收数组指针char*[7],但是会被强制转化为const char*类型,
  //值还是首元素的地址,所以统计的长度还是6
  printf("%d\n", strlen(&arr + 1));
  //&arr+1是跳过这个数组后的一个地址,从这个地址往后不知道什么时候后有\0,所以结果为随机值
  printf("%d\n", strlen(&arr[0] + 1));
  //&arr[0]+1为数组的第二个元素地址,从后计算长度直到\0,所以结果为5

(2.3). 未明确赋值的字符指针(有结束符\0):


9e5048e7da5b4e53a2aa1315ce3c0abc.png

image.png

对应代码:

  char* p = "abcdef";
  //这样写是把字符串的首字符地址放入指针中,即‘a’的地址
  printf("%d\n", sizeof(p));
  //p为一级指针,存放的是地址,是地址就是4/8个字节
  printf("%d\n", sizeof(p + 1));
  //p+1是字符'b'的地址,是地址就是4/8个字节
  printf("%d\n", sizeof(*p));
  //相当于‘b’的地址,是地址就是4/8个字节
  printf("%d\n", sizeof(p[0]));
  //p[0]相当于*(p+0),即*p,为字符‘a’,为1个字节
  printf("%d\n", sizeof(&p));
  //取出一级指针p的地址,相当于p的二级指针,存放的是指针p的地址,是地址就是4/8个字节
  printf("%d\n", sizeof(&p + 1));
  //相当于指针p地址的后面一个地址,是地址就是4/8个字节
  printf("%d\n", sizeof(&p[0] + 1));
  //相当于a的地址的后一个地址,即b的地址,是地址就是4/8个字节
  printf("%d\n", strlen(p));
  //指针p存放的是‘a’的地址,所以从a的地址往后数,所以长度为6
  printf("%d\n", strlen(p + 1));
  //p+1是‘b’的地址,所以长度为5
  printf("%d\n", strlen(*p));
  //*p是字符‘a’,strlen的参数应该是const char*指针,所以会报错
  printf("%d\n", strlen(p[0]));
  //字符串可以理解为一个字符数组,所以p[0]相当于字符‘a’,会报错
  printf("%d\n", strlen(&p));
  //&是一级指针p的地址,从该地址往后数直到\0,所以结果为随机值
  printf("%d\n", strlen(&p + 1));
  //从一级指针的地址的后一个地址开始数直到\0,结果为随机值
  printf("%d\n", strlen(&p[0] + 1));
  //相当于a的地址的后一个地址,即b的地址,直到\0,所以结果为5

(3). 二维数组相关题:

网络异常,图片无法展示
|
de9ed86b490546a5ac1889f31fc7af4a.png

对应代码:

//二维数组
  int a[3][4] = { 0 };
  //创建三行四列的二维数组
  printf("%d\n", sizeof(a));
  //三行四列每个4个字节,所以一个是3*4*4=48字节
  printf("%d\n", sizeof(a[0][0]));
  //求该二维数组0行0列的元素大小,为4个字节
  printf("%d\n", sizeof(a[0]));
  //a[0]是二维数组的第一个元素地址,即第一行地址(第一行数组的数组名)
  //相当于把数组名放入sizeof中,所以计算的是整个数组的大小,为16个字节
  printf("%d\n", sizeof(a[0] + 1));
  //这里a[0]不是单独放在sizeof中的,所以是第一行数组的首元素地址,即a[0][0]地址
  //所以a[0]+1是第一行第二个元素的地址,是地址就是4/8个字节
  printf("%d\n", sizeof(*(a[0] + 1)));
  //相当于解引用第一行数组的第二个元素地址,元素是int类型,为4个字节
  printf("%d\n", sizeof(a + 1));
  //a是二维数组首元素地址,即第一行的地址 -- int(*)[4]
  //a+1就是第二行的地址,是地址就是4/8个字节
  printf("%d\n", sizeof(*(a + 1)));
  //a+1是第二行地址--int(*)[4],*(a+1)解引用后是4个int类型的元素
  //为16个字节
  printf("%d\n", sizeof(&a[0] + 1));
  //&a[0]是二维数组第一行数组地址--int(*)[4]
  //&a[0] + 1是第二行的地址,是地址就是4/8个字节
  printf("%d\n", sizeof(*(&a[0] + 1)));
  //&a[0] + 1是第二行的地址,解引用后是4*4=16个字节
  printf("%d\n", sizeof(*a));
  //*a是解引用二维数组首元素地址,即第一行地址,为4*4=16个字节
  printf("%d\n", sizeof(a[3]));
  //a[3]看起来是越界了,但sizeof()运行时是不会访问内存的(类型属性),只看类型
  //这里a[3]的类型是int[4],为4*4=16个字节
  //表达式都有两个属性:1.值属性  2.类型属性
  //代码运行时:编译+链接 --> 可执行程序 --> 运行 --> 结果
  //sizeof是在 编译 时就已经有结果了(类型属性),根据类型为16个字节
  //a[3]是在 运行 是才有结果(值属性),根据运行结果应该为数组越界报错

相关文章
|
5月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
5月前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
5月前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
5月前
|
存储 C语言 C++
指针进阶(函数指针)(C语言)
指针进阶(函数指针)(C语言)
|
5月前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)
|
5月前
|
搜索推荐
指针进阶(2)
指针进阶(2)
47 4
|
5月前
指针进阶(3)
指针进阶(3)
41 1
|
5月前
|
C++
指针进阶(1)
指针进阶(1)
43 1
|
5月前
|
存储 安全 编译器
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
42 2
|
5月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
49 0