关于sizeof 和strlen的一些易混淆的题目**

**

关于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* 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值