**
关于sizeof 和strlen的一些易混淆的题目**
sizeof()是运算符,大部分编译程序在编译时已经将它计算过了,sizeof()是计算数据(包括数组、变量、类型、结构体等)所占内存空间,用字节数表示(当然用在字符数组计算"\0"的大小),在32和64位操作系统中计算出的值有可能不同。
而strlen是函数,该函数就是求参数 s指向字符串的长度。C语言规定,字符串必须以’\0’字符作为最后一个字符,表示字符串的结束字符,称为:字符串结束符。字符串结束符’\0’的ASCII是0数值,strlen()遇到字符串结束符’\0’之后,结束字符串长度的计算。
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&aa + 1);
int* ptr2 = (int*)(*(aa + 1));
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
int a[5][5];
int(*p)[4];
// 这个赋值操作本身其实就是不科学的. (强行把两个不同类型的指针赋值, 就是有很大风险的)
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d\n", p[0]);
printf("%d\n", a[0][0]);
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
printf("%p\n", p + 0x1); // 0x100020 ?? 0x100014!!
printf("%p\n", (unsigned long)p + 0x1); // 0x100001
printf("%p\n", (unsigned int*)p + 0x1); // 0x100004
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1)); // 2 5
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); // 48
printf("%d\n", sizeof(a[0][0])); // 4 a[0][0] 结果是 int
printf("%d\n", sizeof(a[0])); // 16 a => int[3][4] a[0] => int[4]
printf("%d\n", sizeof(a[0] + 1)); // a => int[3][4] a[0] => int[4] a[0] + 1 => int[4] + 1 => int*
printf("%d\n", sizeof(*(a[0] + 1))); // a[0] => int[4] + 1 => int* * => int
printf("%d\n", sizeof(a + 1)); // 4 a => int[3][4] + 1 就是把数组转成指向首元素的指针. int(*)[4]
printf("%d\n", sizeof(*(a + 1))); // 16 a + 1 => int(*)[4] 再 * 结果就是 int[4]
printf("%d\n", sizeof(&a[0] + 1)); // a[0] => int[4] 再 & 得到了 int(*)[4] 再 + 1 还是 int(*)[4]
printf("%d\n", sizeof(*(&a[0] + 1))); // a[0] => int[4] 再 & 还是 int(*)[4] 再+1 还是 int(*)[4] 再来 * 得到 int[4]
printf("%d\n", sizeof(*a)); // a 是 int[3][4] *a 会导致 a 先隐式转成指向首元素的指针 int(*)[4] , 再 * 得到 int[4]
printf("%d\n", sizeof(a[3])); // 16 sizeof 是编译期求值, 于是这个 a[3] 不会在运行时执行
char* p = "abcdef";
printf("%d\n", sizeof(p)); // 4 p 是 char* 类型
printf("%d\n", sizeof(p + 1)); // 4 p 是 char* 类型, p + 1 还是 char*
printf("%d\n", sizeof(*p)); // 1 p 是 char* *p 就是 char 类型
printf("%d\n", sizeof(p[0])); // 1 p[0] => *(p + 0) => *p
printf("%d\n", sizeof(&p)); // 4 p 是 char* &p => char**
printf("%d\n", sizeof(&p + 1)); // 4 同上
printf("%d\n", sizeof(&p[0] + 1)); // 4 p[0] 得到 char, & 得到 char* 再 + 1 还是 char*
printf("%d\n", strlen(p)); // 6 从 a 开始, 找 6 个字符就找到了 \0
printf("%d\n", strlen(p + 1)); // 5 从 b 开始, 找 5 个字符就找到了 \0
printf("%d\n", strlen(*p)); // 未定义行为. *p 得到的是 char 'a'. strlen 需要的是 char* 类型. 原则上讲是不应该编译通过的. 但是 C 语言对于类型检查不严格. strlen 就会尝试从 'a' ascii 这个地址, 开始去找 \0. 由于这个地址是非法内存.
printf("%d\n", strlen(p[0])); // 未定义行为. 同上.
printf("%d\n", strlen(&p)); // 未定义行为. &p 得到的是 char**, 本身内部存的地址, 是 p 变量本身的地址. 这个地址和 \0 无关, 尝试去找, 能不能找到这个 \0 , 这是完全不好说的.
printf("%d\n", strlen(&p + 1)); // 未定义行为. 原因同上
printf("%d\n", strlen(&p[0] + 1)); // 5 p[0] 得到了 char 'a'. & 得到了 char*, 指向 'a', 再 + 1 得到 指向 'b' 的指针.
" " 本身已经是字符串, 本身里面已经有 \0 了.
得到的 arr 应该是一个 char[7]
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); // 7
printf("%d\n", sizeof(arr + 0)); // 4 arr 是 char[7], +0 就相当于隐式转成了 char*
printf("%d\n", sizeof(*arr)); // 1 *arr 得到的是 'a' , char
printf("%d\n", sizeof(arr[1])); // 1 arr[1] 得到的是 'b', char
printf("%d\n", sizeof(&arr)); // 4 arr 是 char[7], &arr 是 char(*)[7] , 这也是指针类型.
printf("%d\n", sizeof(&arr + 1)); // 4 同上.
printf("%d\n", sizeof(&arr[0] + 1)); // 4 arr[0] 得到 char, 再 & 得到 char* , 再 +1 还是 char*
printf("%d\n", strlen(arr)); // 6 计算 strlen 的时候, 不算 \0 本身.
printf("%d\n", strlen(arr + 0)); // 6 同上.
printf("%d\n", strlen(*arr)); // 未定义行为. *arr 得到的是 'a'. char, 不是 const char* , 尝试把 'a' 的 ascii 码当做内存地址了, 并尝试访问这个地址的内存.
printf("%d\n", strlen(arr[1])); // 未定义行为. arr[1] 得到的是 'b'. char, 不是 const char*. 尝试把 'b' 的 ascii 值当做一个内存地址了. 并尝试访问这个地址的内存.
printf("%d\n", strlen(&arr)); // 6 &arr 得到的是数组指针, char(*)[7], 和 形参要求的 const char* 不同的. 虽然类型不匹配, 但是结果碰巧是对.
printf("%d\n", strlen(&arr + 1)); // 未定义行为了.
printf("%d\n", strlen(&arr[0] + 1)); // 5 arr[0] 得到 字符 'a', 再 & 得到了 指向 'a' 的指针. 再 +1 得到了 指向 'b' 的指针. 从这个位置开始, 往后找 \0,
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); // 6 没有 \0
printf("%d\n", sizeof(arr + 0)); // 4 arr 本来是 char[6], +0 就触发了隐式转换. 就变成了 char*
printf("%d\n", sizeof(*arr)); // 1 arr 是 char[6], 本来是个数组, 但是在 * 运算中也隐式转成了 char*, 针对 char* 解引用, 结果就是一个 char
printf("%d\n", sizeof(arr[1])); // 1 arr 是 char[6] 进行 [1] 操作, 就得到了一个 char 'b'
printf("%d\n", sizeof(&arr)); // 4 arr 是 char[6], &arr 得到了一个 char(*)[6]
printf("%d\n", sizeof(&arr + 1)); // 4 arr 是 char[6], &arr 得到了 char(*)[6], 再 +1 结果仍然是 char(*)[6]
printf("%d\n", sizeof(&arr[0] + 1)); // 4 arr[0] 得到了 char, 取地址, 得到了 char*, 再 + 1 仍然是 char*
代码中永远不能依赖未定义行为
printf("%d\n", strlen(arr)); // 未定义行为. arr 没有 \0. 进行 strlen 就会出现数组下标越界.
printf("%d\n", strlen(arr + 0)); // 未定义行为. 和上面一样.
printf("%d\n", strlen(*arr)); // 未定义行为 arr 是 char[6], *arr 得到的是 char, 'a' 这个字符. 把 'a' 的 ascii 当做内存地址了. 这是一个非法地址, 尝试读取数据, 就会出现未定义行为.
printf("%d\n", strlen(arr[1])); // 同上. 这回访问的是 'b' 的 ascii 值对应的内存.
printf("%d\n", strlen(&arr)); // 未定义行为 &arr 得到的是数组指针. char(*)[6] , 虽然和形参类型不同, 但是大家都是指针, 里面存的地址相同. 这个时候也就相当于是从 'a' 地址开始往后找 \0
printf("%d\n", strlen(&arr + 1)); // 未定义行为 &arr 得到了一个 char(*)[6]. 再 + 1 还是 char(*)[6] . 指针 +1, 地址要跳过一个元素. 这一跳就把整个数组的内容都跳过了.
printf("%d\n", strlen(&arr[0] + 1));
int a[] = { 1, 2, 3, 4 };
printf("%d\n", sizeof(a)); // 16
printf("%d\n", sizeof(a + 0)); // 4 当前由于数组名参与了 + 0 运算, 隐式转成了指针.
printf("%d\n", sizeof(*a)); // 4 当前 a 参与了 * 运算, 也隐式转成了指针 int* . 针对 int* 解引用, 结果是一个 int
printf("%d\n", sizeof(a + 1)); // 4 和第二个代码一样, 数组名参与了 + 1 运算, 隐式转成了 int*
printf("%d\n", sizeof(a[1])); // 4 a 是数组, [1] 取到下标为 1 的元素, 是一个 int
printf("%d\n", sizeof(&a)); // 4 &a 得到一个数组指针. int(*)[4]
printf("%d\n", sizeof(*&a)); // 16 &a 得到一个数组指针. int(*)[4], 针对这个数组指针 * 操作, 结果就得到了一个 int[4]
printf("%d\n", sizeof(&*a)); // 4 a 是 int[4], *a, 其实就会先把 a 转成 int*, 然后 * 得到了一个 int. 然后再 & 就又得到了一个 int*
printf("%d\n", sizeof(&a + 1)); // 4 a 是 int[4], &a 是 int(*)[4], 再 + 1 仍然是 int(*)[4]
printf("%d\n", sizeof(&a[0])); // 4 a 是 int[4], a[0] 得到 int, 再 & 得到 int*
printf("%d\n", sizeof(&a[0] + 1)); // 4 a 是 int[4], a[0] 得到 int, 再 & 得到 int* , 再 + 1 结果仍然是 int*