Declaring arrays and out of bounds

声明数组方式

格式化字符串(典型静态数组)

char command[(FILE_NAME_LEN+1)*4] = {0};

# 全部值会变为 ‘\0
复制代码
snprintf(command, FILE_NAME_LEN*4, "dot -T png %s -o %s", dotFileName, pngFileName);

# 格式化字符串复制
复制代码

如果超过最大长度,如FILE_NAME_LEN*4,函数为了避免缓冲区溢出问题,会自动截断字符串,保证安全。

# graph node name 赋值
void GraphAddNode(struct Graph *pGraph, long u, char *name) {
    if (IsLegalNodeNum(pGraph, u)) {        
        snprintf(pGraph->pNodes[u].name, MAX_ID_LEN, "%s", name);
    }
}
复制代码

动态内存分配

(如 malloc)malloc( xx, sizeof(xx) 1000) memset(0, sizeof(xxx)1000)

Type *xx = (Type *)malloc(sizeof(Type) * 1000);
memset(xx, 0, sizeof(Type) * 1000);
复制代码

动态分配内存,追加数组长度:realloc

realloc 函数通常返回与传递给它的指针相同的地址,但这并不是保证

如果原来的内存无法扩展(例如,如果没有足够的连续内存),realloc 会分配一块新的内存,并将原始数据复制到这块新内存中。在这种情况下,原来的数据也会被保留(只要 realloc 成功),并且可以通过新的指针访问。因此不需要考虑手动复制先前数据到新地址的过程

// 扩展数组
int *new_array = realloc(array, 10 * sizeof(int)); // 试图扩展到 10 个 int
if (new_array == NULL) {
    fprintf(stderr, "Memory reallocation failed\n");
    free(array); // 如果 realloc 失败,释放原始内存
    return 1;
}

array = new_array; // 更新指针

// 初始化新分配的元素 for (int i = 5; i < 10; i++) { array[i] = i + 1; // 赋值 6, 7, 8, 9, 10 }
复制代码

两种方式比较

静态数组

优点

简单性:定义和使用非常简单,直接分配内存并在上管理,易于理解。
语法清晰,直接通过数组名和索引访问元素。

自动内存管理:静态数组在栈上分配,自动管理内存的分配和释放。当超出作用域时,内存会自动释放,减少了内存泄漏的风险。

速度:在栈上分配内存通常比在堆上分配更快,因为栈的内存分配和释放是通过指针的移动实现的。

缺点

大小限制:数组的大小必须在编译时确定,不能动态调整。如果需要更大的数组,需要手动修改代码和重新编译。

栈内存有限,大型数组可能导致栈溢出(stack overflow)

灵活性差:对于需要根据运行时条件变化的数组,静态数组不够灵活。

动态内存分配(如 malloc)

优点

灵活性:可以在运行时决定数组的大小,适合处理不确定大小的数据。可以根据需求动态扩展数组大小。

内存管理:在堆上分配的内存可以容纳比栈更多的数据,适合大数据量的存储

共享数据:可以通过指针共享动态分配的内存,允许多个函数访问同一内存块

缺点:

复杂性:动态内存管理需要显式调用 malloc、free 等函数,增加了编程复杂性
需要手动管理内存,容易引入内存泄漏(未释放的内存)或悬空指针(指向已释放内存的指针)

性能开销:动态分配内存的性能通常低于栈上的分配,因为堆内存管理涉及更多的操作和可能的碎片化。每次调用 malloc 都有可能影响性能,特别是在频繁分配和释放内存时

错误处理:

需要检查 malloc 返回的指针是否为 NULL,以处理内存分配失败的情况。

关于越界操作:越界赋值和访问

不推荐越界操作,因为不论是哪种分配数组方式,越界赋值或访问都会有失败的问题。

比如这里对a[6] 更多的值进行赋值

越界确实会赋值成功几个,但是那个11就失败了。赋值失败

直接访问、打印这个值也是失败的。

数组默认值

就当默认全是 ‘\0’就好理解了,{‘0’}和{0}不一样

默认全是’\0’
char command[(FILE_NAME_LEN + 1) 4] = {0};
char
nodeNames[NUM_OF_NODES] = {NULL};

只有第一个元素是’0’
char *nodeNames[NUM_OF_NODES] = {‘0’};

char array[10] = {‘a’, ‘b’, ‘c’}; // 只有前3个元素被赋值,其他元素被初始化为 ‘\0’

数组指针及访问方式

没有负数下标的访问方式,不如py

arr[-1] in C

访问地址

&arr[0]

Arr+I (i可以是0 如arr+i是第i个元素的地址)

访问值

*(arr+i)

Arr[i]

字符指针(字符串首地址(即字符串本身))

字符串本身隐式的写了一个char[],然后char*的首地址可以直接以数组形式访问。
数字没法办到这一点。

#include <stdio.h>
int main()
{
    char *a = "hello";
    printf("%c\n", a[2]);
    int b[] = {1, 2, 3, 4, 5};
    printf("%p\n", b);
    return 0;
}
复制代码

更详细一些,双引号包裹的字符串(本质是字符数组),返回的就是首地址(既char*)

char *argv[] = { "a", "b", "c", NULL }; // 这里是双引号包裹的,是字符串不是数组
复制代码
char *cptr ="CSE@UNSW";
复制代码
  • argv[0] 是指向字符串 “a” 的指针(即 char*),
  • argv[1] 是指向字符串 “b” 的指针,
  • argv[2] 是指向字符串 “c” 的指针。

如多名字数组赋值

“0”是给 char的正确方式,‘0’ 给char 直接报错

// char *nodeNames[NUM_OF_NODES] = {"0", "1", "2", "3", "4", "5", "6", "7"};
char *nodeNames[NUM_OF_NODES] = {'0', '1', '2', '3', '4', '5', '6', '7'};
复制代码

结构体的成员变量是一个指针,但是可以直接malloc多个当作数组使用

typedef long AdjMatrixElementTy;

struct Graph{
AdjMatrixElementTy *pAdjMatrix;
}

pGraph->pAdjMatrix = (AdjMatrixElementTy *) malloc(sizeof(AdjMatrixElementTy) * n * n);
memset(pGraph->pAdjMatrix, 0, sizeof(AdjMatrixElementTy) * n * n);
复制代码

Welcome to point out the mistakes and faults!

Gitalking ...