C语言 入坑总结

什么是内存泄漏?

内存泄漏是指计算机程序在使用内存时,没有正确释放不再需要的内存,导致系统中的可用内存逐渐减少,最终可能导致程序性能下降或崩溃。

数组指针和指针数组区别

数组指针

  • 数组指针是指一个指针,它指向一个数组的首元素。
  • 它是一个单一的指针变量,用于存储数组的地址。
  • 数组指针的类型是指向数组的指针,它指定了数组元素的数据类型和维度。
  • 通过数组指针,可以遍历整个数组,访问数组中的元素。
int arr[5] = {1, 2, 3, 4, 5};
int (*ptr)[5]; // 声明一个指向包含5个int元素的数组的指针
ptr = &arr;    // 将ptr指向arr数组的首地址

指针数组

  • 指针数组是一个数组,它的元素都是指针。
  • 它包含多个指针变量,每个指针可以指向不同的数据或对象。
  • 指针数组的元素可以是不同类型的指针,指向不同类型的数据。
  • 常用于创建数组的数组,每个元素指向不同的字符串或对象。
int x = 1, y = 2, z = 3;
int *ptrArr[3]; // 声明一个包含3个int指针的数组
ptrArr[0] = &x; // 第一个元素指向x
ptrArr[1] = &y; // 第二个元素指向y
ptrArr[2] = &z; // 第三个元素指向z

小结:
数组指针是一个指针,它指向整个数组,用于处理多维数组或将数组传递给函数。
指针数组是一个数组,它的元素是指针,用于存储不同的指针对象,通常用于管理多个同类型的指针。

指针常量和常量指针区别

指针常量是指指针所指向的位置不能改变,即指针本身是一个常量,但是指针所指向的内容可以改变。

int * const p=&a;

常量指针具有只能够读取内存中数据,却不能够修改内存中数据的属性的指针,称为指向常量的指针,简称常量指针。

const int * p; int const * p;

#include<>和#include""区别

#include<>会先到系统指定目录寻找头文件
#include""先到项目所在目录寻找头文件,如果没有找到再到系统指定目录寻找头文件

#运算符和##作用

# 运算符用于将宏参数转换为字符串常量。它可以将宏参数的值包装在双引号中,将其转换为字符串。

#define STRINGIFY(x) #x
printf("%s\n", STRINGIFY(Hello)); // 输出 "Hello"

## 将连个符号连接成一个新的符号

#define CONCAT(x, y) x##y
int xy = CONCAT(10, 20); // 这将展开为 int xy = 1020;

什么是泛型指针

泛型指针是指void类型指针,它可以指向任何类型的数据。

0长度数组

0长度数组是一种特殊的数组类型,它的大小为0。0长度数组通常用于柔性数组(Flexible Array)的实现。这些数组在编译时不占用任何内存空间,但可以用于计算偏移量,以便在结构体中创建可变长度的数据结构。

struct MyStruct {
    int someData;
    double moreData;
    char flexArray[0]; // 0长度数组
};

typedef和#define区别

typedef 用于创建类型别名,而 #define 用于创建宏。
typedef 创建一个新的类型名称,提高代码的可读性。#define 只进行文本替换。
使用 typedef 时,编译器会进行类型检查,而宏则只进行简单的文本替换。
typedef 只能用于创建类型别名,而宏可以用于各种目的,包括创建常量、函数、表达式等。
typedef 只在编译器的作用域内有效,而宏在整个文件中有效。

typedef int MyInt;
MyInt x = 42; // 现在 MyInt 可以用来声明变量

#define MAX_VALUE 100
int num = MAX_VALUE; // 编译时会替换为 int num = 100;

const和#define区别

#define 是用于预处理器文本替换的,而 const 是用于声明常量变量的。
#define 不分配内存,而 const 分配内存以存储常量值。
#define 不进行类型检查,而 const 提供类型安全。
const 具有作用域,而宏在整个文件中有效。

