文章目录
- 思维导图
- 数组和指针
- 库函数的模拟实现
- 判断大小端
最近知识学的差不多了,因此开始复习,本篇开始的是对于C语言的复习
思维导图
下面就依据下图,进行内容的整理
数组和指针
这个模块算是C语言中比较大的一个模块了,具体概念就不多说了,直接用例题
数组
#include <stdio.h>
int main()
{
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); // 16
printf("%d\n", sizeof(a + 0)); // 4/8
printf("%d\n", sizeof(*a)); // 4
printf("%d\n", sizeof(a + 1)); // 4/8
printf("%d\n", sizeof(a[1])); // 4
printf("%d\n", sizeof(&a)); // 4/8
printf("%d\n", sizeof(*&a)); // 16
printf("%d\n", sizeof(&a + 1));// 4/8
printf("%d\n", sizeof(&a[0])); // 4/8
printf("%d\n", sizeof(&a[0] + 1));// 4/8
//字符数组
char arr[] = { 'a','b','c','d','e','f', '\0'};
printf("%d\n", sizeof(arr)); // 6
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));// 4/8
printf("%d\n", strlen(arr)); // 6
printf("%d\n", strlen(arr + 0));// 6
//printf("%d\n", strlen((const char*) * arr)); // ?
//printf("%d\n", strlen((const char*)arr[1])); // ?
printf("%d\n", strlen((const char*)&arr)); // 6
printf("%d\n", strlen((const char*)(&arr + 1))); // 未定义
printf("%d\n", strlen((const char*)(&arr[0] + 1))); // 5
return 0;
}
指针
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1); // 指向最后一个元素的下一个位置
printf("%d,%d", *(a + 1), *(ptr - 1)); // a[1] 和 a[4]
return 0;
}
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
接下来分析 main()
函数中的 printf
表达式:
-
printf("%p\n", p + 0x1);
在这里,
p
是一个指向struct Test
的指针,对其进行加0x1
操作意味着指针向前偏移0x1
个struct Test
类型的大小,即偏移 20 字节。因此,p + 0x1
的值为0x100014
。%p
格式说明符用于打印指针的值,所以这行代码输出0x100014
。 -
printf("%p\n", (unsigned long)p + 0x1);
首先,将指针
p
强制转换为unsigned long
类型。虽然p
的原始类型是一个指针,但转换为unsigned long
后,它被视为一个数值。然后对这个数值加上0x1
,即0x100000 + 0x1 = 0x100001
。接着,将结果作为指针传递给printf
函数,使用%p
格式说明符打印。理论上,这行代码的行为是未定义的,因为0x100001
不是一个有效的指针值。实际运行时可能会导致程序崩溃或其他不可预期的结果。 -
printf("%p\n", (unsigned int*)p + 0x1);
这里,将指针
p
强制转换为unsigned int*
类型,即指向无符号整型的指针。由于struct Test
大小为 20 字节,而通常情况下unsigned int
类型为 4 字节(具体字节数可能因编译器和平台而异),所以p
指向的对象大小与unsigned int*
类型指针所期望的对象大小不匹配。接着,对转换后的指针加上0x1
,即偏移 4 字节。最后,将结果作为指针传递给printf
函数,使用%p
格式说明符打印。这行代码同样存在未定义行为,因为指针类型转换后与原始对象大小不匹配,后续操作可能导致程序崩溃或其他不可预期的结果。
综上所述:
- 第一个
printf
表达式输出0x100014
,这是正确地对struct Test
类型指针进行偏移后的结果。 - 第二个和第三个
printf
表达式涉及不恰当的指针类型转换和指针运算,其行为是未定义的,可能会导致程序崩溃或其他不可预期的结果。在实际编程中应避免这类操作。
int main()
{
int a[4] = { 1, 2, 3, 4 };
// 1000 0000 0000 0000 0000 0000 0000 0000
// 0100 0000 0000 0000 0000 0000 0000 0000
// 00 00 00 02
// 0000 0000 0000 0000 0000 0000 0000 0100
int* ptr1 = (int*)(&a + 1); // 最后一个元素的下一个位置
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2); // 最后一个元素
return 0;
}
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]); // p + 0 实际存储的是1 3 5 0 0 ...
return 0;
}
#include <stdio.h>
int main()
{
// 1, 2, 3, 4, 5
// 6, 7, 8, 9, 10
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&aa + 1); // 跳过了整个二维数组
int* ptr2 = (int*)(*(aa + 1)); // aa[1]
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); // 10 5
return 0;
}
#include <stdio.h>
int main()
{
const char* a[] = { "work","at","alibaba" };
const char** pa = a; // w
pa++; // at
printf("%s\n", *pa);
return 0;
}
库函数的模拟实现
void* mymemcpy(void* destination, const void* source, size_t num)
{
void* res = destination;
assert(destination);
assert(source);
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
return res;
}
void* mymemmove(void* destination, const void* source, size_t num)
{
void* res = destination;
assert(destination);
assert(source);
if (destination <= source || (char*)destination >= ((char*)source) + num)
{
destination = (char*)destination + num - 1;
source = (char*)source + num - 1;
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination - 1;
source = (char*)source - 1;
}
}
else
{
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
}
return res;
}
判断大小端
union MyUnion
{
int a;
char c[4];
};
int main()
{
MyUnion un;
un.a = 0x123456;
if (un.c[0] == 0x12)
printf("大端\n");
else
printf("小端\n");
return 0;
}
int main()
{
int a = 0x123456;
char* pa = (char*)&a;
if (*pa == 0x12)
printf("大端\n");
else
printf("小端\n");
return 0;
}