文章目录
- 思维导图
- 数组和指针
- 库函数的模拟实现
- 判断大小端
最近知识学的差不多了,因此开始复习,本篇开始的是对于C语言的复习
思维导图
下面就依据下图,进行内容的整理
数组和指针
这个模块算是C语言中比较大的一个模块了,具体概念就不多说了,直接用例题
数组
#include <stdio.h>int main()
{//一维数组int a[] = { 1,2,3,4 };printf("%d\n", sizeof(a)); // 16printf("%d\n", sizeof(a + 0)); // 4/8printf("%d\n", sizeof(*a)); // 4printf("%d\n", sizeof(a + 1)); // 4/8printf("%d\n", sizeof(a[1])); // 4printf("%d\n", sizeof(&a)); // 4/8printf("%d\n", sizeof(*&a)); // 16printf("%d\n", sizeof(&a + 1));// 4/8printf("%d\n", sizeof(&a[0])); // 4/8printf("%d\n", sizeof(&a[0] + 1));// 4/8//字符数组char arr[] = { 'a','b','c','d','e','f', '\0'};printf("%d\n", sizeof(arr)); // 6printf("%d\n", sizeof(arr + 0)); // 4/8printf("%d\n", sizeof(*arr)); // 1printf("%d\n", sizeof(arr[1])); // 1printf("%d\n", sizeof(&arr)); // 4/8printf("%d\n", sizeof(&arr + 1)); // 4/8printf("%d\n", sizeof(&arr[0] + 1));// 4/8printf("%d\n", strlen(arr)); // 6printf("%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)); // 6printf("%d\n", strlen((const char*)(&arr + 1))); // 未定义printf("%d\n", strlen((const char*)(&arr[0] + 1))); // 5return 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 0100int* 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, 10int 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 5return 0;
}
#include <stdio.h>
int main()
{const char* a[] = { "work","at","alibaba" };const char** pa = a; // wpa++; // atprintf("%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");elseprintf("小端\n");return 0;
}int main()
{int a = 0x123456;char* pa = (char*)&a;if (*pa == 0x12)printf("大端\n");elseprintf("小端\n");return 0;
}