习题1
cs
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main() {
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//16
//sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
printf("%d\n", sizeof(a + 0));//4
//a不是单独放在sizeof内部,也没有取地址,所以a就是首元素的地址,a+0还是首元素的地址
//是地址大小就是4个字节
printf("%d\n",sizeof(*a));//4
//*a中的a是数组首元素的地址,*a就是对首元素的地址的解引用,找到的就是首元素
//首元素的大小就是4个字节
printf("%d\n", sizeof(a + 1));//4
这里的a是数组首元素的地址
//a+1是第二个元素的地址
printf("%d\n", sizeof(a[1]));//4
//计算的是第二个元素的大小
printf("%d\n", sizeof(&a));//4或者8
//&a取出的数组的地址,数组的地址,也就是个地址
printf("%d\n", sizeof(*&a));// 16
//&a---->int (*)[4]
//&a拿到的是数组名的地址,类型是int(*)[4], 是一种数组指针
//数组指针解引用找到的是数组 *&a---->a
printf("%d\n", sizeof(&a + 1));// 4或者8
//&a取出的是数组的地址
// &a-->int(*)4
//&a+1是从数组a的地址向后跳过了一个(4个整型元素的)数组的大小
//a+1还是地址,是地址就是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个字节]
return 0;
}
总结
- 对于数组名
a
,当它单独放在sizeof
内部时,表示整个数组。 - 对于数组名
a
,当它与+
运算符结合使用时,表示数组首元素的地址。 - 对于地址操作(如
&a
、a + 0
、&a[0]
),地址的大小在 32 位系统上是 4 字节,在 64 位系统上是 8 字节。 - 对于解引用操作(如
*a
、*&a
),得到的是数组元素或数组本身,大小取决于数据类型。
习题2
cs
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main() {
char arr[] = { 'a','b','c','d','e','f' }; // 定义一个字符数组
// sizeof(arr) 计算整个数组的大小,单位是字节
// 数组中有6个字符,每个字符占1个字节,所以结果是6
printf("%d\n", sizeof(arr)); // 输出: 6
// arr + 0 表示数组首元素的地址
// sizeof(arr + 0) 计算的是地址的大小,在32位系统上是4字节,在64位系统上是8字节
printf("%d\n", sizeof(arr + 0)); // 输出: 4 或 8
// *arr 表示数组的首元素 'a'
// sizeof(*arr) 计算的是字符的大小,即1字节
printf("%d\n", sizeof(*arr)); // 输出: 1
// arr[1] 表示数组的第二个元素 'b'
// sizeof(arr[1]) 计算的是字符的大小,即1字节
printf("%d\n", sizeof(arr[1])); // 输出: 1
// &arr 表示整个数组的地址
// sizeof(&arr) 计算的是地址的大小,在32位系统上是4字节,在64位系统上是8字节
printf("%d\n", sizeof(&arr)); // 输出: 4 或 8
// &arr + 1 表示从数组地址向后跳过一个数组大小的地址
// sizeof(&arr + 1) 计算的是地址的大小,在32位系统上是4字节,在64位系统上是8字节
printf("%d\n", sizeof(&arr + 1)); // 输出: 4 或 8
// &arr[0] 表示数组第一个元素的地址
// &arr[0] + 1 表示数组第二个元素的地址
// sizeof(&arr[0] + 1) 计算的是地址的大小,在32位系统上是4字节,在64位系统上是8字节
printf("%d\n", sizeof(&arr[0] + 1)); // 输出: 4 或 8
return 0;
}
习题3
cs
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main() {
char arr[] = "abcdef"; // 定义一个包含字符串 "abcdef" 的字符数组
// 在 C 语言中,字符串常量自动添加一个结束符 '\0',所以 arr 实际上是 7 个字符:'a', 'b', 'c', 'd', 'e', 'f', '\0'
// sizeof(arr) 计算整个数组的大小
// 数组中有 7 个字符(包括结束符 '\0'),每个字符占 1 个字节,所以结果是 7
printf("%d\n", sizeof(arr)); // 输出: 7
// arr + 0 表示数组首元素的地址
// sizeof(arr + 0) 计算的是这个地址的大小
// 在 32 位系统上是 4 字节,在 64 位系统上是 8 字节
printf("%d\n", sizeof(arr + 0)); // 输出: 4 或 8
// *arr 表示数组的首元素 'a'
// sizeof(*arr) 计算的是字符的大小,即 1 字节
printf("%d\n", sizeof(*arr)); // 输出: 1
// arr[1] 表示数组的第二个元素 'b'
// sizeof(arr[1]) 计算的是字符的大小,即 1 字节
printf("%d\n", sizeof(arr[1])); // 输出: 1
// &arr 表示整个数组的地址
// sizeof(&arr) 计算的是地址的大小,在 32 位系统上是 4 字节,在 64 位系统上是 8 字节
printf("%d\n", sizeof(&arr)); // 输出: 4 或 8
// &arr + 1 表示从数组地址向后跳过一个数组的大小
// sizeof(&arr + 1) 计算的是地址的大小,在 32 位系统上是 4 字节,在 64 位系统上是 8 字节
printf("%d\n", sizeof(&arr + 1)); // 输出: 4 或 8
// &arr[0] 表示数组第一个元素的地址
// &arr[0] + 1 表示数组第二个元素的地址
// sizeof(&arr[0] + 1) 计算的是地址的大小,在 32 位系统上是 4 字节,在 64 位系统上是 8 字节
printf("%d\n", sizeof(&arr[0] + 1)); // 输出: 4 或 8
return 0;
}
习题4
cs
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h> // 需要包含 string.h 头文件以使用 strlen 函数
//strlen是求字符串长度的,关注的是字符串中的8,计算的是\之前出现的字符的个数
//strlen是库函数,只针对字符串
//sizeof只关注占用内存空间的大小,不在乎内存中放的是什么
//sizeof是操作符
int main() {
char arr[] = "abcdef";
// 定义一个包含字符串 "abcdef" 的字符数组
// 在 C 语言中,字符串常量自动添加一个结束符 '\0',
//所以 arr 实际上是 7 个字符:'a', 'b', 'c', 'd', 'e', 'f', '\0'
// strlen(arr) 计算字符串的长度,不包括结束符 '\0'
// 字符串 "abcdef" 有 6 个字符,所以结果是 6
printf("%d\n", strlen(arr)); // 输出: 6
// arr + 0 表示数组首元素的地址,即字符串的起始地址
// strlen(arr + 0) 计算字符串的长度,结果是 6
printf("%d\n", strlen(arr + 0)); // 输出: 6
// *arr 表示数组的首元素 'a',字符 'a' 的 ASCII 码值是 97
// strlen(*arr) 试图计算以 97 为地址的字符串长度,这会导致未定义行为(非法访问内存)
// 一般情况下,程序会崩溃或产生不可预期的结果
// printf("%d\n", strlen(*arr)); // 未定义行为,程序可能会崩溃
// arr[1] 表示数组的第二个元素 'b',字符 'b' 的 ASCII 码值是 98
// strlen(arr[1]) 试图计算以 98 为地址的字符串长度,这会导致未定义行为(非法访问内存)
// 一般情况下,程序会崩溃或产生不可预期的结果
// printf("%d\n", strlen(arr[1])); // 未定义行为,程序可能会崩溃
// &arr 表示整个数组的地址,但它仍然是字符串的起始地址
// strlen(&arr) 计算字符串的长度,结果是 6
printf("%d\n", strlen(&arr)); // 输出: 6
// &arr + 1 表示从数组地址向后跳过一个数组的大小,指向数组结束后的位置
// strlen(&arr + 1) 计算从这个位置开始的字符串长度,这会导致未定义行为(非法访问内存)
// 一般情况下,程序会崩溃或产生不可预期的结果
// printf("%d\n", strlen(&arr + 1)); // 未定义行为,程序可能会崩溃
// &arr[0] 表示数组第一个元素的地址,&arr[0] + 1 表示数组第二个元素的地址
// strlen(&arr[0] + 1) 计算从第二个元素开始的字符串长度,结果是 5
printf("%d\n", strlen(&arr[0] + 1)); // 输出: 5
return 0;
}
习题5
cs
#include <stdio.h>
#include <string.h> // 需要包含 string.h 头文件以使用 strlen 函数
int main() {
char* p = "abcdef"; // 定义一个指向字符串常量 "abcdef" 的指针
// sizeof(p) 计算指针 p 的大小
// 在 32 位系统下是 4 字节,在 64 位系统下是 8 字节
printf("%d\n", sizeof(p)); // 输出: 4 或 8
// sizeof(p + 1) 计算的是指针的大小(p + 1 依然是指针)
printf("%d\n", sizeof(p + 1)); // 输出: 4 或 8
// sizeof(*p) 计算的是 char 类型的大小,即 1 字节
printf("%d\n", sizeof(*p)); // 输出: 1
// sizeof(p[0]) 也是计算第一元素 'a' 的大小,结果也是 1 字节
printf("%d\n", sizeof(p[0])); // 输出: 1
// sizeof(&p) 计算的是指针 p 的地址(指向 char* 的指针),大小在 32 位系统下是 4 字节,在 64 位系统下是 8 字节
printf("%d\n", sizeof(&p)); // 输出: 4 或 8
// sizeof(&p + 1) 计算的是第二级指针的大小(sizeof(char**)),结果指针的大小,4 或 8 字节
printf("%d\n", sizeof(&p + 1)); // 输出: 4 或 8
// sizeof(&p[0] + 1) 计算的是偏移后的指针大小,指向下一个字符,输出 result 是 4 或 8。
printf("%d\n", sizeof(&p[0] + 1)); // 输出: 4 或 8
// strlen(p) 计算字符串的长度,不包括结束符 '\0',结果是 6
printf("%d\n", strlen(p)); // 输出: 6
// strlen(p + 1) 计算从 'b' 开始到末尾的长度,即 "bcdef",结果是 5
printf("%d\n", strlen(p + 1)); // 输出: 5
// strlen(*p) 将会导致未定义行为,因为 *p 是字符 'a',将其作为指针处理是不正确的
// printf("%d\n", strlen(*p)); // 可能导致崩溃或未定义行为
// strlen(p[0]) 同样是不正确的,p[0] 是 'a',作为参数传入也是不正确
// printf("%d\n", strlen(p[0])); // 可能导致崩溃或未定义行为
// strlen(&p) 计算的是 p 的地址,以指针形式传入,结果是不正确的(不代表字符串)。
// printf("%d\n", strlen(&p)); //随机值
// strlen(&p + 1) 计算的是结束后的位置,结果也是未定义的。
// printf("%d\n", strlen(&p + 1)); //随机值
// strlen(&p[0] + 1) 计算的是从 'b' 开始的字符串长度,结果是 5
printf("%d\n", strlen(&p[0] + 1)); // 输出: 5
return 0;
}
习题6
cs
#include <stdio.h>
int main() {
int a[3][4] = { 0 }; // 定义一个 3 行 4 列的二维数组
// sizeof(a) 计算整个二维数组的大小,即 3 行 4 列的 int 数组
// 每个 int 占 4 字节,总大小为 3 * 4 * 4 = 48 字节
printf("%d\n", sizeof(a)); // 输出: 48
// sizeof(a[0][0]) 计算二维数组第一个元素的大小,即一个 int 的大小
printf("%d\n", sizeof(a[0][0])); // 输出: 4
// sizeof(a[0]) 计算第一行的数组大小,即 4 个 int 的大小
// 总大小为 4 * 4 = 16 字节
printf("%d\n", sizeof(a[0])); // 输出: 16
// a[0] 是第一行的数组名,a[0] + 1 是数组首元素的地址加 1,即指向 a[0][1]
// sizeof(a[0] + 1) 计算的是指针的大小,在 32 位系统下是 4 字节,在 64 位系统下是 8 字节
printf("%d\n", sizeof(a[0] + 1)); // 输出: 4 或 8
// (*a[0] + 1) 是未定义行为,a[0] 是一个数组名,不是指针
// printf("%d\n", sizeof((*a[0] + 1))); // 未定义行为
// a 是二维数组的数组名,a + 1 指向第二行的起始地址
// sizeof(a + 1) 计算的是指针的大小,在 32 位系统下是 4 字节,在 64 位系统下是 8 字节
printf("%d\n", sizeof(a + 1)); // 输出: 4 或 8
// *(a + 1) 是第二行的数组名,等价于 a[1]
// sizeof(*(a + 1)) 计算第二行的数组大小,即 4 个 int 的大小,总大小为 4 * 4 = 16 字节
printf("%d\n", sizeof(*(a + 1))); // 输出: 16
// &a[0] 是第一行的地址,&a[0] + 1 指向第二行的起始地址
// sizeof(&a[0] + 1) 计算的是指针的大小,在 32 位系统下是 4 字节,在 64 位系统下是 8 字节
printf("%d\n", sizeof(&a[0] + 1)); // 输出: 4 或 8
// *(&a[0] + 1) 是第二行的数组名,等价于 a[1]
// sizeof(*(&a[0] + 1)) 计算第二行的数组大小,总大小为 4 * 4 = 16 字节
printf("%d\n", sizeof(*(&a[0] + 1))); // 输出: 16
// *a 是第一行的数组名,等价于 a[0]
// sizeof(*a) 计算第一行的数组大小,总大小为 4 * 4 = 16 字节
printf("%d\n", sizeof(*a)); // 输出: 16
// a[3] 越界访问,但 sizeof 是在编译时计算的,所以不会导致运行时错误
// 编译器会认为 a[3] 是一个数组名,计算 4 个 int 的大小,总大小为 4 * 4 = 16 字节
printf("%d\n", sizeof(a[3])); // 输出: 16
return 0;
}
习题7
cs
#include <stdio.h>
int main() {
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1); // &a 是整个数组的指针,&a + 1 指向数组末尾后的位置
// *(a + 1) 访问的是数组 a 的第二个元素,即 a[1]
// *(ptr + 1) 访问的是数组 a 末尾后的内存位置,这是未定义行为
printf("%d, %d\n", *(a + 1), *(ptr - 1)); //2 5
// 这里我们改为 *(ptr - 1) 来访问数组 a 的最后一个元素
return 0;
}
&a
是整个数组a
的指针,类型是int (*)[5]
,即指向包含 5 个int
的数组的指针。&a + 1
指向数组a
末尾后的位置,即指向下一个int[5]
数组的位置。(int*)(&a + 1)
将&a + 1
强制转换为int*
类型,指向数组a
末尾后的位置。*(a + 1)
访问的是数组a
的第二个元素,即a[1]
,值为2
。*(ptr - 1)
访问的是数组a
的最后一个元素,即a[4]
,值为5
。
习题8
cs
#include <stdio.h>
// 定义结构体 test
struct test {
int num; // 4 字节
char* name; // 指针,通常为 4 或 8 字节(32位系统为4字节,64位系统为8字节)
short date; // 2 字节
char cha[2]; // 2 字节
short sba[4]; // 8 字节(4 个 short,每个 2 字节)
} *p;
//假设p的值为0x100000.如下表达式的值分别为多少
//已知结构体test类型的变量大小是20个字节
int main() {
p = (struct test*)0x100000; // 假设 p 的值为 0x100000
// (1) p + 0x1
// p 是一个指向 test 结构体的指针,p + 0x1 表示 p 向前移动 1 个 test 结构体的大小
// 结构体 test 的大小是 20 字节,所以结果是将指针 p 增加 20 字节,即 0x100014
printf("%p\n", p + 0x1); // 输出: 0x100014
// (2) (unsigned long)p + 0x1
// p 被强制转换为 unsigned long 类型,这时它不再是一个指针,而是一个整数
// 因此,(unsigned long)p + 0x1 只是将 p 的整数值 0x100000 加上 0x1,结果是 0x100001
printf("%p\n", (unsigned long)p + 0x1); // 输出: 0x100001
// (3) (unsigned int*)p + 0x1
// p 被强制转换为 unsigned int* 类型,这时它是一个指向 unsigned int 的指针
// p + 0x1 表示 p 向前移动 1 个 unsigned int 的大小
// 在 x86 架构下,unsigned int 的大小是 4 字节,所以结果是将指针 p 增加 4 字节,即 0x100004
printf("%p\n", (unsigned int*)p + 0x1); // 输出: 0x100004
return 0;
}
习题8
cs
#include <stdio.h>
int main() {
// 定义一个二维数组 a,大小为 3x2,并初始化
// 注意:这里使用了逗号表达式 (0,1), (2,3), (4,5)
// 逗号表达式的值是最后一个表达式的值,因此:
// (0,1) 的结果是 1
// (2,3) 的结果是 3
// (4,5) 的结果是 5
// 所以数组 a 实际上被初始化为:
// a[0][0] = 1, a[0][1] = 3
// a[1][0] = 5, a[1][1] = 0(未初始化,默认为 0)
// a[2][0] = 0, a[2][1] = 0(未初始化,默认为 0)
int a[3][2] = { (0,1),(2,3),(4,5) };
// 定义一个指向 int 的指针 p
int* p;
// 将 p 指向数组 a 的第一个元素 a[0],即 p 指向 a[0][0]
p = a[0];
// 输出 p[0],即 a[0][0],其值为 1
printf("%d", p[0]);
return 0;
}