1. 数组越界:
int arr[3] = {1, 2, 3};
arr[3] = 4;    // 数组越界

for (int i = 0; i <= 3; i++) {
    printf("%d\n", arr[i]);    // 数组越界
}

在上述代码中,由于数组下标从0开始,arr[3]越界访问了数组最后一个元素的位置,此时程序可能会崩溃或者产生其他不确定的结果;另外在循环中因为i<=3条件中使用了小于等于号,导致循环访问到了数组最后一个元素之后的位置,同样也引起了数组越界问题。

  1. 指针问题:
int *p;
int a = 10;
*p = a;    // 指针未初始化

int arr[3] = {1, 2, 3};
p = &arr[3];    // 指针越界

int *q = NULL;    // 空指针
*q = 10;

在上述代码中,第一个例子中,指针p未初始化就被用来存储变量a的地址,这将导致未知的结果;第二个例子中,指针p超出了数组索引的范围,指向了不存在的地址,同样会导致未知的结果;第三个例子中,指针q初始化为NULL,但是仍然被用来存储变量10,这是一个空指针引用问题,可能会导致程序崩溃。

  1. 内存泄漏:
void func () {
    int *p = (int *)malloc(sizeof(int));
    // do something
    return;    // 遗漏了释放内存
}

在上述代码中,函数func()中使用了动态内存分配函数malloc分配了一段内存,但却没有在函数结束后手动释放这段内存,造成内存泄漏。

  1. 循环陷阱:
int i = 0;
do {
    printf("%d\n", i);
} while (i++ < 5);    // 条件不当

int j = 0;
while (j >= 0) {    // 计数器错误
    j++;
}

在上述代码中,第一个例子中,循环条件中使用了后缀自增运算符,导致循环结束后,i的值又增加了1;第二个例子中,计数器j一直增加,但由于j是无符号整型变量,所以它永远不会小于0,会导致死循环。

  1. 悬垂指针:
int *p;
{
    int a = 10;
    p = &a;    // 悬垂指针
}
*p = 20;

在上述代码中,指针p被用来存储局部变量a的地址,但是当函数块结束后,变量a被销毁了,此时指针p被称为悬垂指针,它指向已经被释放的内存区域,在使用它的时候可能会引起不可预料的问题。