C 语言数组

数组基础

在 C 语言中定义数组时,可以选择性地为数组中的某些元素指定初始值。例如,如果只对数组的第二个元素赋值为 1,而忽略了其他元素的初始化,那么在全局或静态作用域中,未被显式初始化的元素将自动被设置为 0。这种特性确保了数组在使用前具有确定的初始状态,避免了未定义行为。

#define ARRAY_SIZE 5

int array[ARRAY_SIZE] = {[2]=1}; // 除去第二个元素值为 1 外,其余元素均为 0

int array[ARRAY_SIZE] = {[2]=1, 2, 3};  // 若是这样,则是从数组的第二个元素开始赋值,0 0 1 2 3

在 C 语言编程中,局部数组和全局数组的默认初始化行为是不同的。全局数组,由于其生命周期贯穿整个程序,存储在静态存储区,如果未显式初始化,则其元素会被自动初始化为 0。这确保了全局数组在使用前具有一个已知的初始状态。

相对地,局部数组的元素在没有明确初始化的情况下,其值是未定义的。局部数组存储在堆栈上,编译器不会为它们提供自动初始化,因此在函数内部定义的数组在使用前需要显式初始化,以避免不确定的行为。

数组的边界

在 C 语言中,访问数组元素可以通过数组名后跟索引的方式实现。例如,array [5] 访问的是数组 array 从首地址开始的第六个元素,这等同于 array 地址加上 5 个该数组元素类型的字节大小。同样地,array [0] 访问的是数组的第一个元素,即数组的首地址,也就是 array 本身。

需要注意的是,不同的编译器可能对数组长度的处理存在差异。在某些情况下,如果数组长度是一个变量,MSVC(Microsoft Visual C)编译器可能无法通过编译,因为它可能期望数组的大小在编译时是已知的。而 MinGW(Minimalist GNU for Windows)编译器可能允许这种编译,因为它遵循了 GNU GCC 的某些行为,允许在运行时确定数组的大小。

#include <stdio.h>

#define ARRAY_SIZE 5

int main() {

  int value = 5;
  int array_size[value];

  for (int i = 0; i < value; ++i) {
    array_size[i] = i;
    printf("%d ", array_size[i]);    // 0 1 2 3 4
  }

  return 0;
}

MSVC 编译器遵循 C90 标准,该标准并不支持变长数组(VLA,Variable-Length Array),这是由于 C90 对数组长度要求在编译时必须是常量表达式。而 C99 标准放宽了这一限制,允许数组的长度在运行时确定,从而支持变长数组。GCC 编译器作为遵循 C99 及之后标准的编译器,对变长数组进行了特别处理,因此能够支持这种特性。

因此,如果代码中使用了变长数组,可能会在 MSVC 编译器中遇到编译错误,因为它默认遵循 C90 标准。而 GCC 编译器则由于其对 C99 及更新标准的兼容,能够正确处理变长数组的定义和使用。

在 C 语言的某些版本和编译器中,使用 const 关键字声明的常量可能会遇到兼容性问题。例如,MSVC(Microsoft Visual C)编译器在处理某些 const 变量时可能会报错,这通常与其对 C 标准的支持程度有关。相比之下,GCC(GNU Compiler Collection)编译器对 const 常量的支持较为全面,能够顺利编译包含 const 声明的代码。

当涉及到 C++ 时,情况有所不同。C++ 语言规范对 const 常量提供了完整的支持,将其视为不可修改的值。在 C++ 中,const 修饰的变量在编译时和运行时都受到保护,确保其值不会被改变,这与 C++ 对数据安全性和稳定性的重视相一致。

const int Ksize = 5;
int array_const[Ksize]; // msvc报错: 应输入常量表达式; mingw: 成功运行

字符串

英文字符串数组

在 C 语言中,字符数组用于存储字符串,必须在数组的末尾包含空字符 ‘\0’ 作为字符串结束的标识。如果仅将字符数组的大小设置为待存储字符的数量,就会忽略这个重要的空字符,从而导致在使用 printf 函数打印字符串时出现潜在的数组越界问题。

例如,要存储字符串 “hello world”,需要的字符数组大小应该是 13,而不是 11。这是因为除了字符串中的 12 个字符外,还需要一个额外的位置来存储空字符 ‘\0’。正确的声明方式如下:char string [13] = "hello world";

这样声明后,使用 printf ("\n% s", string) 打印字符串时,将正确地在字符串的末尾遇到空字符并停止打印,避免了数组越界。

#include <stdio.h>

void TestString() {
  char string[11] = {"hello world"};

  for (int i = 0; i < 11; ++i) {
    printf("%c", string[i]);    // hello world
  }

  // 由于字符数组设置为 11,不是 12,所以输出结果为:hello world 烫烫烫烫烫烫烫烫?
  printf("\n%s", string);

  // char string[12] = {"hello world"};  // 此处设置为 12
  // printf("\n%s", string);             // 输出结果正常,为:hello world   
}

中文字符串数组

// 中文转换为当前文件的编码,然后以那个编码的形式存入到字符数组中
char zh[] = "你好,中国";    

使用 MSVC 在调试过程中可以查看字符数组:

