• C语言:复习


    最近知识学的差不多了,因此开始复习,本篇开始的是对于C语言的复习

    思维导图

    下面就依据下图,进行内容的整理

    在这里插入图片描述

    在这里插入图片描述

    数组和指针

    这个模块算是C语言中比较大的一个模块了,具体概念就不多说了,直接用例题

    数组

    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    指针

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    接下来分析 main() 函数中的 printf 表达式

    1. printf("%p\n", p + 0x1);
      
      • 1

      在这里,p 是一个指向 struct Test 的指针,对其进行加 0x1 操作意味着指针向前偏移 0x1struct Test 类型的大小,即偏移 20 字节。因此,p + 0x1 的值为 0x100014%p 格式说明符用于打印指针的值,所以这行代码输出 0x100014

    2. printf("%p\n", (unsigned long)p + 0x1);
      
      • 1

      首先,将指针 p 强制转换为 unsigned long 类型。虽然 p 的原始类型是一个指针,但转换为 unsigned long 后,它被视为一个数值。然后对这个数值加上 0x1,即 0x100000 + 0x1 = 0x100001。接着,将结果作为指针传递给 printf 函数,使用 %p 格式说明符打印。理论上,这行代码的行为是未定义的,因为 0x100001 不是一个有效的指针值。实际运行时可能会导致程序崩溃或其他不可预期的结果。

    3. printf("%p\n", (unsigned int*)p + 0x1);
      
      • 1

      这里,将指针 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    #include 
    
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    #include 
    int main()
    {
    	const char* a[] = { "work","at","alibaba" };
    	const char** pa = a; // w
    	pa++; // at
    	printf("%s\n", *pa);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    库函数的模拟实现

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    判断大小端

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    哪吒X选车指南:推荐哪吒X 500lite 版
    紧跟热点:教你如何快速掌握ChatGPT
    前端代码上线前验证whistle
    Python之读写文件
    图解:Go Mutext
    2.15 OrCAD中怎么创建带图片的Title Block?【OrCAD原理图封装库50问解析】
    Python 中的万能之王 Lambda 函数
    SpringBoot Web开发----请求参数处理
    初识容器Docker
    python教你两行代码添加水印,超级简单~
  • 原文地址:https://blog.csdn.net/qq_73899585/article/details/137949326