1.对数组名的理解
①数组名是数组首元素的地址。
②但是有两个例外:
sizeof(数组名)这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
&数组名,这里的数组名也表示整个数组,取出的是数组的地址。
//一维数组 int a[] = {1,2,3,4};//4个元素,每个元素是int类型(4个字节) printf("%d\n",sizeof(a)); //16 数组名a单独放在sizeof内部,计算的是整个数组的大小,单位是字节,是16个字节 printf("%d\n",sizeof(a+0)); //a并非单独放在sizeof内部,也没有&,所以数组名表示首元素的地址,a+0还是首元素地址,是地址就是4/8个字节 printf("%d\n",sizeof(*a)); //a并非单独放在sizeof内部,也没有&,所以还是表示首元素地址,对a解引用,就是a[0],大小是4个字节 printf("%d\n",sizeof(a+1)); //a并非单独放在sizeof内部,也没有&,表示首元素地址,a+1就表示第二个元素的地址,等价于&a[1],是地址就是4/8个字节 printf("%d\n",sizeof(a[1])); //a并非单独放在sizeof内部,也没有&,表示数组第二个元素,计算的是第二个元素的大小单位是字节-4 printf("%d\n",sizeof(&a)); //&a--表示的是:取出整个数组的地址,但是数组的地址也是地址,是地址就是4/8个字节 //数组的地址 和 数组首元素的地址的 本质区别是类型的区别,并非大小的区别 //a--int* int* p= a; 解引用访问一个整型的大小 //&a--int *()[4] int (*p)[4] =&a; 解引用访问一个数组的大小 printf("%d\n",sizeof(*&a)); //16字节--&a,取出数组的地址,解引用可以访问整个数组的大小,即4个整型的大小,就是16字节 //*和&可以互相抵消,所以sizeof(*&a)==sizeof(a),所以是16字节 printf("%d\n",sizeof(&a+1)); //4/8字节--&a取出的是数组a的地址,&a+1还是地址,是地址就是4/8个字节,需要理解的是,&a+1跳过的是一个数组 printf("%d\n",sizeof(&a[0])); //4/8字节--取出的是数组首元素的地址,计算的是地址的大小,是地址就是4/8个字节 printf("%d\n",sizeof(&a[0]+1)); //4/8字节--&a[0]取出首元素的地址,+1就是第二个元素的地址,是地址就是4/8个字节
//字符数组 char arr[] = {'a','b','c','d','e','f'}; printf("%d\n", sizeof(arr)); //数组名arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,是6个字节 printf("%d\n", sizeof(arr+0)); //arr并非单独放在sizeof内部,也没有&,所以arr是首元素的地址,&arr[0],是地址就是4/8个字节 //在这里简单说明一下,指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节 //指针变量是用来存放地址的,地址存放需要多大空间,指针变量的大小就是几个字节 //32位环境下,地址是32个二进制位,需要4个字节 //64位环境下,地址是64个二进制位,需要8个字节 printf("%d\n", sizeof(*arr)); //arr表示数组首元素的地址,解引用就是数组首元素的,大小是1个字节 printf("%d\n", sizeof(arr[1])); //arr[1]表示第二个元素,大小是1个字节 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)); //&arr[0]取出首元素地址,+1就是第二个元素的地址,是地址就是4/8个字节
分析了上面的代码后,对上面的代码稍加修改,再来看一下输出的结果。
//strlen 求字符串长度,统计的是在字符串中\0之前的字符个数 char arr[] = {'a','b','c','d','e','f'}; //当前数组并没有在末尾有'\0' printf("%d\n", strlen(arr)); //>=6随机值--arr是首元素的地址(没有'\0') printf("%d\n", strlen(arr+0)); //arr是首元素地址,+0还是首元素地址,还是随机值 //printf("%d\n", strlen(*arr)); //通常给strlen传的参数是地址,arr是首元素的地址,*arr就是首元素,字符'a'的ASCII码值是97, //站在strlen角度,认为传参进去的'a'-97就是地址,97作为地址直接进行访问,就是非法访问(错误代码) //printf("%d\n", strlen(arr[1])); //同上一样,也是错误代码 printf("%d\n", strlen(&arr)); //&arr取出整个数组的地址,类型是--char (*)[6],而strlen的参数类型是const char*, //会有类型上的差异,但是依然可以运行, printf("%d\n", strlen(&arr+1)); //&arr取出数组的地址,+1跳过数组,依然是随机值 printf("%d\n", strlen(&arr[0]+1)); //从第二个元素的地址开始,仍然是随机值
运行结果都是随机值:
char arr[] = "abcdef"; //a b c d e f \0 printf("%d\n", sizeof(arr)); //计算整个数组的大小,7 printf("%d\n", sizeof(arr+0)); //表示首元素地址,大小4/8个字节 printf("%d\n", sizeof(*arr)); //表示首元素,大小是1个字节 printf("%d\n", sizeof(arr[1])); //表示第二个元素,大小是1个字节 printf("%d\n", sizeof(&arr)); //表示数组的地址,是地址,就是4/8个字节 printf("%d\n", sizeof(&arr+1)); //表示跳过一个数组,仍然是地址,是地址就是4/8个字节 printf("%d\n", sizeof(&arr[0]+1)); //取出第一个元素的地址,+1就是第二个元素的地址,那么就是4/8个字节
char arr[] = "abcdef"; //a b c d e f \0 printf("%d\n", strlen(arr)); //表示首元素地址,统计\0之前的字符,结果是6 printf("%d\n", strlen(arr+0)); //首元素地址+0还是首元素地址,结果还是6 printf("%d\n", strlen(*arr));//err //*arr表示第一个元素,会报错 printf("%d\n", strlen(arr[1]));//err //同上的道理,传进去的是第二个元素的ASCII码值 printf("%d\n", strlen(&arr)); //取出的数组的地址,结果也是6 printf("%d\n", strlen(&arr+1)); //&arr+1跳过整个数组,把\0也跳过,结果就是随机值 printf("%d\n", strlen(&arr[0]+1)); //表示第二个元素的地址,结果是5
char* p = "abcdef"; //把a的地址存到指针变量p中 printf("%d\n", sizeof(p)); //4/8,计算的是指针变量的大小 printf("%d\n", sizeof(p + 1)); //p+1指向第二个元素,存的是第二个元素的地址,就是4/8个字节 printf("%d\n", sizeof(*p)); //*p是第一个元素,大小是1个字节 printf("%d\n", sizeof(p[0]));//p[0]==*(p+0)--->*p //也是第一个元素,大小是1个字节 printf("%d\n", sizeof(&p)); //取出的是p的地址,大小也是4/8个字节,p的地址的类型是char**,是二级指针 printf("%d\n", sizeof(&p + 1)); //&p是地址,+1还是地址,是地址就是4/8个字节 //需要理解的是:&p+1指向哪里?跳过一个p的大小 printf("%d\n", sizeof(&p[0] + 1)); //表示的是第二个元素的地址,是地址就是4/8个字节 printf("%d\n", strlen(p)); //6 printf("%d\n", strlen(p + 1)); //5 printf("%d\n", strlen(*p));//err printf("%d\n", strlen(p[0]));//err printf("%d\n", strlen(&p)); //&p的地址,无法确定有没有\0,是随机值 printf("%d\n", strlen(&p + 1)); //随机值,这里不再过多解释 printf("%d\n", strlen(&p[0] + 1)); //5