引言
在初学编程语言时,我们经常容易被数组名和指针的实际表示含义混淆,本文将从一些数组与指针的笔试题入手,旨在弄明白它们在各种场景下表达的实际含义。
知识点补充
1. 数组名的含义:
(1)sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
(2)&数组名,这里的数组名表示整个数组,取出的是整个数组的地址,不是数组首个元素的地址。
除了这两种情况以外,数组名表示数组首元素的地址。
2. sizeof操作符:
sizeof操作符是C语言和C++语言中的一种特殊操作符,用于获取某个变量或类型的大小(占用的字节数)。它的作用是在编译时求得数据类型或变量的大小,而不是在运行时。
sizeof操作符可以用于以下方面:
- 获取基本数据类型的大小:可以使用sizeof操作符来获取int、float、char等基本数据类型的大小。例如,sizeof(int)将返回int类型的字节数,通常为4个字节。
- 获取数组的大小:sizeof操作符可以用于获取数组的大小,包括静态数组和动态数组。例如,sizeof(array)将返回数组array的总字节数,即元素个数乘以每个元素的大小。
- 获取指针的大小:sizeof操作符可以用于获取指针的大小,即指针所占的字节数。例如,sizeof(int*)将返回指针类型int*的字节数,通常为4或8个字节,这取决于系统的位数。
- 获取结构体和联合体的大小:sizeof操作符可以用于获取结构体和联合体的大小,即结构体或联合体的所有成员变量所占的总字节数。例如,sizeof(struct MyStruct)将返回结构体MyStruct的总字节数。
使用说明:
- sizeof操作符的结果是一个常量表达式,可以在编译时计算得到。
- sizeof操作符的结果是一个size_t类型的无符号整数。
- sizeof操作符的参数可以是变量名、类型名或表达式。
- sizeof操作符不能直接用于函数类型,但可以用于指向函数类型的指针。
- sizeof操作符的结果可能受到编译器和编译选项的影响,不同的编译器可能有不同的实现方式。
需要注意的是,sizeof操作符只是获取内存大小,而不是获取实际存储的数据大小。例如,对一个指针使用sizeof操作符,只会返回指针本身的大小,而不是它所指向的数据的大小。所以在使用sizeof操作符时,需要根据具体的需求选择合适的用法。
3. stelen函数:
strlen函数用于计算字符串的长度,即统计字符串中字符的个数。该函数属于C语言的标准库函数,位于头文件<string.h>中。
使用说明:
-
函数原型:size_t strlen(const char* str);
参数str为要计算长度的字符串,返回值为字符串的长度。注意不应与保存字符串的数组大小相混淆。比如:char mystr[100]=“test string”,定义了一个大小为 100 个字符的字符数组,但初始化 mystr 的 C 字符串长度只有 11 个字符。 因此,sizeof(mystr) 的值是 100,而 strlen(mystr) 的返回值是 11。 -
参数说明:
- str:要计算长度的字符串,可以是字符数组(如char str[100])或字符指针(如const char* str)。
-
返回值说明:
- 返回值类型为size_t,即无符号整数类型。返回值为字符串中字符的个数(不包括字符串末尾的空字符’\0’)。
-
注意事项:
- 传入的字符串必须以空字符’\0’结尾,否则函数会继续向后遍历直到遇到空字符。
- 如果传入的字符串为空指针(即str为NULL),一般会导致程序崩溃或产生段错误。这是因为当传递NULL时,strlen函数会试图访问指向NULL的内存地址,而这是非法的操作。
-
示例代码:
#include <stdio.h> #include <string.h> int main() { char str1[] = "Hello World"; const char* str2 = "Hello, strlen!"; printf("Length of str1: %zu\n", strlen(str1)); printf("Length of str2: %zu\n", strlen(str2)); return 0; }
输出结果:
Length of str1: 11
Length of str2: 14
一维数组
题目实例:
#include<stdio.h>
int main() {
//一维数组
int a[] = {4,3,2,1,0};
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));
return 0;
}
运行结果(这个结果是基于vs2022 x64环境下运行的结果,不同编译环境最终的结果可能不同,这里仅做参考):
20
8
4
8
4
8
20
8
8
8
题目解释:
int a[] = {4,3,2,1,0}; //一维数组,有5个int类型(4个字节)的元素
printf("%d\n", sizeof(a)); // 20 这里数组名单独放在sizeof内部,这里的数组名a表示整个数组,计算的是整个数组的大小,共4*5=20字节
printf("%d\n", sizeof(a + 0));// 4/8 a并非单独放在sizeof内部,也没有&,这里的a是数组首元素的地址,a+0还是数组首元素的地址。是地址就是4个字节(32位系统)或8个字节(64位系统)。
printf("%d\n", sizeof(*a)); // 4 这里的a不是前面提到的两种特殊情况 ,*a==*(a+0)==a[0], *a就是数组的首元素,大小就是4字节。
printf("%d\n", sizeof(a + 1));// 4/8 a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+1就是第二个元素的地址,这里a+1=&a[1]。是地址就是4或者8字节。
printf("%d\n", sizeof(a[1]));// 4 a[1]表示数组第二个元素,大小是4字节。a[1]==*(a+1)。
printf("%d\n", sizeof(&a)); // 4/8 &a表示整个数组的地址,并非是数组首元素的地址,不过由于其还是地址,所以还是4或者8个字节。
printf("%d\n", sizeof(*&a));// 20 可以理解成*和&可以相互抵消,sizeof(*&a)可以看成sizeof(a)。
//如果从本质理解,&a是一个数组指针类型,*&a就是对一个数组指针解引用访问一个数组的大小。
//a是int*类型 int *str=a;
// &a是int(*)[5]类型 int (*str)[5]=&a;
printf("%d\n", sizeof(&a + 1));// 4/8 &a是数组的地址,&a+1还是地址,表示数组a的下一个地址,是地址就是4/8个字节。
printf("%d\n", sizeof(&a[0]));// 4/8 &a[0]是首元素的地址, 计算的还是地址的大小。
printf("%d\n", sizeof(&a[0] + 1)); //4/8 &a[0] + 1是第二个元素的地址,计算地址的大小就是4或者8个字节。
字符数组
题目实例1:
#include<stdio.h>
#include<string.h>
int main() {
char arr[] = { 'a','b','c','d','e' };
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));
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));
return 0;
}
题目解释1:
char arr[] = { 'a','b','c','d','e' }; //字符数组,每个元素char类型(1字节),这里给定的字符数组arr并不包含空字符。
printf("%d\n", sizeof(arr));// 5 数组名arr单独放在sizeof内部,计算整个数组的大小。
printf("%d\n", sizeof(arr + 0)); // 4/8 数组名arr没有单独放在sizeof内部,且没有&,表示数组首元素的地址,arr+0还是首元素的地址,地址大小为4或8个字节。
printf("%d\n", sizeof(*arr));// 1 *arr==*(arr+0)==arr[0],对数组首元素的地址解引用表示数组首元素,大小为1字节。
printf("%d\n", sizeof(arr[1]));// 1 表示数组第二个元素,大小为1个字节。
printf("%d\n", sizeof(&arr));// 4/8 表示数组的地址,地址大小为4或者8个字节。
printf("%d\n", sizeof(&arr + 1));// 4/8 表示跳过该数组的地址,是地址就是4或者8个字节。
printf("%d\n", sizeof(&arr[0] + 1));// 4/8 表示第二个元素的地址,是地址就是4或者8个字节。
printf("%d\n", strlen(arr));//随机值,strlen函数是通过寻找数组中的空字符('\0')来确定字符串的长度的。然而,给定的字符数组arr并不包含空字符,strlen(arr)可能会继续向后遍历内存直到遇到一个空字符。注意与sizeof(arr)不同的是,这里的arr是首元素地址。
printf("%d\n", strlen(arr + 0)); // 随机值,arr+0还是首元素地址。
printf("%d\n", strlen(*arr));// 错误,arr是数组首元素地址,*arr就是数组首元素 'a', strlen认为传参进去的'a'-97就是地址,若97作为地址,直接进行访问,就是非法访问。
printf("%d\n", strlen(arr[1])); //错误,表示数组第二个元素,与上题错误原因一样。
printf("%d\n", strlen(&arr)); //随机值 &arr表示数组的地址,char (*)[5]
printf("%d\n", strlen(&arr + 1));// 随机值 表示该数组下一个的地址
printf("%d\n", strlen(&arr[0] + 1)); // 随机值 表示数组第二个元素的地址
题目实例2:
#include<stdio.h>
#include<string.h>
int main() {
char arr[] = "abcde";
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));
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));
return 0;
}
题目解释2:
char arr[] = "abcde"; 字符串末尾有'\0'
printf("%d\n", sizeof(arr)); // 6 整个数组的大小
printf("%d\n", sizeof(arr + 0)); //其余的与题1一致
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));
printf("%d\n", strlen(arr)); // 5 首元素地址,从首元素开始往后统计
printf("%d\n", strlen(arr + 0)); // 5
printf("%d\n", strlen(*arr)); //错误
printf("%d\n", strlen(arr[1])); //错误
printf("%d\n", strlen(&arr)); // 5 数组的地址
printf("%d\n", strlen(&arr + 1)); // 随机值 表示该数组下一个的地址,从'\0'之后开始,不知道下一次遇到空字符在哪。
printf("%d\n", strlen(&arr[0] + 1)); //4 表示第二个元素的地址,从该地址往后直到'\0',有4个。
题目实例3:
#include<stdio.h>
#include<string.h>
int main() {
char* p = "abcde";
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));
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));
return 0;
}
题目解释3:
char* p = "abcde";
printf("%d\n", sizeof(p)); // 4/8 计算指针变量的大小,为4或者8个字节
//指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节。比如char*,int *,double*大小都一样。
//指针变量是用来存放地址的,地址存放需要多大空间,指针变量的大小就是几个字节。32位环境下,地址是32个二进制位,需要4个字节,所以指针变量的大小就是4个字节;同理,64位环境下,地址是64个二进制位,需要8个字节,所以指针变量的大小就是8个字节。
printf("%d\n", sizeof(p + 1)); // 4/8 p+1还是地址
printf("%d\n", sizeof(*p)); // 1 *p=='a'
printf("%d\n", sizeof(p[0])); // 1 p[0]==*(p+0)==*p
printf("%d\n", sizeof(&p)); // 4/8
printf("%d\n", sizeof(&p + 1)); // 4/8
printf("%d\n", sizeof(&p[0] + 1)); // 4/8