在 GBK 编码系统中,“你好,中国” 这个中文短语被转换成对应的 GBK 编码序列,例如 “\xc4\xe3\xbc\xd3\xc8\xd5″。这表明在 C 语言中,存储中文字符串的字符数组实际上是在存储 GBK 编码的字节序列。由于 GBK 编码的每个中文字符占用 2 个字节,因此存储这个短语的数组大小需要是 11 个字节,这包括了 7 个中文字符和 2 个字节的标点,以及额外的 1 个字节用于存储字符串结束的空字符 ‘\0’。

宽字符数组与常规字符数组在内部表示上存在显著差异,它们专门设计用来存储 Unicode 字符,每个元素通常对应一个 Unicode 码点的值。例如,如果宽字符数组 ws 的第一个元素 ws [0] 的值被赋为 20320,这实际上代表了 Unicode 编码中的汉字 “你”。这种表示方式允许宽字符数组准确地存储和处理国际化文本。

// wchar_t 是无符号短整型
wchar_t ws[] = L"你好,中国";    // 存储的是 Unicode 的码点

同样需要注意的是,尽管宽字符数组存储的是 Unicode 值,它们仍然遵循 C 语言字符串的惯例,以空字符 ‘\0’ 结尾,这确保了字符串的结束可以被正确识别。因此,如果数组用于存储包含 5 个汉字的字符串,加上结尾的 ‘\0’,数组将总共包含 6 个宽字符元素。

函数的数组类型参数

当在 C 语言中将数组用于函数参数时,应传递数组的首地址以及元素的数量。这是因为数组名在传递过程中会自动转换为指向其第一个元素的指针。因此,为了在函数内部正确地访问数组的每个元素,需要额外提供数组的长度信息。

#include <stdio.h>

int SumArray(int array[], int n) {
  int sum = 0;
  for (int i = 0; i < n; ++i) {
    sum += array[i];
  }
  return sum;
}

int main() {

  int arrays[] = {1, 2, 3, 4, 5};
  int array_size[] = {1, 2, 3, 4, 5, 6, 7};

  int a = SumArray(arrays, 5);    // 15
  int b = SumArray(array_size, 7);  // 28

  printf("%d\n", a);
  printf("%d", b);

  return 0;
}

二维数组

二维数组的初始化

在 C 语言中初始化二维数组时,如果只指定了数组中某些特定位置的初始值,那么剩余未指定的位置将默认初始化为 0。这与一维数组的初始化行为保持一致,确保了数组的每个元素都有一个确定的初始状态。

int vehicle[3][2] = {1, 2, 4, 5, [1][1] = -1    // 二维数组结果为 1 2 4 -1 0 0
};

二维数组的传参

在 C 语言中,当需要将二维数组传递给函数时,由于 C 语言在函数参数中只识别一维数组的概念,因此必须对二维数组的传递方式进行特殊处理。具体来说,可以在函数参数中指定二维数组的第二维大小,例如使用 int array [rows][cols] 的形式,其中 rows 是数组的行数,而 cols 是列数,两者都需要在函数调用时明确。

然而,如果在函数原型中使用变量来指定数组的大小,如 int array[][cols] ,其中 cols 是函数的参数,这在某些编译器中可能会导致编译错误。MSVC(Microsoft Visual C)编译器可能不支持这种语法,因为它要求数组的大小在编译时是已知的常量表达式。而 MinGW(Minimalist GNU for Windows)编译器则可能允许这种语法,因为它遵循了 GNU GCC 的某些行为。

// 参数 row, col 的作用域是函数原型作用域
void SumIntArray(int rows, int cols, int array[][cols], int result[rows]) {
  for (int i = 0; i < rows; ++i) {
    for (int j = 0; j < cols; ++j) {
      result[i] += array[i][j];
    }
  }
}
// MSVC 编译器无法运行,报错: error C2057: 应输入常量表达式
//                         error C2466: 不能分配常量大小为 0 的数组
//                         error C2087: “array”: 缺少下标

数组作为函数返回值

在 C 语言编程中,数组本身不能作为函数的返回类型,因为数组在返回时会被退化为指向其首元素的指针,从而导致数据丢失。相反,我们通常通过将数组的指针或引用作为函数参数来实现数组数据的传递和修改。

这意味着,当我们希望在函数中处理数组并改变其内容时,我们会将数组的指针传递给函数。函数可以修改指针指向的数组元素,因为指针指向的是原始数组的内存地址。例如,一个函数可能会接收一个整型数组的指针,并在函数内部对数组中的元素进行变换或排序。

#include <stdio.h>

void SumIntArray(int rows, int cols, int array[][cols], int result[rows]) {
  for (int i = 0; i < rows; ++i) {
    for (int j = 0; j < cols; ++j) {
      result[i] += array[i][j];
    }
  }
}

int main() {

  int vehicle[3][2] = {1, 2, 4, 5, [1][1] = -1    // 二维数组结果为 1 2 4 -1 0 0
  };

  int result[3] = {0};    // 注意一定要清 0,否则计算求和要出大问题,因为初值不确定

  SumIntArray(3, 2, vehicle, result);

  for (int i = 0; i < 3; ++i) {
    printf("%d ", result[i]);    // 3 3 0
  }

  return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