#define MAX_VALUE 100
const int MAX_VALUE = 100;

什么是悬挂指针和野指针

悬挂指针是指已经指向了一个对象或变量,但该对象或变量已经被释放或销毁,使得指针指向的内存区域不再有效。

int* ptr = (int*)malloc(sizeof(int));
free(ptr); // 现在 ptr 是一个悬挂指针
*ptr = 42; // 访问悬挂指针的值,可能导致未定义的行为

野指针是指一个未被初始化的指针,它包含一个随机或无效的内存地址,没有明确指向任何有效的对象或变量。

int* ptr; // 未初始化的野指针
*ptr = 42; // 访问野指针的值,可能导致未定义的行为

浅拷贝和深拷贝

浅拷贝

  • 浅拷贝是一种简单的拷贝操作,它仅复制数据结构中的成员的值或引用。
  • 对于指向动态分配内存的指针成员,浅拷贝只会复制指针的值,而不会复制指针指向的实际数据。这意味着原始对象和副本将共享相同的动态分配内存块。
  • 当一个对象发生改变时,另一个对象也会受到影响,因为它们引用相同的数据。
struct MyStruct {
    int* data;
};

struct MyStruct obj1, obj2;
obj1.data = (int*)malloc(sizeof(int));
*obj1.data = 42;

// 浅拷贝
obj2 = obj1;
*obj2.data = 100; // 修改 obj2 也会影响 obj1

深拷贝
深拷贝是一种复制操作,它不仅复制数据结构的成员的值,还复制指针成员指向的数据。这意味着原始对象和副本拥有各自独立的内存副本。
对于包含动态分配内存的成员,深拷贝将为每个成员分配新的内存,并将数据复制到新内存中。
当一个对象发生改变时,另一个对象不会受到影响,因为它们引用不同的数据。

struct MyStruct {
    int* data;
};

struct MyStruct obj1, obj2;
obj1.data = (int*)malloc(sizeof(int));
*obj1.data = 42;

// 深拷贝
obj2.data = (int*)malloc(sizeof(int));
*obj2.data = *obj1.data; // 复制数据,而不是指针
*obj2.data = 100; // 修改 obj2 不会影响 obj1

联合体和共用体区别

  • 联合体允许不同的成员共享相同的内存位置,而共用体的成员不共享内存。
  • 联合体的大小等于最大成员的大小,而共用体的大小等于所有成员的大小之和。
  • 在联合体中,只能同时访问一个成员,而在共用体中,可以同时访问多个成员。
  • 在联合体中,只能同时访问一个成员,而在共用体中,可以同时访问多个成员。

extern关键字

注意函数内部定义变量int g_x, g_y; 和 extern g_x, g_y是完全不一样的,前面是定义局部变量,后面是已经定义好的外部全局变量,这里是声明外部全局变量,后面才可以使用该全局变量。

#include "stdio.h"
#include "stdlib.h"
#include "utils.h"
#include "gnu/libc-version.h"
 
int g_m, g_n; // 表示定义该文件全局变量
extern int g_a, g_b; // 表示外部已经定义好的全局变量,这里是该文件对其声明,不是定义。
 
void main_test(void)
{
    printf("TEST ENTRY\n");
    printf("GNU libc version: %s\n", gnu_get_libc_version());
 
    /* int g_x, g_y; 定义局部变量*/
    extern int g_x, g_y; // 声明外部全局变量,下面才可以使用
    printf("g_x=%d, g_y=%d\n", g_x, g_y);
}
 
// 定义全局变量g_x, g_y,但是上面main_test函数想使用全局变量必须使用extern进行声明才能引用
int g_x = 0;
int g_y = 1;
void test(void)
{
    printf("test, g_x=%d, g_y=%d\n", g_x, g_y);
}

热门相关:神秘总裁小小妻   楚氏赘婿   隐婚试爱:娇妻,好甜!   嫡嫁千金   精灵掌门人